summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AconfigFlags.bp309
-rw-r--r--Android.bp3
-rw-r--r--LSE_APP_COMPAT_OWNERS6
-rw-r--r--Ravenwood.bp93
-rw-r--r--WEAR_OWNERS1
-rw-r--r--apct-tests/perftests/autofill/Android.bp1
-rw-r--r--apct-tests/perftests/blobstore/Android.bp1
-rw-r--r--apct-tests/perftests/contentcapture/Android.bp1
-rw-r--r--apct-tests/perftests/core/Android.bp1
-rw-r--r--apct-tests/perftests/core/apps/overlay/Android.bp62
-rw-r--r--apct-tests/perftests/core/apps/reources_manager/Android.bp1
-rw-r--r--apct-tests/perftests/core/jni/Android.bp1
-rw-r--r--apct-tests/perftests/healthconnect/Android.bp1
-rw-r--r--apct-tests/perftests/inputmethod/Android.bp1
-rw-r--r--apct-tests/perftests/multiuser/Android.bp1
-rw-r--r--apct-tests/perftests/multiuser/apps/dummyapp/Android.bp1
-rw-r--r--apct-tests/perftests/packagemanager/Android.bp1
-rw-r--r--apct-tests/perftests/packagemanager/apps/query-all/Android.bp150
-rw-r--r--apct-tests/perftests/permission/Android.bp1
-rw-r--r--apct-tests/perftests/permission/apps/usepermissionapp/Android.bp1
-rw-r--r--apct-tests/perftests/rubidium/Android.bp1
-rw-r--r--apct-tests/perftests/settingsprovider/Android.bp1
-rw-r--r--apct-tests/perftests/surfaceflinger/Android.bp1
-rw-r--r--apct-tests/perftests/textclassifier/Android.bp1
-rw-r--r--apct-tests/perftests/utils/Android.bp1
-rw-r--r--apct-tests/perftests/windowmanager/Android.bp1
-rw-r--r--apex/jobscheduler/service/Android.bp1
-rw-r--r--apex/jobscheduler/service/aconfig/Android.bp12
-rw-r--r--apex/jobscheduler/service/aconfig/alarm.aconfig11
-rw-r--r--apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java5
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java78
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java8
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java4
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java238
-rw-r--r--api/Android.bp3
-rw-r--r--boot/hiddenapi/hiddenapi-unsupported.txt2
-rw-r--r--cmds/am/src/com/android/commands/am/Am.java2
-rw-r--r--cmds/am/src/com/android/commands/am/Instrument.java19
-rw-r--r--core/TEST_MAPPING12
-rw-r--r--core/api/current.txt185
-rw-r--r--core/api/lint-baseline.txt28
-rw-r--r--core/api/module-lib-current.txt2
-rw-r--r--core/api/module-lib-lint-baseline.txt4
-rw-r--r--core/api/system-current.txt357
-rw-r--r--core/api/system-lint-baseline.txt18
-rw-r--r--core/api/test-current.txt43
-rw-r--r--core/api/test-lint-baseline.txt12
-rw-r--r--core/java/Android.bp19
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java5
-rw-r--r--core/java/android/adaptiveauth/flags.aconfig7
-rw-r--r--core/java/android/animation/Animator.java15
-rw-r--r--core/java/android/app/Activity.java215
-rw-r--r--core/java/android/app/ActivityClient.java20
-rw-r--r--core/java/android/app/ActivityGroup.java4
-rw-r--r--core/java/android/app/ActivityManager.java28
-rw-r--r--core/java/android/app/ActivityManagerInternal.java2
-rw-r--r--core/java/android/app/ActivityOptions.java3
-rw-r--r--core/java/android/app/ActivityThread.java66
-rw-r--r--core/java/android/app/AppOpsManager.java128
-rw-r--r--core/java/android/app/ApplicationPackageManager.java14
-rw-r--r--core/java/android/app/ApplicationStartInfo.java42
-rw-r--r--core/java/android/app/BroadcastOptions.java78
-rw-r--r--core/java/android/app/ComponentCaller.java19
-rw-r--r--core/java/android/app/IActivityClientController.aidl2
-rw-r--r--core/java/android/app/IActivityManager.aidl9
-rw-r--r--core/java/android/app/INotificationManager.aidl1
-rw-r--r--core/java/android/app/Instrumentation.java79
-rw-r--r--core/java/android/app/Notification.java98
-rw-r--r--core/java/android/app/NotificationManager.java20
-rw-r--r--core/java/android/app/OWNERS3
-rw-r--r--core/java/android/app/PictureInPictureUiState.java34
-rw-r--r--core/java/android/app/ResourcesManager.java4
-rw-r--r--core/java/android/app/ResultInfo.java14
-rw-r--r--core/java/android/app/Service.java9
-rw-r--r--core/java/android/app/StatusBarManager.java41
-rw-r--r--core/java/android/app/UiModeManager.java6
-rw-r--r--core/java/android/app/activity_manager.aconfig7
-rw-r--r--core/java/android/app/admin/DeviceAdminInfo.java5
-rw-r--r--core/java/android/app/admin/DevicePolicyCache.java14
-rw-r--r--core/java/android/app/admin/DevicePolicyIdentifiers.java20
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java183
-rw-r--r--core/java/android/app/admin/DevicePolicyManagerInternal.java5
-rw-r--r--core/java/android/app/admin/IAuditLogEventsCallback.aidl24
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl12
-rw-r--r--core/java/android/app/admin/SecurityLog.aidl20
-rw-r--r--core/java/android/app/admin/SecurityLog.java5
-rw-r--r--core/java/android/app/admin/flags/flags.aconfig17
-rw-r--r--core/java/android/app/assist/AssistStructure.java29
-rw-r--r--core/java/android/app/ondeviceintelligence/Content.aidl22
-rw-r--r--core/java/android/app/ondeviceintelligence/Content.java90
-rw-r--r--core/java/android/app/ondeviceintelligence/DownloadCallback.java114
-rw-r--r--core/java/android/app/ondeviceintelligence/Feature.aidl (renamed from location/java/android/location/GeocoderParams.aidl)9
-rw-r--r--core/java/android/app/ondeviceintelligence/Feature.java279
-rw-r--r--core/java/android/app/ondeviceintelligence/FeatureDetails.aidl22
-rw-r--r--core/java/android/app/ondeviceintelligence/FeatureDetails.java176
-rw-r--r--core/java/android/app/ondeviceintelligence/FilePart.java137
-rw-r--r--core/java/android/app/ondeviceintelligence/IDownloadCallback.aidl32
-rw-r--r--core/java/android/app/ondeviceintelligence/IFeatureCallback.aidl14
-rw-r--r--core/java/android/app/ondeviceintelligence/IFeatureDetailsCallback.aidl14
-rw-r--r--core/java/android/app/ondeviceintelligence/IListFeaturesCallback.aidl15
-rw-r--r--core/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl70
-rw-r--r--core/java/android/app/ondeviceintelligence/IProcessingSignal.aidl14
-rw-r--r--core/java/android/app/ondeviceintelligence/IResponseCallback.aidl15
-rw-r--r--core/java/android/app/ondeviceintelligence/IStreamingResponseCallback.aidl18
-rw-r--r--core/java/android/app/ondeviceintelligence/ITokenCountCallback.aidl13
-rw-r--r--core/java/android/app/ondeviceintelligence/OWNERS6
-rw-r--r--core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java624
-rw-r--r--core/java/android/app/ondeviceintelligence/ProcessingSignal.java221
-rw-r--r--core/java/android/app/ondeviceintelligence/StreamingResponseReceiver.java43
-rw-r--r--core/java/android/app/ondeviceintelligence/flags/ondevice_intelligence.aconfig8
-rw-r--r--core/java/android/app/servertransaction/ClientTransaction.java14
-rw-r--r--core/java/android/app/servertransaction/ClientTransactionListenerController.java9
-rw-r--r--core/java/android/app/servertransaction/LaunchActivityItem.java29
-rw-r--r--core/java/android/app/servertransaction/TransactionExecutor.java3
-rw-r--r--core/java/android/app/ui_mode_manager.aconfig7
-rw-r--r--core/java/android/app/wearable/IWearableSensingManager.aidl7
-rw-r--r--core/java/android/app/wearable/WearableSensingManager.java92
-rw-r--r--core/java/android/companion/CompanionDeviceManager.java7
-rw-r--r--core/java/android/companion/virtual/flags/flags.aconfig15
-rw-r--r--core/java/android/content/ClipData.java74
-rw-r--r--core/java/android/content/Context.java24
-rw-r--r--core/java/android/content/Intent.java137
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java18
-rw-r--r--core/java/android/content/pm/ArchivedActivityInfo.java9
-rw-r--r--core/java/android/content/pm/CrossProfileApps.java42
-rw-r--r--core/java/android/content/pm/ILauncherApps.aidl2
-rw-r--r--core/java/android/content/pm/LauncherApps.java53
-rw-r--r--core/java/android/content/pm/LauncherUserInfo.java7
-rw-r--r--core/java/android/content/pm/PackageManager.java15
-rw-r--r--core/java/android/content/pm/ShortcutServiceInternal.java5
-rw-r--r--core/java/android/content/pm/SignedPackage.java14
-rw-r--r--core/java/android/content/pm/SignedPackageParcel.aidl2
-rw-r--r--core/java/android/content/pm/UserProperties.java2
-rw-r--r--core/java/android/content/pm/multiuser.aconfig37
-rw-r--r--core/java/android/content/res/AssetManager.java36
-rw-r--r--core/java/android/content/res/ResourcesImpl.java13
-rw-r--r--core/java/android/credentials/GetCredentialResponse.java3
-rw-r--r--core/java/android/credentials/flags.aconfig16
-rw-r--r--core/java/android/credentials/selection/Constants.java7
-rw-r--r--core/java/android/credentials/selection/IntentFactory.java31
-rw-r--r--core/java/android/database/CursorWindow.java2
-rw-r--r--core/java/android/hardware/CameraPrivacyAllowlistEntry.aidl24
-rw-r--r--core/java/android/hardware/ISensorPrivacyListener.aidl1
-rw-r--r--core/java/android/hardware/ISensorPrivacyManager.aidl19
-rw-r--r--core/java/android/hardware/OverlayProperties.java25
-rw-r--r--core/java/android/hardware/SensorPrivacyManager.java210
-rw-r--r--core/java/android/hardware/SerialManager.java3
-rw-r--r--core/java/android/hardware/SerialManagerInternal.java35
-rw-r--r--core/java/android/hardware/biometrics/BiometricFaceConstants.java20
-rw-r--r--core/java/android/hardware/biometrics/BiometricFingerprintConstants.java20
-rw-r--r--core/java/android/hardware/biometrics/BiometricManager.java7
-rw-r--r--core/java/android/hardware/biometrics/BiometricPrompt.java25
-rw-r--r--core/java/android/hardware/biometrics/PromptInfo.java15
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java30
-rw-r--r--core/java/android/hardware/camera2/CameraDevice.java2
-rw-r--r--core/java/android/hardware/camera2/CameraExtensionCharacteristics.java38
-rw-r--r--core/java/android/hardware/camera2/CameraExtensionSession.java31
-rw-r--r--core/java/android/hardware/camera2/CameraMetadata.java38
-rw-r--r--core/java/android/hardware/camera2/CaptureRequest.java141
-rw-r--r--core/java/android/hardware/camera2/CaptureResult.java209
-rw-r--r--core/java/android/hardware/camera2/ExtensionCaptureRequest.java227
-rw-r--r--core/java/android/hardware/camera2/ExtensionCaptureResult.java272
-rw-r--r--core/java/android/hardware/camera2/extension/CameraOutputSurface.java49
-rw-r--r--core/java/android/hardware/camera2/extension/ICaptureCallback.aidl2
-rw-r--r--core/java/android/hardware/camera2/extension/OutputSurface.aidl2
-rw-r--r--core/java/android/hardware/camera2/extension/SessionProcessor.java38
-rw-r--r--core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java18
-rw-r--r--core/java/android/hardware/camera2/impl/ExtensionKey.java36
-rw-r--r--core/java/android/hardware/camera2/params/ExtensionSessionConfiguration.java59
-rw-r--r--core/java/android/hardware/devicestate/DeviceState.java255
-rw-r--r--core/java/android/hardware/devicestate/DeviceStateManager.java111
-rw-r--r--core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java92
-rw-r--r--core/java/android/hardware/devicestate/feature/flags.aconfig9
-rw-r--r--core/java/android/hardware/display/BrightnessInfo.java5
-rw-r--r--core/java/android/hardware/display/DisplayManager.java3
-rw-r--r--core/java/android/hardware/face/FaceEnrollOptions.aidl (renamed from core/java/android/service/voice/HotwordTrainingData.aidl)6
-rw-r--r--core/java/android/hardware/face/FaceEnrollOptions.java259
-rw-r--r--core/java/android/hardware/face/FaceManager.java8
-rw-r--r--core/java/android/hardware/face/IFaceService.aidl3
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintEnrollOptions.aidl19
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintEnrollOptions.java260
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintManager.java5
-rw-r--r--core/java/android/hardware/fingerprint/IFingerprintService.aidl3
-rw-r--r--core/java/android/hardware/input/KeyboardLayoutPreviewDrawable.java80
-rw-r--r--core/java/android/hardware/input/PhysicalKeyLayout.java11
-rw-r--r--core/java/android/hardware/input/VirtualKeyboard.java13
-rw-r--r--core/java/android/net/flags.aconfig8
-rw-r--r--core/java/android/net/thread/flags.aconfig12
-rw-r--r--core/java/android/os/BugreportParams.java8
-rw-r--r--core/java/android/os/ExternalVibrationScale.aidl45
-rw-r--r--core/java/android/os/IExternalVibratorService.aidl26
-rw-r--r--core/java/android/os/MessageQueue.java2
-rw-r--r--core/java/android/os/OWNERS1
-rw-r--r--core/java/android/os/Parcel.java3
-rw-r--r--core/java/android/os/ParcelFileDescriptor.java3
-rw-r--r--core/java/android/os/PermissionEnforcer.java18
-rw-r--r--core/java/android/os/ServiceManager.java50
-rw-r--r--core/java/android/os/SystemProperties.java3
-rw-r--r--core/java/android/os/UserManager.java23
-rw-r--r--core/java/android/os/VibrationEffect.java19
-rw-r--r--core/java/android/os/WakeLockStats.java172
-rw-r--r--core/java/android/os/vibrator/PrebakedSegment.java8
-rw-r--r--core/java/android/os/vibrator/PrimitiveSegment.java20
-rw-r--r--core/java/android/os/vibrator/RampSegment.java15
-rw-r--r--core/java/android/os/vibrator/StepSegment.java21
-rw-r--r--core/java/android/os/vibrator/VibrationEffectSegment.java14
-rw-r--r--core/java/android/permission/PermissionGroupUsage.java34
-rw-r--r--core/java/android/permission/PermissionManager.java16
-rw-r--r--core/java/android/permission/PermissionUsageHelper.java63
-rw-r--r--core/java/android/permission/flags.aconfig38
-rw-r--r--core/java/android/print/pdf/TEST_MAPPING7
-rw-r--r--core/java/android/provider/Settings.java18
-rw-r--r--core/java/android/security/flags.aconfig3
-rw-r--r--core/java/android/service/chooser/ChooserResult.java2
-rw-r--r--core/java/android/service/chooser/CustomChoosers.java84
-rw-r--r--core/java/android/service/chooser/flags.aconfig7
-rw-r--r--core/java/android/service/notification/ZenModeConfig.java6
-rw-r--r--core/java/android/service/notification/flags.aconfig2
-rw-r--r--core/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl44
-rw-r--r--core/java/android/service/ondeviceintelligence/IOnDeviceTrustedInferenceService.aidl44
-rw-r--r--core/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl34
-rw-r--r--core/java/android/service/ondeviceintelligence/OWNERS1
-rw-r--r--core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java383
-rw-r--r--core/java/android/service/ondeviceintelligence/OnDeviceTrustedInferenceService.java410
-rw-r--r--core/java/android/service/voice/AlwaysOnHotwordDetector.java74
-rw-r--r--core/java/android/service/voice/HotwordDetectionService.java16
-rw-r--r--core/java/android/service/voice/HotwordTrainingAudio.java388
-rw-r--r--core/java/android/service/voice/HotwordTrainingData.java305
-rw-r--r--core/java/android/service/voice/SoftwareHotwordDetector.java7
-rw-r--r--core/java/android/service/voice/VisualQueryDetectedResult.java73
-rw-r--r--core/java/android/service/voice/VisualQueryDetector.java114
-rw-r--r--core/java/android/service/voice/VoiceInteractionManagerInternal.java42
-rw-r--r--core/java/android/service/voice/flags/flags.aconfig2
-rw-r--r--core/java/android/service/wallpaper/Android.bp1
-rw-r--r--core/java/android/service/wearable/IWearableSensingService.aidl6
-rw-r--r--core/java/android/service/wearable/WearableSensingService.java159
-rw-r--r--core/java/android/text/BoringLayout.java15
-rw-r--r--core/java/android/text/DynamicLayout.java40
-rw-r--r--core/java/android/text/Layout.java147
-rw-r--r--core/java/android/text/MeasuredParagraph.java7
-rw-r--r--core/java/android/text/StaticLayout.java34
-rw-r--r--core/java/android/tracing/flags.aconfig1
-rw-r--r--core/java/android/tracing/perfetto/DataSource.java5
-rw-r--r--core/java/android/tracing/perfetto/DataSourceInstance.java5
-rw-r--r--core/java/android/util/EventLog.java2
-rw-r--r--core/java/android/util/Log.java2
-rw-r--r--core/java/android/util/MemoryIntArray.java20
-rw-r--r--core/java/android/util/TimingsTraceLog.java1
-rw-r--r--core/java/android/view/AttachedSurfaceControl.java72
-rw-r--r--core/java/android/view/BatchedInputEventReceiver.java13
-rw-r--r--core/java/android/view/HandwritingInitiator.java171
-rw-r--r--core/java/android/view/ISensitiveContentProtectionManager.aidl35
-rw-r--r--core/java/android/view/IWindowManager.aidl10
-rw-r--r--core/java/android/view/IWindowSession.aidl5
-rw-r--r--core/java/android/view/InputEventReceiver.java17
-rw-r--r--core/java/android/view/KeyCharacterMap.java11
-rw-r--r--core/java/android/view/KeyboardShortcutGroup.java20
-rw-r--r--core/java/android/view/PointerIcon.java5
-rw-r--r--core/java/android/view/SurfaceControl.java46
-rw-r--r--core/java/android/view/SurfaceControlViewHost.java50
-rw-r--r--core/java/android/view/SurfaceView.java53
-rw-r--r--core/java/android/view/TextureView.java2
-rw-r--r--core/java/android/view/View.java207
-rw-r--r--core/java/android/view/ViewRootImpl.java245
-rw-r--r--core/java/android/view/ViewStructure.java10
-rw-r--r--core/java/android/view/Window.java36
-rw-r--r--core/java/android/view/WindowManager.java259
-rw-r--r--core/java/android/view/WindowManagerGlobal.java27
-rw-r--r--core/java/android/view/WindowManagerImpl.java42
-rw-r--r--core/java/android/view/WindowlessWindowManager.java15
-rw-r--r--core/java/android/view/accessibility/AccessibilityWindowInfo.java11
-rw-r--r--core/java/android/view/accessibility/flags/accessibility_flags.aconfig7
-rw-r--r--core/java/android/view/animation/AnimationUtils.java2
-rw-r--r--core/java/android/view/autofill/AutofillManager.java89
-rw-r--r--core/java/android/view/autofill/IAutoFillManagerClient.aidl4
-rw-r--r--core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java60
-rw-r--r--core/java/android/view/inputmethod/InputBinding.java2
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java239
-rw-r--r--core/java/android/view/inputmethod/flags.aconfig8
-rw-r--r--core/java/android/view/textclassifier/TextClassificationConstants.java11
-rw-r--r--core/java/android/widget/Editor.java18
-rw-r--r--core/java/android/widget/RemoteViews.java41
-rw-r--r--core/java/android/widget/TextView.java60
-rw-r--r--core/java/android/window/IGlobalDragListener.aidl (renamed from core/java/android/window/IUnhandledDragListener.aidl)11
-rw-r--r--core/java/android/window/InputTransferToken.java22
-rw-r--r--core/java/android/window/TrustedPresentationThresholds.java99
-rw-r--r--core/java/android/window/flags/OWNERS3
-rw-r--r--core/java/android/window/flags/lse_desktop_experience.aconfig9
-rw-r--r--core/java/android/window/flags/window_surfaces.aconfig15
-rw-r--r--core/java/android/window/flags/windowing_sdk.aconfig8
-rw-r--r--core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java3
-rw-r--r--core/java/com/android/internal/app/IAppOpsService.aidl1
-rw-r--r--core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl7
-rw-r--r--core/java/com/android/internal/app/IVoiceInteractionAccessibilitySettingsListener.aidl24
-rw-r--r--core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl18
-rw-r--r--core/java/com/android/internal/app/NfcResolverActivity.java11
-rw-r--r--core/java/com/android/internal/app/SetScreenLockDialogActivity.java160
-rw-r--r--core/java/com/android/internal/app/UnlaunchableAppActivity.java11
-rw-r--r--core/java/com/android/internal/colorextraction/OWNERS3
-rw-r--r--core/java/com/android/internal/content/ReferrerIntent.java14
-rw-r--r--core/java/com/android/internal/display/RefreshRateSettingsUtils.java29
-rw-r--r--core/java/com/android/internal/inputmethod/IBooleanListener.aidl25
-rw-r--r--core/java/com/android/internal/inputmethod/IInputMethodClient.aidl1
-rw-r--r--core/java/com/android/internal/inputmethod/InputBindResult.java1
-rw-r--r--core/java/com/android/internal/jank/Cuj.java6
-rw-r--r--core/java/com/android/internal/os/BatteryStatsHistory.java515
-rw-r--r--core/java/com/android/internal/os/BatteryStatsHistoryIterator.java6
-rw-r--r--core/java/com/android/internal/os/LongArrayMultiStateCounter.java4
-rw-r--r--core/java/com/android/internal/os/LongMultiStateCounter.java2
-rw-r--r--core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java15
-rw-r--r--core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java5
-rw-r--r--core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java6
-rw-r--r--core/java/com/android/internal/policy/PhoneWindow.java7
-rw-r--r--core/java/com/android/internal/protolog/LegacyProtoLogImpl.java (renamed from core/java/com/android/internal/protolog/BaseProtoLogImpl.java)115
-rw-r--r--core/java/com/android/internal/protolog/LegacyProtoLogViewerConfigReader.java117
-rw-r--r--core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java559
-rw-r--r--core/java/com/android/internal/protolog/ProtoLogDataSource.java294
-rw-r--r--core/java/com/android/internal/protolog/ProtoLogImpl.java65
-rw-r--r--core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java156
-rw-r--r--core/java/com/android/internal/protolog/ViewerConfigInputStreamProvider.java26
-rw-r--r--core/java/com/android/internal/protolog/common/ILogger.java25
-rw-r--r--core/java/com/android/internal/protolog/common/IProtoLog.java55
-rw-r--r--core/java/com/android/internal/protolog/common/IProtoLogGroup.java1
-rw-r--r--core/java/com/android/internal/protolog/common/LogLevel.java26
-rw-r--r--core/java/com/android/internal/protolog/common/ProtoLog.java43
-rw-r--r--core/java/com/android/internal/protolog/common/ProtoLogToolInjected.java28
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBar.aidl6
-rw-r--r--core/java/com/android/internal/view/IInputMethodManager.aidl24
-rw-r--r--core/java/com/android/internal/widget/CallLayout.java47
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java19
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java12
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java10
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java2
-rw-r--r--core/java/com/android/server/pm/pkg/AndroidPackage.java7
-rw-r--r--core/jni/android_hardware_OverlayProperties.cpp26
-rw-r--r--core/jni/android_util_AssetManager.cpp17
-rw-r--r--core/jni/android_view_InputEventReceiver.cpp28
-rw-r--r--core/jni/android_view_InputEventSender.cpp2
-rw-r--r--core/jni/android_view_InputQueue.cpp2
-rw-r--r--core/jni/android_view_KeyCharacterMap.cpp8
-rw-r--r--core/jni/android_view_KeyEvent.cpp24
-rw-r--r--core/jni/android_view_KeyEvent.h9
-rw-r--r--core/jni/android_view_MotionEvent.cpp41
-rw-r--r--core/jni/android_view_MotionEvent.h11
-rw-r--r--core/jni/android_view_MotionPredictor.cpp3
-rw-r--r--core/jni/android_view_SurfaceControl.cpp11
-rw-r--r--core/proto/android/app/appstartinfo.proto1
-rw-r--r--core/proto/android/content/intent.proto1
-rw-r--r--core/proto/android/content/package_item_info.proto1
-rw-r--r--core/proto/android/hardware/sensorprivacy.proto5
-rw-r--r--core/proto/android/internal/protolog.proto5
-rw-r--r--core/res/AndroidManifest.xml90
-rw-r--r--core/res/res/values-af/strings.xml14
-rw-r--r--core/res/res/values-am/strings.xml14
-rw-r--r--core/res/res/values-ar/strings.xml14
-rw-r--r--core/res/res/values-as/strings.xml14
-rw-r--r--core/res/res/values-az/strings.xml14
-rw-r--r--core/res/res/values-b+sr+Latn/strings.xml14
-rw-r--r--core/res/res/values-be/strings.xml14
-rw-r--r--core/res/res/values-bg/strings.xml14
-rw-r--r--core/res/res/values-bn/strings.xml14
-rw-r--r--core/res/res/values-bs/strings.xml14
-rw-r--r--core/res/res/values-ca/strings.xml14
-rw-r--r--core/res/res/values-cs/strings.xml14
-rw-r--r--core/res/res/values-da/strings.xml14
-rw-r--r--core/res/res/values-de/strings.xml14
-rw-r--r--core/res/res/values-el/strings.xml16
-rw-r--r--core/res/res/values-en-rAU/strings.xml14
-rw-r--r--core/res/res/values-en-rCA/strings.xml14
-rw-r--r--core/res/res/values-en-rGB/strings.xml14
-rw-r--r--core/res/res/values-en-rIN/strings.xml14
-rw-r--r--core/res/res/values-en-rXC/strings.xml14
-rw-r--r--core/res/res/values-es-rUS/strings.xml14
-rw-r--r--core/res/res/values-es/strings.xml14
-rw-r--r--core/res/res/values-et/strings.xml14
-rw-r--r--core/res/res/values-eu/strings.xml14
-rw-r--r--core/res/res/values-fa/strings.xml14
-rw-r--r--core/res/res/values-fi/strings.xml14
-rw-r--r--core/res/res/values-fr-rCA/strings.xml14
-rw-r--r--core/res/res/values-fr/strings.xml22
-rw-r--r--core/res/res/values-gl/strings.xml16
-rw-r--r--core/res/res/values-gu/strings.xml14
-rw-r--r--core/res/res/values-hi/strings.xml16
-rw-r--r--core/res/res/values-hr/strings.xml14
-rw-r--r--core/res/res/values-hu/strings.xml14
-rw-r--r--core/res/res/values-hy/strings.xml18
-rw-r--r--core/res/res/values-in/strings.xml14
-rw-r--r--core/res/res/values-is/strings.xml14
-rw-r--r--core/res/res/values-it/strings.xml14
-rw-r--r--core/res/res/values-iw/strings.xml14
-rw-r--r--core/res/res/values-ja/strings.xml14
-rw-r--r--core/res/res/values-ka/strings.xml14
-rw-r--r--core/res/res/values-kk/strings.xml74
-rw-r--r--core/res/res/values-km/strings.xml14
-rw-r--r--core/res/res/values-kn/strings.xml14
-rw-r--r--core/res/res/values-ko/strings.xml14
-rw-r--r--core/res/res/values-ky/strings.xml14
-rw-r--r--core/res/res/values-lo/strings.xml14
-rw-r--r--core/res/res/values-lt/strings.xml14
-rw-r--r--core/res/res/values-lv/strings.xml22
-rw-r--r--core/res/res/values-mk/strings.xml14
-rw-r--r--core/res/res/values-ml/strings.xml14
-rw-r--r--core/res/res/values-mn/strings.xml14
-rw-r--r--core/res/res/values-mr/strings.xml42
-rw-r--r--core/res/res/values-ms/strings.xml14
-rw-r--r--core/res/res/values-my/strings.xml14
-rw-r--r--core/res/res/values-nb/strings.xml14
-rw-r--r--core/res/res/values-ne/strings.xml14
-rw-r--r--core/res/res/values-nl/strings.xml14
-rw-r--r--core/res/res/values-or/strings.xml14
-rw-r--r--core/res/res/values-pa/strings.xml14
-rw-r--r--core/res/res/values-pl/strings.xml14
-rw-r--r--core/res/res/values-pt-rBR/strings.xml14
-rw-r--r--core/res/res/values-pt-rPT/strings.xml14
-rw-r--r--core/res/res/values-pt/strings.xml14
-rw-r--r--core/res/res/values-ro/strings.xml16
-rw-r--r--core/res/res/values-ru/strings.xml14
-rw-r--r--core/res/res/values-si/strings.xml14
-rw-r--r--core/res/res/values-sk/strings.xml14
-rw-r--r--core/res/res/values-sl/strings.xml14
-rw-r--r--core/res/res/values-sq/strings.xml14
-rw-r--r--core/res/res/values-sr/strings.xml14
-rw-r--r--core/res/res/values-sv/strings.xml14
-rw-r--r--core/res/res/values-sw/strings.xml14
-rw-r--r--core/res/res/values-ta/strings.xml14
-rw-r--r--core/res/res/values-te/strings.xml14
-rw-r--r--core/res/res/values-th/strings.xml18
-rw-r--r--core/res/res/values-tl/strings.xml14
-rw-r--r--core/res/res/values-tr/strings.xml14
-rw-r--r--core/res/res/values-uk/strings.xml14
-rw-r--r--core/res/res/values-ur/strings.xml14
-rw-r--r--core/res/res/values-uz/strings.xml14
-rw-r--r--core/res/res/values-vi/strings.xml14
-rw-r--r--core/res/res/values-zh-rCN/strings.xml14
-rw-r--r--core/res/res/values-zh-rHK/strings.xml46
-rw-r--r--core/res/res/values-zh-rTW/strings.xml14
-rw-r--r--core/res/res/values-zu/strings.xml14
-rw-r--r--core/res/res/values/attrs.xml18
-rw-r--r--core/res/res/values/attrs_manifest.xml9
-rw-r--r--core/res/res/values/config.xml7
-rw-r--r--core/res/res/values/config_battery_saver.xml3
-rw-r--r--core/res/res/values/config_display.xml33
-rw-r--r--core/res/res/values/dimens_material.xml7
-rw-r--r--core/res/res/values/public-staging.xml18
-rw-r--r--core/res/res/values/strings.xml7
-rw-r--r--core/res/res/values/symbols.xml19
-rw-r--r--core/tests/BroadcastRadioTests/Android.bp1
-rw-r--r--core/tests/BroadcastRadioTests/TEST_MAPPING2
-rw-r--r--core/tests/coretests/Android.bp1
-rw-r--r--core/tests/coretests/res/values/styles.xml6
-rw-r--r--core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java107
-rw-r--r--core/tests/coretests/src/android/app/activity/ActivityThreadTest.java61
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java17
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java41
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java3
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/TestUtils.java12
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java137
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java26
-rw-r--r--core/tests/coretests/src/android/content/ContextTest.java38
-rw-r--r--core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java45
-rw-r--r--core/tests/coretests/src/android/hardware/face/FaceManagerTest.java6
-rw-r--r--core/tests/coretests/src/android/hardware/fingerprint/FingerprintManagerTest.java6
-rw-r--r--core/tests/coretests/src/android/os/RemoteCallbackListTest.java18
-rw-r--r--core/tests/coretests/src/android/os/WakeLockStatsTest.java145
-rw-r--r--core/tests/coretests/src/android/text/LayoutTest.java275
-rw-r--r--core/tests/coretests/src/android/view/ViewRootImplTest.java136
-rw-r--r--core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java23
-rw-r--r--core/tests/coretests/src/android/widget/RemoteViewsTest.java22
-rw-r--r--core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java60
-rw-r--r--core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateTest.java26
-rw-r--r--core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java3
-rw-r--r--core/tests/utiltests/src/android/util/MemoryIntArrayTest.java2
-rw-r--r--core/tests/utiltests/src/android/util/RemoteMemoryIntArrayService.java6
-rw-r--r--core/tests/vibrator/Android.bp1
-rw-r--r--core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java7
-rw-r--r--core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java21
-rw-r--r--core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java31
-rw-r--r--core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java31
-rw-r--r--data/etc/Android.bp5
-rw-r--r--data/etc/com.android.intentresolver.xml2
-rw-r--r--data/etc/core.protolog.pbbin0 -> 54073 bytes
-rw-r--r--data/etc/services.core.protolog.json18
-rw-r--r--graphics/java/Android.bp1
-rw-r--r--graphics/java/android/graphics/Canvas.java40
-rw-r--r--graphics/java/android/graphics/pdf/TEST_MAPPING7
-rw-r--r--graphics/proto/Android.bp1
-rw-r--r--keystore/java/android/security/keystore/KeyGenParameterSpec.java6
-rw-r--r--keystore/java/android/security/keystore/KeyProtection.java6
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java2
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java2
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java17
-rw-r--r--libs/WindowManager/Shell/Android.bp35
-rw-r--r--libs/WindowManager/Shell/aconfig/OWNERS3
-rw-r--r--libs/WindowManager/Shell/aconfig/multitasking.aconfig7
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt20
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt215
-rw-r--r--libs/WindowManager/Shell/res/color/desktop_mode_caption_button_color_selector_dark.xml21
-rw-r--r--libs/WindowManager/Shell/res/color/desktop_mode_caption_button_color_selector_light.xml21
-rw-r--r--libs/WindowManager/Shell/res/drawable/circular_progress.xml33
-rw-r--r--libs/WindowManager/Shell/res/drawable/rounded_button.xml19
-rw-r--r--libs/WindowManager/Shell/res/layout/bubble_bar_expanded_view.xml5
-rw-r--r--libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml16
-rw-r--r--libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml1
-rw-r--r--libs/WindowManager/Shell/res/layout/maximize_menu_button.xml36
-rw-r--r--libs/WindowManager/Shell/res/values-af/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-am/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-ar/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-as/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-az/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-be/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-bg/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-bn/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-bs/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-ca/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-cs/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-da/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-de/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-el/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-en-rAU/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-en-rCA/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-en-rGB/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-en-rIN/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-en-rXC/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-es-rUS/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-es/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-et/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-eu/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-fa/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-fi/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-fr-rCA/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-fr/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-gl/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-gu/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-hi/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-hr/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-hu/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-hy/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-in/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-is/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-it/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-iw/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-ja/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-ka/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-kk/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-km/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-kn/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-ko/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-ky/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-lo/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-lt/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-lv/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-mk/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-ml/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-mn/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-mr/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-ms/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-my/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-nb/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-ne/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-nl/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-or/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-pa/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-pl/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rBR/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rPT/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-pt/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-ro/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-ru/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-si/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-sk/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-sl/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-sq/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-sr/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-sv/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-sw/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-ta/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-te/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-th/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-tl/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-tr/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-uk/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-ur/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-uz/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-vi/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rCN/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rHK/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rTW/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-zu/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values/colors.xml5
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java46
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java37
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/OWNERS4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java27
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java114
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt124
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPip.aidl (renamed from libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl)4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPipAnimationListener.aidl (renamed from libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPipAnimationListener.aidl)2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipPerfHintController.java166
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java28
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java28
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java118
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt152
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java86
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/GlobalDragListener.kt (renamed from libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/UnhandledDragController.kt)49
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java31
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java25
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java28
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchGesture.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java37
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java141
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java25
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java120
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java28
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java169
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/util/KtProtoLog.kt14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java81
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java36
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt106
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt36
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java21
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt28
-rw-r--r--libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipWithSourceRectHintTest.kt86
-rw-r--r--libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt1
-rw-r--r--libs/WindowManager/Shell/tests/unittest/AndroidManifest.xml1
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiInstanceHelperTest.kt171
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitScreenUtilsTests.kt69
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java41
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt139
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt50
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/GlobalDragListenerTest.kt (renamed from libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/UnhandledDragControllerTest.kt)58
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java12
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java12
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java32
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java7
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java156
-rw-r--r--libs/androidfw/AssetManager2.cpp20
-rw-r--r--libs/androidfw/include/androidfw/AssetManager2.h5
-rw-r--r--libs/hostgraphics/OWNERS4
-rw-r--r--libs/hwui/Android.bp41
-rw-r--r--libs/hwui/CanvasTransform.cpp7
-rw-r--r--libs/hwui/DamageAccumulator.cpp2
-rw-r--r--libs/hwui/HardwareBitmapUploader.cpp15
-rw-r--r--libs/hwui/ProfileData.cpp2
-rw-r--r--libs/hwui/SkiaCanvas.cpp11
-rw-r--r--libs/hwui/SkiaCanvas.h1
-rw-r--r--libs/hwui/hwui/Bitmap.cpp6
-rw-r--r--libs/hwui/hwui/Canvas.h1
-rw-r--r--libs/hwui/hwui/DrawTextFunctor.h1
-rw-r--r--libs/hwui/jni/android_graphics_Canvas.cpp23
-rw-r--r--libs/hwui/platform/android/thread/ThreadBase.h (renamed from libs/hwui/thread/ThreadBase.h)6
-rw-r--r--libs/hwui/platform/host/ProfileDataContainer.cpp40
-rw-r--r--libs/hwui/platform/host/Readback.cpp50
-rw-r--r--libs/hwui/platform/host/WebViewFunctorManager.cpp75
-rw-r--r--libs/hwui/platform/host/renderthread/CacheManager.cpp64
-rw-r--r--libs/hwui/platform/host/renderthread/RenderThread.cpp141
-rw-r--r--libs/hwui/platform/host/thread/ThreadBase.h76
-rw-r--r--libs/hwui/private/hwui/WebViewFunctor.h8
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp1
-rw-r--r--libs/hwui/renderthread/RenderThread.cpp3
-rw-r--r--libs/hwui/renderthread/RenderThread.h3
-rw-r--r--location/api/current.txt12
-rw-r--r--location/api/system-current.txt49
-rw-r--r--location/java/android/location/Geocoder.java231
-rw-r--r--location/java/android/location/IGeocodeProvider.aidl33
-rw-r--r--location/java/android/location/ILocationManager.aidl15
-rw-r--r--location/java/android/location/flags/location.aconfig7
-rw-r--r--location/java/android/location/provider/ForwardGeocodeRequest.aidl19
-rw-r--r--location/java/android/location/provider/ForwardGeocodeRequest.java286
-rw-r--r--location/java/android/location/provider/GeocodeProviderBase.java174
-rw-r--r--location/java/android/location/provider/IGeocodeCallback.aidl (renamed from location/java/android/location/IGeocodeListener.aidl)13
-rw-r--r--location/java/android/location/provider/IGeocodeProvider.aidl31
-rw-r--r--location/java/android/location/provider/ReverseGeocodeRequest.aidl (renamed from core/java/android/service/voice/HotwordTrainingAudio.aidl)7
-rw-r--r--location/java/android/location/provider/ReverseGeocodeRequest.java230
-rw-r--r--location/lib/Android.bp1
-rw-r--r--location/lib/README.txt40
-rw-r--r--location/lib/api/system-current.txt24
-rw-r--r--location/lib/java/android/location/GeocoderParams.java (renamed from core/java/android/location/GeocoderParams.java)62
-rw-r--r--location/lib/java/com/android/location/provider/GeocodeProvider.java104
-rw-r--r--media/java/android/media/FadeManagerConfiguration.java30
-rw-r--r--media/java/android/media/MediaCodec.java189
-rw-r--r--media/java/android/media/MediaCodecInfo.java68
-rw-r--r--media/java/android/media/MediaFormat.java54
-rw-r--r--media/java/android/media/MediaRouter2.java27
-rw-r--r--media/java/android/media/audiofx/Visualizer.java13
-rw-r--r--media/java/android/media/browse/MediaBrowser.java34
-rw-r--r--media/java/android/media/flags/media_better_together.aconfig7
-rw-r--r--media/java/android/media/flags/projection.aconfig2
-rw-r--r--media/java/android/media/metrics/EditingEndedEvent.java9
-rw-r--r--media/java/android/media/metrics/MediaItemInfo.java14
-rw-r--r--media/java/android/media/midi/package.html2
-rw-r--r--media/java/android/media/tv/SignalingDataResponse.java4
-rwxr-xr-xmedia/java/android/media/tv/interactive/TvInteractiveAppService.java8
-rwxr-xr-xmedia/java/android/media/tv/interactive/TvInteractiveAppView.java3
-rw-r--r--media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl7
-rw-r--r--media/java/android/service/media/MediaBrowserService.java741
-rw-r--r--media/jni/android_media_MediaCodec.cpp211
-rw-r--r--media/jni/android_media_MediaCodec.h16
-rw-r--r--media/jni/audioeffect/Visualizer.cpp34
-rw-r--r--media/jni/audioeffect/Visualizer.h8
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java4
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/StaticMetadata.java4
-rw-r--r--media/tests/mediatestutils/Android.bp1
-rw-r--r--media/tests/mediatestutils/tests/Android.bp1
-rw-r--r--native/android/input.cpp12
-rw-r--r--native/android/libandroid.map.txt1
-rw-r--r--native/android/surface_control.cpp18
-rw-r--r--nfc-extras/Android.bp3
-rw-r--r--nfc-extras/tests/Android.bp1
-rw-r--r--nfc/Android.bp1
-rw-r--r--nfc/api/current.txt35
-rw-r--r--nfc/api/system-current.txt2
-rw-r--r--nfc/java/android/nfc/INfcCardEmulation.aidl4
-rw-r--r--nfc/java/android/nfc/NfcAdapter.java31
-rw-r--r--nfc/java/android/nfc/cardemulation/ApduServiceInfo.java90
-rw-r--r--nfc/java/android/nfc/cardemulation/CardEmulation.java55
-rw-r--r--nfc/java/android/nfc/cardemulation/HostApduService.java90
-rw-r--r--nfc/java/android/nfc/cardemulation/PollingFrame.java243
-rw-r--r--nfc/tests/Android.bp1
-rw-r--r--packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiver.java4
-rw-r--r--packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiverTest.java8
-rw-r--r--packages/CompanionDeviceManager/res/values-nb/strings.xml4
-rw-r--r--packages/CompanionDeviceManager/res/values/styles.xml4
-rw-r--r--packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java8
-rw-r--r--packages/CrashRecovery/services/java/com/android/server/RescueParty.java55
-rw-r--r--packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java300
-rw-r--r--packages/CrashRecovery/services/java/com/android/server/rollback/WatchdogRollbackLogger.java3
-rw-r--r--packages/CredentialManager/res/values-kk/strings.xml6
-rw-r--r--packages/CredentialManager/res/values/colors.xml3
-rw-r--r--packages/CredentialManager/res/values/dimens.xml2
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/client/CredentialManagerClient.kt22
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt77
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt6
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt2
-rw-r--r--packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt7
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt10
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt16
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt54
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt17
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/UserConfigRepo.kt44
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt130
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/common/ui/InlinePresentationFactory.kt84
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt142
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt6
-rw-r--r--packages/CredentialManager/wear/res/values/strings.xml13
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt3
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt1
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt71
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/FlowEngine.kt45
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt21
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/CredentialsScreenChip.kt74
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/SignInHeader.kt6
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt1
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFoldScreen.kt12
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt78
-rw-r--r--packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt77
-rw-r--r--packages/FusedLocation/AndroidManifest.xml13
-rw-r--r--packages/FusedLocation/src/com/android/location/gnss/GnssOverlayLocationProvider.java134
-rw-r--r--packages/FusedLocation/src/com/android/location/gnss/GnssOverlayLocationService.java52
-rw-r--r--packages/FusedLocation/test/src/com/android/location/gnss/tests/GnssOverlayLocationServiceTest.java202
-rw-r--r--packages/PackageInstaller/res/values-af/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-am/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-ar/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-as/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-az/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-b+sr+Latn/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-be/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-bg/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-bn/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-bs/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-ca/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-cs/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-da/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-de/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-el/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-en-rAU/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-en-rGB/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-en-rIN/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-es-rUS/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-es/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-et/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-eu/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-fa/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-fi/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-fr-rCA/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-fr/strings.xml5
-rw-r--r--packages/PackageInstaller/res/values-gl/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-gu/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-hi/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-hr/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-hu/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-hy/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-in/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-is/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-it/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-iw/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-ja/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-ka/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-kk/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-km/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-kn/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-ko/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-ky/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-lo/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-lt/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-lv/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-mk/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-ml/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-mn/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-mr/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-ms/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-my/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-nb/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-ne/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-night/themes.xml3
-rw-r--r--packages/PackageInstaller/res/values-nl/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-or/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-pa/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-pl/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-pt-rBR/strings.xml5
-rw-r--r--packages/PackageInstaller/res/values-pt-rPT/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-pt/strings.xml5
-rw-r--r--packages/PackageInstaller/res/values-ro/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-ru/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-si/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-sk/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-sl/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-sq/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-sr/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-sv/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-sw/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-ta/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-te/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-th/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-tl/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-tr/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-uk/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-ur/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-uz/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-vi/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-zh-rCN/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-zh-rHK/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-zh-rTW/strings.xml3
-rw-r--r--packages/PackageInstaller/res/values-zu/strings.xml3
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java5
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java14
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt6
-rw-r--r--packages/PrintRecommendationService/res/values/strings.xml1
-rw-r--r--packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java9
-rw-r--r--packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/google/CloudPrintPlugin.java166
-rw-r--r--packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java1
-rw-r--r--packages/SettingsLib/Spa/OWNERS2
-rw-r--r--packages/SettingsLib/Spa/build.gradle.kts2
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt8
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt6
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/EditorMainPageProvider.kt4
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsDropdownBoxPageProvider.kt (renamed from packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuBoxPageProvider.kt)55
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsDropdownCheckBoxProvider.kt134
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuCheckBoxProvider.kt75
-rw-r--r--packages/SettingsLib/Spa/gradle/libs.versions.toml2
-rw-r--r--packages/SettingsLib/Spa/spa/build.gradle.kts4
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt2
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt10
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/DropdownTextBox.kt (renamed from packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBox.kt)87
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsDropdownBox.kt69
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsDropdownCheckBox.kt145
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt164
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextField.kt8
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ListPreference.kt12
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt5
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCardTest.kt17
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsDropdownBoxTest.kt109
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsDropdownCheckBoxTest.kt145
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBoxTest.kt95
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBoxTest.kt115
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ListPreferenceTest.kt20
-rw-r--r--packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/SemanticsMatcher.kt26
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt3
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt1
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt4
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceTest.kt25
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItemTest.kt24
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/RestrictedTestUtils.kt13
-rw-r--r--packages/SettingsLib/aconfig/settingslib.aconfig21
-rw-r--r--packages/SettingsLib/res/drawable/ic_bt_le_audio_sharing.xml87
-rw-r--r--packages/SettingsLib/res/values-af/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-am/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ar/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-as/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-az/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-b+sr+Latn/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-be/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-bg/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-bn/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-bs/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ca/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-cs/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-da/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-de/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-el/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-en-rAU/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-en-rCA/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-en-rGB/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-en-rIN/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-en-rXC/strings.xml2
-rw-r--r--packages/SettingsLib/res/values-es-rUS/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-es/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-et/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-eu/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-fa/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-fi/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-fr-rCA/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-fr/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-gl/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-gu/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-hi/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-hr/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-hu/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-hy/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-in/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-is/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-it/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-iw/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ja/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ka/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-kk/strings.xml9
-rw-r--r--packages/SettingsLib/res/values-km/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-kn/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ko/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ky/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-lo/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-lt/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-lv/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-mk/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ml/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-mn/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-mr/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-ms/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-my/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-nb/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ne/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-nl/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-or/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-pa/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-pl/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-pt-rBR/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-pt-rPT/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-pt/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ro/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ru/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-si/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-sk/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-sl/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-sq/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-sr/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-sv/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-sw/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ta/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-te/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-th/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-tl/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-tr/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-uk/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ur/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-uz/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-vi/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-zh-rCN/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-zh-rHK/strings.xml8
-rw-r--r--packages/SettingsLib/res/values-zh-rTW/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-zu/strings.xml6
-rw-r--r--packages/SettingsLib/res/values/strings.xml2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java70
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java81
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java11
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java39
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java10
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java3
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothBroadcastUtils.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothLeBroadcastMetadataExt.kt397
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java40
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java66
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidStatsLogUtils.java113
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java217
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java22
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java13
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/OWNERS3
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java40
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/session/MediaSessionManagerExt.kt44
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/FakeNotificationsSoundPolicyRepository.kt18
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractor.kt93
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/volume/OWNERS5
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt53
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt15
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerExt.kt100
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerRepository.kt55
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioVolumeInteractor.kt86
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioManagerEventsReceiver.kt (renamed from packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioManagerIntentsReceiver.kt)46
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/volume/shared/model/AudioManagerEvent.kt37
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/volume/shared/model/AudioStream.kt2
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java25
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/OWNERS1
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt42
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/LocalMediaRepositoryImplTest.kt6
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/MediaControllerRepositoryImplTest.kt16
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/AudioManagerEventsReceiverTest.kt139
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/FakeAudioManagerEventsReceiver.kt (renamed from packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/FakeAudioManagerIntentsReceiver.kt)16
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java15
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidStatsLogUtilsTest.java88
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java22
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java209
-rw-r--r--packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java3
-rw-r--r--packages/SettingsLib/tests/unit/src/com/android/settingslib/bluetooth/BluetoothLeBroadcastMetadataExtTest.kt74
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java12
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java4
-rw-r--r--packages/Shell/AndroidManifest.xml3
-rw-r--r--packages/SystemUI/Android.bp1
-rw-r--r--packages/SystemUI/accessibility/accessibilitymenu/TEST_MAPPING11
-rw-r--r--packages/SystemUI/accessibility/accessibilitymenu/aconfig/Android.bp4
-rw-r--r--packages/SystemUI/accessibility/accessibilitymenu/res/values-sv/strings.xml2
-rw-r--r--packages/SystemUI/aconfig/Android.bp2
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig100
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt2
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectView.kt51
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt10
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt6
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/PlatformSlider.kt23
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/theme/AndroidColorScheme.kt1
-rw-r--r--packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt2
-rw-r--r--packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/anc/AncModule.kt21
-rw-r--r--packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/mediaoutput/MediaOutputModule.kt21
-rw-r--r--packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/volume/VolumeSlidersModule.kt21
-rw-r--r--packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt25
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt17
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt26
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt11
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt19
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/DragAndDropTargetState.kt21
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt10
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt2
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt6
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt409
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt5
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt (renamed from packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/ClockSection.kt)7
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt58
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt60
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt44
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt2
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposeAwareExtensions.kt22
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt7
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt1
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToShadeTransition.kt5
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToShadeTransition.kt15
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromShadeToQuickSettingsTransition.kt16
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt288
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt27
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt6
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/AncModule.kt53
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncPopup.kt110
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/bottombar/BottomBarModule.kt2
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/bottombar/ui/BottomBarComponent.kt12
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt81
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt3
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/MediaOutputModule.kt44
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt186
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopup.kt116
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/VolumeSlidersModule.kt43
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt200
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/GridVolumeSliders.kt51
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt76
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlidersComponent.kt58
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/HorizontalVolumePanelContent.kt75
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt16
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelComposeScope.kt14
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt127
-rw-r--r--packages/SystemUI/compose/scene/Android.bp1
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt59
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt155
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt55
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt16
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt56
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt11
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt8
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt2
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt46
-rw-r--r--packages/SystemUI/compose/scene/tests/Android.bp1
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt43
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt44
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt88
-rw-r--r--packages/SystemUI/compose/scene/tests/utils/Android.bp1
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt25
-rw-r--r--packages/SystemUI/customization/tests/utils/Android.bp1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt85
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java27
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt26
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt44
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt41
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt24
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt55
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt174
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt67
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt26
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt23
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt55
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalWidgetHostTest.kt166
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt7
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt15
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt26
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt11
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt43
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelTest.kt21
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt72
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt)34
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModelTest.kt105
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModelTest.kt105
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelTest.kt114
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt20
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt40
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModelTest.kt115
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt16
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/MinimumTilesResourceRepositoryTest.kt45
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt97
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt18
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt28
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt41
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt147
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractorTest.kt241
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt143
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt265
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractorTest.kt283
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt31
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterTest.kt13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt7
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt16
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt113
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/PrivacyChipRepositoryTest.kt183
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeHeaderClockRepositoryTest.kt58
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/PrivacyChipInteractorTest.kt154
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeHeaderClockInteractorTest.kt80
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt33
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt33
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java614
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationContentDescriptionTest.kt19
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractorTest.kt306
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt)20
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt442
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java)4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/OWNERS1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioModeInteractorTest.kt (renamed from packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/domain/interactor/AudioModeInteractorTest.kt)9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioVolumeInteractorTest.kt184
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepositoryTest.kt114
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/AncAvailabilityCriteriaTest.kt89
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractorTest.kt101
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteriaTest.kt79
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt121
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/VolumeSliderInteractorTest.kt60
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/DefaultComponentsLayoutManagerTest.kt4
-rw-r--r--packages/SystemUI/plugin/proguard_plugins.flags10
-rw-r--r--packages/SystemUI/proguard_common.flags4
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml2
-rw-r--r--packages/SystemUI/res-keyguard/values/dimens.xml2
-rw-r--r--packages/SystemUI/res/drawable/ic_call.xml26
-rw-r--r--packages/SystemUI/res/drawable/ic_filled_arrow_down.xml25
-rw-r--r--packages/SystemUI/res/drawable/ic_filled_arrow_up.xml25
-rw-r--r--packages/SystemUI/res/drawable/ic_music_note_off.xml25
-rw-r--r--packages/SystemUI/res/drawable/ic_noise_aware.xml26
-rw-r--r--packages/SystemUI/res/drawable/ic_volume_off.xml27
-rw-r--r--packages/SystemUI/res/drawable/media_squiggly_progress.xml2
-rw-r--r--packages/SystemUI/res/drawable/qs_media_background.xml2
-rw-r--r--packages/SystemUI/res/drawable/qs_media_light_source.xml2
-rw-r--r--packages/SystemUI/res/layout/anc_slice.xml20
-rw-r--r--packages/SystemUI/res/layout/media_carousel.xml4
-rw-r--r--packages/SystemUI/res/layout/media_session_view.xml12
-rw-r--r--packages/SystemUI/res/layout/scene_window_root.xml2
-rw-r--r--packages/SystemUI/res/layout/screen_share_dialog.xml8
-rw-r--r--packages/SystemUI/res/values-af/strings.xml47
-rw-r--r--packages/SystemUI/res/values-am/strings.xml47
-rw-r--r--packages/SystemUI/res/values-ar/strings.xml47
-rw-r--r--packages/SystemUI/res/values-as/strings.xml47
-rw-r--r--packages/SystemUI/res/values-az/strings.xml47
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn/strings.xml47
-rw-r--r--packages/SystemUI/res/values-be/strings.xml47
-rw-r--r--packages/SystemUI/res/values-bg/strings.xml49
-rw-r--r--packages/SystemUI/res/values-bn/strings.xml47
-rw-r--r--packages/SystemUI/res/values-bs/strings.xml47
-rw-r--r--packages/SystemUI/res/values-ca/strings.xml47
-rw-r--r--packages/SystemUI/res/values-cs/strings.xml47
-rw-r--r--packages/SystemUI/res/values-da/strings.xml47
-rw-r--r--packages/SystemUI/res/values-de/strings.xml47
-rw-r--r--packages/SystemUI/res/values-el/strings.xml47
-rw-r--r--packages/SystemUI/res/values-en-rAU/strings.xml47
-rw-r--r--packages/SystemUI/res/values-en-rCA/strings.xml8
-rw-r--r--packages/SystemUI/res/values-en-rGB/strings.xml47
-rw-r--r--packages/SystemUI/res/values-en-rIN/strings.xml47
-rw-r--r--packages/SystemUI/res/values-en-rXC/strings.xml8
-rw-r--r--packages/SystemUI/res/values-es-rUS/strings.xml47
-rw-r--r--packages/SystemUI/res/values-es/strings.xml47
-rw-r--r--packages/SystemUI/res/values-et/strings.xml47
-rw-r--r--packages/SystemUI/res/values-eu/strings.xml47
-rw-r--r--packages/SystemUI/res/values-fa/strings.xml49
-rw-r--r--packages/SystemUI/res/values-fi/strings.xml47
-rw-r--r--packages/SystemUI/res/values-fr-rCA/strings.xml49
-rw-r--r--packages/SystemUI/res/values-fr/strings.xml49
-rw-r--r--packages/SystemUI/res/values-gl/strings.xml47
-rw-r--r--packages/SystemUI/res/values-gu/strings.xml47
-rw-r--r--packages/SystemUI/res/values-hi/strings.xml47
-rw-r--r--packages/SystemUI/res/values-hr/strings.xml47
-rw-r--r--packages/SystemUI/res/values-hu/strings.xml49
-rw-r--r--packages/SystemUI/res/values-hy/strings.xml47
-rw-r--r--packages/SystemUI/res/values-in/strings.xml47
-rw-r--r--packages/SystemUI/res/values-is/strings.xml47
-rw-r--r--packages/SystemUI/res/values-it/strings.xml47
-rw-r--r--packages/SystemUI/res/values-iw/strings.xml51
-rw-r--r--packages/SystemUI/res/values-ja/strings.xml22
-rw-r--r--packages/SystemUI/res/values-ka/strings.xml47
-rw-r--r--packages/SystemUI/res/values-kk/strings.xml59
-rw-r--r--packages/SystemUI/res/values-km/strings.xml47
-rw-r--r--packages/SystemUI/res/values-kn/strings.xml47
-rw-r--r--packages/SystemUI/res/values-ko/strings.xml47
-rw-r--r--packages/SystemUI/res/values-ky/strings.xml47
-rw-r--r--packages/SystemUI/res/values-lo/strings.xml47
-rw-r--r--packages/SystemUI/res/values-lt/strings.xml47
-rw-r--r--packages/SystemUI/res/values-lv/strings.xml47
-rw-r--r--packages/SystemUI/res/values-mk/strings.xml47
-rw-r--r--packages/SystemUI/res/values-ml/strings.xml47
-rw-r--r--packages/SystemUI/res/values-mn/strings.xml47
-rw-r--r--packages/SystemUI/res/values-mr/strings.xml53
-rw-r--r--packages/SystemUI/res/values-ms/strings.xml47
-rw-r--r--packages/SystemUI/res/values-my/strings.xml47
-rw-r--r--packages/SystemUI/res/values-nb/strings.xml49
-rw-r--r--packages/SystemUI/res/values-ne/strings.xml47
-rw-r--r--packages/SystemUI/res/values-nl/strings.xml47
-rw-r--r--packages/SystemUI/res/values-or/strings.xml47
-rw-r--r--packages/SystemUI/res/values-pa/strings.xml47
-rw-r--r--packages/SystemUI/res/values-pl/strings.xml47
-rw-r--r--packages/SystemUI/res/values-pt-rBR/strings.xml47
-rw-r--r--packages/SystemUI/res/values-pt-rPT/strings.xml47
-rw-r--r--packages/SystemUI/res/values-pt/strings.xml47
-rw-r--r--packages/SystemUI/res/values-ro/strings.xml51
-rw-r--r--packages/SystemUI/res/values-ru/strings.xml47
-rw-r--r--packages/SystemUI/res/values-si/strings.xml47
-rw-r--r--packages/SystemUI/res/values-sk/strings.xml47
-rw-r--r--packages/SystemUI/res/values-sl/strings.xml47
-rw-r--r--packages/SystemUI/res/values-sq/strings.xml47
-rw-r--r--packages/SystemUI/res/values-sr/strings.xml47
-rw-r--r--packages/SystemUI/res/values-sv/strings.xml47
-rw-r--r--packages/SystemUI/res/values-sw/strings.xml47
-rw-r--r--packages/SystemUI/res/values-sw600dp/config.xml3
-rw-r--r--packages/SystemUI/res/values-ta/strings.xml47
-rw-r--r--packages/SystemUI/res/values-te/strings.xml47
-rw-r--r--packages/SystemUI/res/values-th/strings.xml47
-rw-r--r--packages/SystemUI/res/values-tl/strings.xml47
-rw-r--r--packages/SystemUI/res/values-tr/strings.xml49
-rw-r--r--packages/SystemUI/res/values-uk/strings.xml47
-rw-r--r--packages/SystemUI/res/values-ur/strings.xml47
-rw-r--r--packages/SystemUI/res/values-uz/strings.xml47
-rw-r--r--packages/SystemUI/res/values-vi/strings.xml47
-rw-r--r--packages/SystemUI/res/values-zh-rCN/strings.xml47
-rw-r--r--packages/SystemUI/res/values-zh-rHK/strings.xml47
-rw-r--r--packages/SystemUI/res/values-zh-rTW/strings.xml49
-rw-r--r--packages/SystemUI/res/values-zu/strings.xml47
-rw-r--r--packages/SystemUI/res/values/config.xml3
-rw-r--r--packages/SystemUI/res/values/dimens.xml15
-rw-r--r--packages/SystemUI/res/values/strings.xml19
-rw-r--r--packages/SystemUI/res/values/styles.xml19
-rw-r--r--packages/SystemUI/res/xml/media_session_collapsed.xml9
-rw-r--r--packages/SystemUI/res/xml/media_session_expanded.xml9
-rw-r--r--packages/SystemUI/shared/biometrics/Android.bp1
-rw-r--r--packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/BiometricUserInfo.kt4
-rw-r--r--packages/SystemUI/shared/keyguard/Android.bp1
-rw-r--r--packages/SystemUI/src/com/android/keyguard/ClockEventController.kt17
-rw-r--r--packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt131
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java5
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java111
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java13
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java5
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java48
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java5
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java50
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java8
-rw-r--r--packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/CameraProtectionInfo.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/SysUICutoutProvider.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java74
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt121
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractor.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt37
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPasswordViewBinder.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialogDelegate.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/domain/interactor/FalsingInteractor.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetCategories.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt64
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt57
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalTutorialIndicatorSection.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/util/WidgetPickerIntentUtils.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalWidgetHost.kt (renamed from packages/SystemUI/src/com/android/systemui/communal/shared/CommunalWidgetHost.kt)30
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalWidgetModule.kt91
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/complication/DreamMediaEntryComplication.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/qualifiers/NotifInflation.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractor.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricSettingsInteractor.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt112
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/BiometricMessageModels.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/display/DisplayExtensions.kt47
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt98
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamOverlayViewModel.kt63
-rw-r--r--packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt102
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/FlagDependenciesBase.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/data/repository/GlobalActionsRepository.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/domain/interactor/GlobalActionsInteractor.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/shared/model/StickyKey.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt71
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt174
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt53
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/KeyguardShadeMigrationNssl.kt52
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/AuthenticationFlags.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt86
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardIndicationArea.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInLayer.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/KeyguardSliceViewSection.kt89
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeMediaSection.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt70
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/DefaultClockSteppingTransition.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerMessageAreaViewModel.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt71
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt72
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt58
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt56
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/MediaBrowserLog.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/MediaCarouselControllerLog.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/MediaTimeoutListenerLog.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/MediaViewLog.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/NavbarOrientationTrackingLog.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/SensitiveNotificationProtectionLog.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatest.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataCombineLatest.kt)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilter.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManager.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt)35
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt)40
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilter.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaSessionBasedFilter.kt)10
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListener.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt)6
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutLogger.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutLogger.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/MediaBrowserFactory.java (renamed from packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaBrowserFactory.java)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/MediaResumeListener.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowser.java (renamed from packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowser.java)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserFactory.java (renamed from packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserFactory.java)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserLogger.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserLogger.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaData.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaData.kt)4
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/shared/model/SmartspaceMediaData.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaData.kt)4
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/shared/model/SmartspaceMediaDataProvider.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataProvider.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/AnimationBindHandler.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/AnimationBindHandler.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransition.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/MediaColorSchemes.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaColorSchemes.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/MetadataAnimationHandler.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/MetadataAnimationHandler.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarObserver.kt)6
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt)41
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerLogger.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerLogger.kt)20
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt)17
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselControllerLogger.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java)130
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt)26
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHostStatesManager.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHostStatesManager.kt)3
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt)13
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewLogger.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewLogger.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/drawable/IlluminationDrawable.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/IlluminationDrawable.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/drawable/LightSourceDrawable.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/LightSourceDrawable.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/drawable/SquigglyProgress.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/SquigglyProgress.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/view/GutsViewHolder.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/models/GutsViewHolder.kt)10
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandler.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt)3
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHost.kt)11
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaScrollView.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaScrollView.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaViewHolder.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt)6
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/view/RecommendationViewHolder.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/RecommendationViewHolder.kt)5
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarViewModel.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/util/LocalMediaManagerFactory.kt (renamed from packages/SystemUI/src/com/android/systemui/media/controls/pipeline/LocalMediaManagerFactory.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/util/MediaUiEventLogger.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt99
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dream/MediaComplicationViewController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavbarOrientationTrackingLogger.kt82
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/process/ProcessWrapper.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSImpl.java41
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/MinimumTilesRepository.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt95
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddable.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractor.kt98
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java)207
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt)39
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileServiceInteractor.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/shared/model/UserActionResult.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotKeyguardController.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt63
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java100
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java48
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeLockscreenInteractor.kt79
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt61
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/data/repository/PrivacyChipRepository.kt133
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeHeaderClockRepository.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PrivacyChipInteractor.kt88
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeHeaderClockInteractor.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt94
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/slice/SliceViewManagerExt.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionController.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationClickNotifier.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java299
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java77
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/ColorUpdateLogger.kt153
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationContentDescription.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt162
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java81
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAlertTimeCoordinator.kt61
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/LaunchFullScreenIntentProvider.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifRowController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpNotificationRepository.kt59
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt188
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/CallLayoutSetDataAsyncFactory.kt37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java48
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt61
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/SceneContainerFlagsExtension.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerImpl.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt53
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ComponentSystemUIDialog.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt282
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogFactory.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt53
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerLogger.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/FullscreenLightRevealAnimation.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/DrawableDump.kt201
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java57
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/kotlin/FlowDumper.kt142
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/Events.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/OWNERS8
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dagger/AncModule.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dagger/SpatializerModule.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepository.kt108
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/AncAvailabilityCriteria.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractor.kt76
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/ui/viewmodel/AncViewModel.kt67
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/button/ui/viewmodel/ButtonViewModel.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteria.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputActionsInteractor.kt75
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt51
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaDeviceSession.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/ConnectedDeviceViewModel.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/DeviceIconViewModel.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt125
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/shared/model/VolumePanelComponents.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/CastVolumeInteractor.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/VolumeSliderInteractor.kt98
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/model/SliderType.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt170
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt102
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderState.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderViewModel.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/AudioVolumeComponentViewModel.kt93
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/dagger/VolumePanelComponent.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/domain/DomainModule.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/ui/UiModule.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/ui/activity/VolumePanelActivity.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelState.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java43
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java22
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt14
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java116
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/CameraProtectionLoaderImplTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/FakeCameraProtectionLoader.kt17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/SysUICutoutProviderTest.kt169
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt22
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt38
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java36
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/complication/DreamMediaEntryComplicationTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractorTest.kt56
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/flags/FlagDependenciesTest.kt10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/data/repository/GlobalActionsRepositoryTest.kt69
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/domain/interactor/GlobalActionsInteractorTest.kt67
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt35
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt83
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt104
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt71
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt88
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt41
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/MediaTestUtils.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataCombineLatestTest.java)6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataFilterTest.kt)10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManagerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt)60
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt)97
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaSessionBasedFilterTest.kt)4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListenerTest.kt)6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/MediaResumeListenerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/MediaResumeListenerTest.kt)10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserTest.kt)2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/shared/SmartspaceMediaDataTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataTest.kt)6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaPlayerDataTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/AnimationBindHandlerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/AnimationBindHandlerTest.kt)2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransitionTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/ColorSchemeTransitionTest.kt)6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/MetadataAnimationHandlerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MetadataAnimationHandlerTest.kt)2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/binder/SeekBarObserverTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/SeekBarObserverTest.kt)6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt)4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt)14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt)97
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt)73
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt)7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/drawable/SquigglyProgressTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/SquigglyProgressTest.kt)2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandlerTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandlerTest.kt)2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/view/MediaViewHolderTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/MediaViewHolderTest.kt)2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/SeekBarViewModelTest.kt)2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaComplicationViewControllerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java33
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java)5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java)196
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt73
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java41
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java27
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt43
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerTest.kt18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt52
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java28
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt202
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java62
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java64
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAlertTimeCoordinatorTest.kt99
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java43
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt30
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt66
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt48
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerFlagDisabledTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerTest.kt83
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseControllerTest.kt24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt34
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java50
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualLocationsServiceTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java38
-rw-r--r--packages/SystemUI/tests/utils/src/android/graphics/drawable/TestStubDrawable.kt19
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/FaceHelpMessageDeferralKosmos.kt31
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/FakeCredentialInteractor.kt5
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/classifier/domain/interactor/FalsingInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt7
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt23
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractorKosmos.kt1
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractorKosmos.kt33
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt6
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/globalactions/data/repository/GlobalActionsRepositoryKosmos.kt21
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/globalactions/domain/interactor/GlobalActionsInteractorKosmos.kt23
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitionsKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorKosmos.kt1
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModelKosmos.kt29
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModelKosmos.kt29
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModelKosmos.kt3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/media/MediaKosmos.kt23
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerKosmos.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/MediaHierarchyManagerKosmos.kt)2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt9
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/privacy/PrivacyDialogControllerKosmos.kt23
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/privacy/PrivacyDialogControllerV2Kosmos.kt23
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/FakeIQSTileService.kt61
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/FakeTileServiceManagerFacade.kt59
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/FakeTileServicesFacade.kt39
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TilesExternalKosmos.kt37
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeDefaultTilesRepository.kt22
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt8
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/QSPipelineRepositoryKosmos.kt12
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/CustomTileKosmos.kt56
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/QSTileStateSubject.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterKosmos.kt25
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakePrivacyChipRepository.kt55
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/PrivacyChipRepositoryKosmos.kt24
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeHeaderClockRepositoryKosmos.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TileLifecycleManagerFactoryKosmos.kt)9
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/PrivacyChipInteractorKosmos.kt35
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeHeaderClockInteractorKosmos.kt29
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt32
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerKosmos.kt8
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractorKosmos.kt27
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/data/repository/HeadsUpNotificationRepositoryKosmos.kt28
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HeadsUpNotificationInteractorKosmos.kt26
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt1
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeDeviceProvisionedController.kt25
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/NextAlarmControllerKosmos.kt23
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt22
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeConfigurationController.java5
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt63
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/VolumeKosmos.kt24
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt (renamed from packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeAudioRepository.kt)16
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeLocalMediaRepository.kt (renamed from packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeLocalMediaRepository.kt)32
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeMediaControllerRepository.kt34
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/MediaOutputComponentKosmos.kt25
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/FakeSliceFactory.kt51
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/VolumePanelAncKosmos.kt29
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/data/repository/FakeAncSliceRepository.kt33
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/FakeLocalMediaRepositoryFactory.kt26
-rw-r--r--packages/VpnDialogs/res/values-mr/strings.xml2
-rw-r--r--packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java75
-rw-r--r--packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java47
-rw-r--r--ravenwood/Android.bp59
-rw-r--r--ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodReplace.java10
-rw-r--r--ravenwood/api-maintainers.md2
-rw-r--r--ravenwood/framework-minus-apex-ravenwood-policies.txt1
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java90
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodPermissionEnforcer.java38
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java94
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java85
-rw-r--r--ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java47
-rw-r--r--ravenwood/ravenwood-annotation-allowed-classes.txt14
-rw-r--r--ravenwood/ravenwood-services-jarjar-rules.txt11
-rwxr-xr-xravenwood/run-ravenwood-tests.sh14
-rw-r--r--ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/CursorWindow_host.java (renamed from tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/CursorWindow_host.java)2
-rw-r--r--ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/EventLog_host.java (renamed from tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/EventLog_host.java)2
-rw-r--r--ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/Log_host.java (renamed from tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Log_host.java)2
-rw-r--r--ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/LongArrayMultiStateCounter_host.java (renamed from tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/LongArrayMultiStateCounter_host.java)2
-rw-r--r--ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/LongMultiStateCounter_host.java (renamed from tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/LongMultiStateCounter_host.java)2
-rw-r--r--ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/MessageQueue_host.java (renamed from tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/MessageQueue_host.java)2
-rw-r--r--ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/ParcelFileDescriptor_host.java (renamed from tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/ParcelFileDescriptor_host.java)2
-rw-r--r--ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/Parcel_host.java (renamed from tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java)2
-rw-r--r--ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/SystemProperties_host.java (renamed from tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/SystemProperties_host.java)2
-rw-r--r--ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java (renamed from tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/runtimehelper/ClassLoadHook.java)6
-rw-r--r--ravenwood/runtime-helper-src/libcore-fake/android/system/ErrnoException.java (renamed from tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/android/system/ErrnoException.java)0
-rw-r--r--ravenwood/runtime-helper-src/libcore-fake/com/android/okhttp/internalandroidapi/Dns.java (renamed from tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/com/android/okhttp/internalandroidapi/Dns.java)0
-rw-r--r--ravenwood/runtime-helper-src/libcore-fake/dalvik/system/VMRuntime.java (renamed from tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/dalvik/system/VMRuntime.java)0
-rw-r--r--ravenwood/runtime-helper-src/libcore-fake/libcore/io/IoUtils.java (renamed from tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/io/IoUtils.java)0
-rw-r--r--ravenwood/runtime-helper-src/libcore-fake/libcore/util/EmptyArray.java (renamed from tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/EmptyArray.java)0
-rw-r--r--ravenwood/runtime-helper-src/libcore-fake/libcore/util/HexEncoding.java (renamed from tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/HexEncoding.java)0
-rw-r--r--ravenwood/runtime-helper-src/libcore-fake/libcore/util/SneakyThrow.java (renamed from tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/SneakyThrow.java)0
-rw-r--r--ravenwood/services-test/Android.bp21
-rw-r--r--ravenwood/services-test/test/com/android/ravenwood/RavenwoodServicesTest.java83
-rw-r--r--ravenwood/services.core-ravenwood-policies.txt7
-rw-r--r--rs/java/android/renderscript/ScriptC.java18
-rw-r--r--services/accessibility/accessibility.aconfig27
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java127
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java49
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java168
-rw-r--r--services/accessibility/java/com/android/server/accessibility/BrailleDisplayConnection.java138
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java23
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java13
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java2
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java16
-rw-r--r--services/autofill/Android.bp1
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java125
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/FillUi.java4
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java12
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java20
-rw-r--r--services/backup/flags.aconfig9
-rw-r--r--services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java4
-rw-r--r--services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java181
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java12
-rw-r--r--services/companion/java/com/android/server/companion/PackageUtils.java12
-rw-r--r--services/companion/java/com/android/server/companion/virtual/Android.bp4
-rw-r--r--services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java13
-rw-r--r--services/companion/java/com/android/server/companion/virtual/InputController.java37
-rw-r--r--services/companion/java/com/android/server/companion/virtual/SensorController.java11
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java24
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java5
-rw-r--r--services/companion/java/com/android/server/companion/virtual/audio/VirtualAudioController.java10
-rw-r--r--services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java10
-rw-r--r--services/core/Android.bp56
-rw-r--r--services/core/java/com/android/server/IntentResolver.java16
-rw-r--r--services/core/java/com/android/server/SensitiveContentProtectionManagerService.java107
-rw-r--r--services/core/java/com/android/server/SerialService.java102
-rw-r--r--services/core/java/com/android/server/SystemConfig.java39
-rw-r--r--services/core/java/com/android/server/SystemService.java1
-rw-r--r--services/core/java/com/android/server/SystemServiceManager.java35
-rw-r--r--services/core/java/com/android/server/UiModeManagerService.java4
-rw-r--r--services/core/java/com/android/server/adaptiveauth/AdaptiveAuthService.java238
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerConstants.java2
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java81
-rw-r--r--services/core/java/com/android/server/am/AppProfiler.java2
-rw-r--r--services/core/java/com/android/server/am/AppStartInfoTracker.java5
-rw-r--r--services/core/java/com/android/server/am/EventLogTags.logtags3
-rw-r--r--services/core/java/com/android/server/am/LmkdStatsReporter.java11
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java65
-rw-r--r--services/core/java/com/android/server/am/OomAdjusterModernImpl.java14
-rw-r--r--services/core/java/com/android/server/am/ProcessErrorStateRecord.java13
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java11
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java31
-rw-r--r--services/core/java/com/android/server/am/ProcessServiceRecord.java6
-rw-r--r--services/core/java/com/android/server/am/ProcessStateRecord.java60
-rw-r--r--services/core/java/com/android/server/am/SettingsToPropertiesMapper.java2
-rw-r--r--services/core/java/com/android/server/am/UserController.java23
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java299
-rw-r--r--services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java5
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceInventory.java53
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java131
-rw-r--r--services/core/java/com/android/server/audio/AudioSystemAdapter.java5
-rw-r--r--services/core/java/com/android/server/audio/AudioVolumeGroupHelperBase.java34
-rw-r--r--services/core/java/com/android/server/audio/HardeningEnforcer.java83
-rw-r--r--services/core/java/com/android/server/audio/MediaFocusControl.java12
-rw-r--r--services/core/java/com/android/server/biometrics/AuthService.java45
-rw-r--r--services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java24
-rw-r--r--services/core/java/com/android/server/biometrics/BiometricNotificationLogger.java104
-rw-r--r--services/core/java/com/android/server/biometrics/BiometricService.java23
-rw-r--r--services/core/java/com/android/server/biometrics/TEST_MAPPING10
-rw-r--r--services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java12
-rw-r--r--services/core/java/com/android/server/biometrics/log/BiometricLogger.java5
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java52
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/EnrollClient.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceService.java29
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceShellCommand.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java3
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java12
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java17
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java10
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java27
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintShellCommand.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java9
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java13
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java7
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java19
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java9
-rw-r--r--services/core/java/com/android/server/devicestate/DeviceStateManagerService.java13
-rw-r--r--services/core/java/com/android/server/devicestate/DeviceStateProvider.java12
-rw-r--r--services/core/java/com/android/server/display/BrightnessThrottler.java61
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceConfig.java31
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java21
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java7
-rw-r--r--services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java2
-rw-r--r--services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java8
-rw-r--r--services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java136
-rw-r--r--services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java2
-rw-r--r--services/core/java/com/android/server/display/config/SensorData.java30
-rw-r--r--services/core/java/com/android/server/display/feature/DisplayManagerFlags.java10
-rw-r--r--services/core/java/com/android/server/display/feature/display_flags.aconfig8
-rw-r--r--services/core/java/com/android/server/display/utils/SensorUtils.java15
-rw-r--r--services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionManagerInternal.java5
-rw-r--r--services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java18
-rw-r--r--services/core/java/com/android/server/input/InputManagerInternal.java30
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java114
-rw-r--r--services/core/java/com/android/server/input/KeyboardMetricsCollector.java7
-rw-r--r--services/core/java/com/android/server/input/NativeInputManagerService.java7
-rw-r--r--services/core/java/com/android/server/input/debug/FocusEventDebugView.java21
-rw-r--r--services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java37
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java147
-rw-r--r--services/core/java/com/android/server/inputmethod/ZeroJankProxy.java420
-rw-r--r--services/core/java/com/android/server/location/GeocoderProxy.java122
-rw-r--r--services/core/java/com/android/server/location/LocationManagerService.java59
-rw-r--r--services/core/java/com/android/server/location/provider/LocationProviderManager.java11
-rw-r--r--services/core/java/com/android/server/location/provider/proxy/ProxyGeocodeProvider.java111
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java33
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsStorage.java30
-rw-r--r--services/core/java/com/android/server/media/MediaSession2Record.java22
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java29
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecordImpl.java7
-rw-r--r--services/core/java/com/android/server/media/MediaSessionService.java107
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java72
-rw-r--r--services/core/java/com/android/server/notification/Android.bp6
-rw-r--r--services/core/java/com/android/server/notification/GroupHelper.java57
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java123
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java58
-rw-r--r--services/core/java/com/android/server/notification/flags.aconfig19
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerService.java80
-rw-r--r--services/core/java/com/android/server/ondeviceintelligence/OWNERS1
-rw-r--r--services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java419
-rw-r--r--services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java56
-rw-r--r--services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceTrustedInferenceService.java64
-rw-r--r--services/core/java/com/android/server/pdb/PersistentDataBlockService.java54
-rw-r--r--services/core/java/com/android/server/pm/BackgroundInstallControlService.java72
-rw-r--r--services/core/java/com/android/server/pm/BroadcastHelper.java35
-rw-r--r--services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java99
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java188
-rw-r--r--services/core/java/com/android/server/pm/PackageAbiHelperImpl.java91
-rw-r--r--services/core/java/com/android/server/pm/PackageArchiver.java62
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerHistoricalSession.java9
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java6
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceUtils.java7
-rw-r--r--services/core/java/com/android/server/pm/Settings.java13
-rw-r--r--services/core/java/com/android/server/pm/ShortcutPackage.java2
-rw-r--r--services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java4
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java25
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java25
-rw-r--r--services/core/java/com/android/server/pm/UserRestrictionsUtils.java6
-rw-r--r--services/core/java/com/android/server/pm/UserTypeFactory.java6
-rw-r--r--services/core/java/com/android/server/pm/VerifyingSession.java8
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java50
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java29
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java34
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java62
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java53
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java41
-rw-r--r--services/core/java/com/android/server/policy/DeviceStateProviderImpl.java4
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java12
-rw-r--r--services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java10
-rw-r--r--services/core/java/com/android/server/power/stats/BatteryStatsImpl.java112
-rw-r--r--services/core/java/com/android/server/power/stats/MobileRadioPowerCalculator.java1
-rw-r--r--services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java21
-rw-r--r--services/core/java/com/android/server/search/SearchManagerService.java146
-rw-r--r--services/core/java/com/android/server/search/Searchables.java50
-rw-r--r--services/core/java/com/android/server/selinux/OWNERS1
-rw-r--r--services/core/java/com/android/server/selinux/SelinuxAuditLogsCollector.java2
-rw-r--r--services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java406
-rw-r--r--services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateController.java15
-rw-r--r--services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateControllerImpl.java31
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java5
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java9
-rw-r--r--services/core/java/com/android/server/uri/UriGrantsManagerService.java50
-rw-r--r--services/core/java/com/android/server/utils/EventLogger.java46
-rw-r--r--services/core/java/com/android/server/utils/TimingsTraceAndSlog.java1
-rw-r--r--services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java9
-rw-r--r--services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java5
-rw-r--r--services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java4
-rw-r--r--services/core/java/com/android/server/vibrator/HalVibration.java17
-rw-r--r--services/core/java/com/android/server/vibrator/TEST_MAPPING3
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationScaler.java51
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationSettings.java90
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationStepConductor.java3
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorFrameworkStatsLogger.java19
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorManagerService.java48
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java42
-rw-r--r--services/core/java/com/android/server/wearable/RemoteWearableSensingService.java107
-rw-r--r--services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java128
-rw-r--r--services/core/java/com/android/server/wearable/WearableSensingManagerService.java96
-rw-r--r--services/core/java/com/android/server/wearable/WearableSensingShellCommand.java24
-rw-r--r--services/core/java/com/android/server/webkit/SystemImpl.java51
-rw-r--r--services/core/java/com/android/server/webkit/SystemInterface.java4
-rw-r--r--services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java41
-rw-r--r--services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java97
-rw-r--r--services/core/java/com/android/server/wm/AbsAppSnapshotController.java16
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java496
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java21
-rw-r--r--services/core/java/com/android/server/wm/ActivityCallerState.java96
-rw-r--r--services/core/java/com/android/server/wm/ActivityClientController.java51
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java163
-rw-r--r--services/core/java/com/android/server/wm/ActivityResult.java7
-rw-r--r--services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java53
-rw-r--r--services/core/java/com/android/server/wm/ActivitySnapshotCache.java16
-rw-r--r--services/core/java/com/android/server/wm/ActivitySnapshotController.java9
-rw-r--r--services/core/java/com/android/server/wm/ActivityStartController.java10
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java50
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java13
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java105
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java9
-rw-r--r--services/core/java/com/android/server/wm/AnrController.java2
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java3
-rw-r--r--services/core/java/com/android/server/wm/BackNavigationController.java323
-rw-r--r--services/core/java/com/android/server/wm/BackgroundActivityStartController.java165
-rw-r--r--services/core/java/com/android/server/wm/ClientLifecycleManager.java17
-rw-r--r--services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java40
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java2
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java17
-rw-r--r--services/core/java/com/android/server/wm/DragDropController.java55
-rw-r--r--services/core/java/com/android/server/wm/EmbeddedWindowController.java65
-rw-r--r--services/core/java/com/android/server/wm/Letterbox.java2
-rw-r--r--services/core/java/com/android/server/wm/LetterboxUiController.java41
-rw-r--r--services/core/java/com/android/server/wm/RecentTasks.java110
-rw-r--r--services/core/java/com/android/server/wm/RemoteAnimationController.java3
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java13
-rw-r--r--services/core/java/com/android/server/wm/SensitiveContentPackages.java89
-rw-r--r--services/core/java/com/android/server/wm/Session.java110
-rw-r--r--services/core/java/com/android/server/wm/SnapshotCache.java57
-rw-r--r--services/core/java/com/android/server/wm/SurfaceAnimator.java3
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java19
-rw-r--r--services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java9
-rw-r--r--services/core/java/com/android/server/wm/TaskPersister.java120
-rw-r--r--services/core/java/com/android/server/wm/TaskPositioningController.java10
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotCache.java18
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotController.java7
-rw-r--r--services/core/java/com/android/server/wm/Transition.java7
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java3
-rw-r--r--services/core/java/com/android/server/wm/TrustedPresentationListenerController.java25
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java66
-rw-r--r--services/core/java/com/android/server/wm/WallpaperWindowToken.java23
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java3
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java41
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java155
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerShellCommand.java22
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java5
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java3
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java25
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java3
-rw-r--r--services/core/java/com/android/server/wm/WindowTracing.java17
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp51
-rw-r--r--services/core/xsd/display-device-config/display-device-config.xsd3
-rw-r--r--services/core/xsd/display-device-config/schema/current.txt2
-rw-r--r--services/credentials/java/com/android/server/credentials/CreateRequestSession.java5
-rw-r--r--services/credentials/java/com/android/server/credentials/CredentialManagerUi.java45
-rw-r--r--services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java10
-rw-r--r--services/credentials/java/com/android/server/credentials/GetRequestSession.java6
-rw-r--r--services/credentials/java/com/android/server/credentials/MetricUtilities.java2
-rw-r--r--services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java5
-rw-r--r--services/credentials/java/com/android/server/credentials/ProviderGetSession.java49
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java8
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java27
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java68
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java324
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java6
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java4
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java11
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java24
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java296
-rw-r--r--services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java16
-rw-r--r--services/java/com/android/server/SystemServer.java18
-rw-r--r--services/midi/java/com/android/server/midi/MidiService.java6
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt90
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/PermissionService.kt184
-rw-r--r--services/tests/InputMethodSystemServerTests/Android.bp3
-rw-r--r--services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ClientControllerTest.java44
-rw-r--r--services/tests/PackageManagerServiceTests/apks/Android.bp1
-rw-r--r--services/tests/PackageManagerServiceTests/apks/install-split-base/Android.bp1
-rw-r--r--services/tests/PackageManagerServiceTests/apks/install-split-feature-a/Android.bp1
-rw-r--r--services/tests/PackageManagerServiceTests/apks/install/Android.bp1
-rw-r--r--services/tests/PackageManagerServiceTests/apks/install_bad_dex/Android.bp1
-rw-r--r--services/tests/PackageManagerServiceTests/apks/install_complete_package_info/Android.bp1
-rw-r--r--services/tests/PackageManagerServiceTests/apks/install_decl_perm/Android.bp1
-rw-r--r--services/tests/PackageManagerServiceTests/apks/install_intent_filters/Android.bp1
-rw-r--r--services/tests/PackageManagerServiceTests/apks/install_loc_auto/Android.bp1
-rw-r--r--services/tests/PackageManagerServiceTests/apks/install_loc_internal/Android.bp1
-rw-r--r--services/tests/PackageManagerServiceTests/apks/install_loc_sdcard/Android.bp1
-rw-r--r--services/tests/PackageManagerServiceTests/apks/install_loc_unspecified/Android.bp1
-rw-r--r--services/tests/PackageManagerServiceTests/apks/install_target_sdk_22/Android.bp1
-rw-r--r--services/tests/PackageManagerServiceTests/apks/install_target_sdk_23/Android.bp1
-rw-r--r--services/tests/PackageManagerServiceTests/apks/install_use_perm_good/Android.bp1
-rw-r--r--services/tests/PackageManagerServiceTests/apks/install_uses_feature/Android.bp1
-rw-r--r--services/tests/PackageManagerServiceTests/apks/install_uses_sdk/Android.bp1
-rw-r--r--services/tests/PackageManagerServiceTests/apks/keyset/Android.bp1
-rw-r--r--services/tests/PackageManagerServiceTests/host/Android.bp1
-rw-r--r--services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp1
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/Apex/Android.bp1
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp1
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/Generic/Android.bp1
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/Overlay/Android.bp1
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/Android.bp1
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/OverlayTarget/Android.bp1
-rw-r--r--services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/Android.bp1
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt3
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/BrightnessThrottlerTest.java67
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java15
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java8
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java39
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalClamperTest.java91
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java2
-rw-r--r--services/tests/dreamservicetests/Android.bp1
-rw-r--r--services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java15
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/OWNERS1
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java178
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceContentTest.java145
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceNotificationTest.java (renamed from services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceTest.java)10
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java121
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java1
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java530
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java6
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java144
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java3
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java798
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/rollback/TEST_MAPPING12
-rw-r--r--services/tests/powerservicetests/Android.bp1
-rw-r--r--services/tests/powerstatstests/Android.bp1
-rw-r--r--services/tests/powerstatstests/BstatsTestApp/Android.bp1
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java141
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java79
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java87
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterInputTest.kt317
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java18
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/BrailleDisplayConnectionTest.java354
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/adaptiveauth/AdaptiveAuthServiceTest.java333
-rw-r--r--services/tests/servicestests/src/com/android/server/adaptiveauth/OWNERS1
-rw-r--r--services/tests/servicestests/src/com/android/server/am/UserControllerTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java726
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java15
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/BiometricNotificationLoggerTest.java157
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java19
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java19
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapterTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java31
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java30
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java35
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java145
-rw-r--r--services/tests/servicestests/src/com/android/server/pdb/PersistentDataBlockServiceTest.java85
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java154
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java23
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java259
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/search/SearchablesTest.java321
-rw-r--r--services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java58
-rw-r--r--services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java50
-rw-r--r--services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java22
-rw-r--r--services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java25
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java115
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java88
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java15
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java183
-rw-r--r--services/tests/vibrator/Android.bp1
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java63
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java35
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java37
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java220
-rw-r--r--services/tests/vibrator/utils/com/android/server/vibrator/VibrationParamGenerator.java54
-rw-r--r--services/tests/wmtests/Android.bp11
-rw-r--r--services/tests/wmtests/AndroidTest.xml7
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java7
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java27
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java6
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java424
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java17
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java19
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java26
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncherTest.java101
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ProtoLogIntegrationTest.java21
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java29
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java147
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SensitiveContentPackagesTest.java134
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java322
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTest.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskPersisterTest.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java7
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java32
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java80
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java21
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java192
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/DspTrustedHotwordDetectorSession.java4
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordAudioStreamCopier.java148
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java58
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/SoftwareTrustedHotwordDetectorSession.java4
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VisualQueryDetectorSession.java32
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java104
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java97
-rw-r--r--telecomm/java/Android.bp1
-rw-r--r--telecomm/java/android/telecom/CallAttributes.java12
-rw-r--r--telecomm/java/android/telecom/CallAudioState.java10
-rw-r--r--telecomm/java/android/telecom/CallControl.java40
-rw-r--r--telecomm/java/android/telecom/CallEventCallback.java11
-rw-r--r--telecomm/java/android/telecom/DisconnectCause.java116
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java70
-rw-r--r--telecomm/java/com/android/internal/telecom/ClientTransactionalServiceWrapper.java13
-rw-r--r--telecomm/java/com/android/internal/telecom/ICallControl.aidl1
-rw-r--r--telecomm/java/com/android/internal/telecom/ICallEventCallback.aidl2
-rw-r--r--telecomm/java/com/android/internal/telecom/ITelecomService.aidl6
-rw-r--r--telephony/common/com/android/internal/telephony/util/TelephonyUtils.java19
-rw-r--r--telephony/java/android/service/euicc/EuiccService.java18
-rw-r--r--telephony/java/android/service/euicc/IGetAvailableMemoryInBytesCallback.aidl1
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java82
-rw-r--r--telephony/java/android/telephony/DomainSelectionService.java6
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java32
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java23
-rw-r--r--telephony/java/android/telephony/data/ApnSetting.java55
-rw-r--r--telephony/java/android/telephony/data/IQualifiedNetworksServiceCallback.aidl1
-rw-r--r--telephony/java/android/telephony/data/QualifiedNetworksService.java57
-rw-r--r--telephony/java/android/telephony/emergency/EmergencyNumber.java99
-rw-r--r--telephony/java/com/android/internal/telephony/ISub.aidl13
-rw-r--r--tests/BootImageProfileTest/AndroidTest.xml1
-rw-r--r--tests/EnforcePermission/Android.bp1
-rw-r--r--tests/EnforcePermission/perf-app/Android.bp1
-rw-r--r--tests/EnforcePermission/service-app/Android.bp1
-rw-r--r--tests/EnforcePermission/test-app/Android.bp1
-rw-r--r--tests/FlickerTests/AppClose/Android.bp1
-rw-r--r--tests/FlickerTests/IME/Android.bp1
-rw-r--r--tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt1
-rw-r--r--tests/FlickerTests/Rotation/Android.bp1
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt1
-rw-r--r--tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt5
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml16
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java25
-rw-r--r--tests/FsVerityTest/Android.bp1
-rw-r--r--tests/FsVerityTest/FsVerityTestApp/Android.bp1
-rw-r--r--tests/FsVerityTest/block_device_writer/Android.bp1
-rw-r--r--tests/FsVerityTest/testdata/Android.bp1
-rw-r--r--tests/HandwritingIme/Android.bp1
-rw-r--r--tests/Input/AndroidTest.xml6
-rw-r--r--tests/InputScreenshotTest/Android.bp1
-rw-r--r--tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.pngbin77207 -> 77171 bytes
-rw-r--r--tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-ansi.pngbin70708 -> 70094 bytes
-rw-r--r--tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-jis.pngbin73441 -> 72593 bytes
-rw-r--r--tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.pngbin71619 -> 70807 bytes
-rw-r--r--tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.pngbin45707 -> 45677 bytes
-rw-r--r--tests/InputScreenshotTest/robotests/Android.bp1
-rw-r--r--tests/Internal/Android.bp3
-rw-r--r--tests/Internal/AndroidManifest.xml6
-rw-r--r--tests/Internal/res/xml/network_security_config.xml21
-rw-r--r--tests/Internal/src/com/android/internal/protolog/LegacyProtoLogImplTest.java396
-rw-r--r--tests/Internal/src/com/android/internal/protolog/OWNERS3
-rw-r--r--tests/Internal/src/com/android/internal/protolog/PerfettoDataSourceTest.java167
-rw-r--r--tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java586
-rw-r--r--tests/Internal/src/com/android/internal/protolog/ProtoLogImplTest.java326
-rw-r--r--tests/Internal/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java8
-rw-r--r--tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/EmbeddedWindowService.java5
-rw-r--r--tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/IAttachEmbeddedWindow.aidl4
-rw-r--r--tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java8
-rw-r--r--tests/graphics/HwAccelerationTest/Android.bp1
-rw-r--r--tests/graphics/HwAccelerationTest/jni/Android.bp1
-rw-r--r--tests/graphics/RenderThreadTest/Android.bp1
-rw-r--r--tests/graphics/SilkFX/Android.bp1
-rw-r--r--tests/graphics/VectorDrawableTest/Android.bp1
-rw-r--r--tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp1
-rw-r--r--tests/vcn/Android.bp2
-rw-r--r--tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java19
-rw-r--r--tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java4
-rw-r--r--tools/hoststubgen/hoststubgen/Android.bp35
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt76
-rw-r--r--tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt24
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt561
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt160
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt258
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt160
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt411
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt21
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C1.java19
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C2.java19
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C3.java19
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CA.java19
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CB.java19
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.java19
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.java19
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.java19
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.java19
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.java19
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.java19
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.java19
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.java19
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.java19
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.java19
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.java19
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.java19
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.java19
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.java19
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.java19
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.java19
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.java19
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I1.java19
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I2.java19
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I3.java19
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IA.java19
-rw-r--r--tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IB.java19
-rwxr-xr-xtools/hoststubgen/scripts/run-all-tests.sh54
-rw-r--r--tools/protologtool/Android.bp4
-rw-r--r--tools/protologtool/README.md15
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/CodeUtils.kt13
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/CommandOptions.kt137
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/LogLevel.kt38
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/MethodCallVisitor.kt23
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessor.kt99
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt145
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/ProtoLogCallVisitor.kt1
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt223
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt255
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/ViewerConfigBuilder.kt124
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/ViewerConfigJsonBuilder.kt62
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/ViewerConfigParser.kt10
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/ViewerConfigProtoBuilder.kt63
-rw-r--r--tools/protologtool/tests/com/android/protolog/tool/CodeUtilsTest.kt11
-rw-r--r--tools/protologtool/tests/com/android/protolog/tool/CommandOptionsTest.kt338
-rw-r--r--tools/protologtool/tests/com/android/protolog/tool/EndToEndTest.kt74
-rw-r--r--tools/protologtool/tests/com/android/protolog/tool/LogParserTest.kt2
-rw-r--r--tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorImplTest.kt (renamed from tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorTest.kt)10
-rw-r--r--tools/protologtool/tests/com/android/protolog/tool/ProtoLogToolTest.kt52
-rw-r--r--tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt245
-rw-r--r--tools/protologtool/tests/com/android/protolog/tool/ViewerConfigJsonBuilderTest.kt (renamed from tools/protologtool/tests/com/android/protolog/tool/ViewerConfigBuilderTest.kt)48
-rw-r--r--wifi/TEST_MAPPING4
2495 files changed, 68722 insertions, 22461 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 7e6c30f0719a..20471682dd2e 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -12,170 +12,109 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-aconfig_srcjars = [
- // !!! KEEP THIS LIST ALPHABETICAL !!!
- ":aconfig_mediacodec_flags_java_lib{.generated_srcjars}",
- ":android.adaptiveauth.flags-aconfig-java{.generated_srcjars}",
- ":android.app.flags-aconfig-java{.generated_srcjars}",
- ":android.app.smartspace.flags-aconfig-java{.generated_srcjars}",
- ":android.app.usage.flags-aconfig-java{.generated_srcjars}",
- ":android.app.wearable.flags-aconfig-java{.generated_srcjars}",
- ":android.appwidget.flags-aconfig-java{.generated_srcjars}",
- ":android.chre.flags-aconfig-java{.generated_srcjars}",
- ":android.companion.flags-aconfig-java{.generated_srcjars}",
- ":android.companion.virtual.flags-aconfig-java{.generated_srcjars}",
- ":android.companion.virtualdevice.flags-aconfig-java{.generated_srcjars}",
- ":android.content.flags-aconfig-java{.generated_srcjars}",
- ":android.content.pm.flags-aconfig-java{.generated_srcjars}",
- ":android.content.res.flags-aconfig-java{.generated_srcjars}",
- ":android.crashrecovery.flags-aconfig-java{.generated_srcjars}",
- ":android.credentials.flags-aconfig-java{.generated_srcjars}",
- ":android.database.sqlite-aconfig-java{.generated_srcjars}",
- ":android.hardware.biometrics.flags-aconfig-java{.generated_srcjars}",
- ":android.hardware.flags-aconfig-java{.generated_srcjars}",
- ":android.hardware.radio.flags-aconfig-java{.generated_srcjars}",
- ":android.hardware.usb.flags-aconfig-java{.generated_srcjars}",
- ":android.location.flags-aconfig-java{.generated_srcjars}",
- ":android.media.codec-aconfig-java{.generated_srcjars}",
- ":android.media.tv.flags-aconfig-java{.generated_srcjars}",
- ":android.multiuser.flags-aconfig-java{.generated_srcjars}",
- ":android.net.platform.flags-aconfig-java{.generated_srcjars}",
- ":android.net.vcn.flags-aconfig-java{.generated_srcjars}",
- ":android.net.wifi.flags-aconfig-java{.generated_srcjars}",
- ":android.nfc.flags-aconfig-java{.generated_srcjars}",
- ":android.os.flags-aconfig-java{.generated_srcjars}",
- ":android.os.vibrator.flags-aconfig-java{.generated_srcjars}",
- ":android.permission.flags-aconfig-java{.generated_srcjars}",
- ":android.provider.flags-aconfig-java{.generated_srcjars}",
- ":android.security.flags-aconfig-java{.generated_srcjars}",
- ":android.server.app.flags-aconfig-java{.generated_srcjars}",
- ":android.service.autofill.flags-aconfig-java{.generated_srcjars}",
- ":android.service.chooser.flags-aconfig-java{.generated_srcjars}",
- ":android.service.controls.flags-aconfig-java{.generated_srcjars}",
- ":android.service.dreams.flags-aconfig-java{.generated_srcjars}",
- ":android.service.notification.flags-aconfig-java{.generated_srcjars}",
- ":android.service.appprediction.flags-aconfig-java{.generated_srcjars}",
- ":android.service.voice.flags-aconfig-java{.generated_srcjars}",
- ":android.speech.flags-aconfig-java{.generated_srcjars}",
- ":android.systemserver.flags-aconfig-java{.generated_srcjars}",
- ":android.tracing.flags-aconfig-java{.generated_srcjars}",
- ":android.view.accessibility.flags-aconfig-java{.generated_srcjars}",
- ":android.view.contentcapture.flags-aconfig-java{.generated_srcjars}",
- ":android.view.contentprotection.flags-aconfig-java{.generated_srcjars}",
- ":android.view.flags-aconfig-java{.generated_srcjars}",
- ":android.view.inputmethod.flags-aconfig-java{.generated_srcjars}",
- ":android.webkit.flags-aconfig-java{.generated_srcjars}",
- ":android.widget.flags-aconfig-java{.generated_srcjars}",
- ":audio-framework-aconfig",
- ":backup_flags_lib{.generated_srcjars}",
- ":camera_platform_flags_core_java_lib{.generated_srcjars}",
- ":com.android.hardware.input-aconfig-java{.generated_srcjars}",
- ":com.android.input.flags-aconfig-java{.generated_srcjars}",
- ":com.android.internal.foldables.flags-aconfig-java{.generated_srcjars}",
- ":com.android.internal.pm.pkg.component.flags-aconfig-java{.generated_srcjars}",
- ":com.android.media.flags.bettertogether-aconfig-java{.generated_srcjars}",
- ":com.android.media.flags.editing-aconfig-java{.generated_srcjars}",
- ":com.android.net.thread.flags-aconfig-java{.generated_srcjars}",
- ":com.android.server.flags.services-aconfig-java{.generated_srcjars}",
- ":com.android.text.flags-aconfig-java{.generated_srcjars}",
- ":com.android.window.flags.window-aconfig-java{.generated_srcjars}",
- ":device_policy_aconfig_flags_lib{.generated_srcjars}",
- ":display_flags_lib{.generated_srcjars}",
- ":framework-jobscheduler-job.flags-aconfig-java{.generated_srcjars}",
- ":framework_graphics_flags_java_lib{.generated_srcjars}",
- ":hwui_flags_java_lib{.generated_srcjars}",
- ":power_flags_lib{.generated_srcjars}",
- ":sdk_sandbox_flags_lib{.generated_srcjars}",
- ":surfaceflinger_flags_java_lib{.generated_srcjars}",
- ":telecom_flags_core_java_lib{.generated_srcjars}",
- ":telephony_flags_core_java_lib{.generated_srcjars}",
- // !!! KEEP THIS LIST ALPHABETICAL !!!
-]
-
-stubs_defaults {
+aconfig_declarations_group {
name: "framework-minus-apex-aconfig-declarations",
- aconfig_declarations: [
- "android.app.flags-aconfig",
- "android.app.smartspace.flags-aconfig",
- "android.app.usage.flags-aconfig",
- "android.appwidget.flags-aconfig",
- "android.companion.flags-aconfig",
- "android.companion.virtual.flags-aconfig",
- "android.content.pm.flags-aconfig",
- "android.content.res.flags-aconfig",
- "android.crashrecovery.flags-aconfig",
- "android.credentials.flags-aconfig",
- "android.database.sqlite-aconfig",
- "android.hardware.biometrics.flags-aconfig",
- "android.hardware.flags-aconfig",
- "android.hardware.radio.flags-aconfig",
- "android.hardware.usb.flags-aconfig",
- "android.location.flags-aconfig",
- "android.media.audio-aconfig",
- "android.media.audiopolicy-aconfig",
- "android.media.midi-aconfig",
- "android.media.tv.flags-aconfig",
- "android.multiuser.flags-aconfig",
- "android.net.platform.flags-aconfig",
- "android.net.vcn.flags-aconfig",
- "android.net.wifi.flags-aconfig",
- "android.nfc.flags-aconfig",
- "android.os.flags-aconfig",
- "android.os.vibrator.flags-aconfig",
- "android.permission.flags-aconfig",
- "android.provider.flags-aconfig",
- "android.security.flags-aconfig",
- "android.server.app.flags-aconfig",
- "android.service.appprediction.flags-aconfig",
- "android.service.autofill.flags-aconfig",
- "android.service.chooser.flags-aconfig",
- "android.service.controls.flags-aconfig",
- "android.service.dreams.flags-aconfig",
- "android.service.notification.flags-aconfig",
- "android.service.voice.flags-aconfig",
- "android.speech.flags-aconfig",
- "android.tracing.flags-aconfig",
- "android.view.accessibility.flags-aconfig",
- "android.view.contentcapture.flags-aconfig",
- "android.view.contentprotection.flags-aconfig",
- "android.view.flags-aconfig",
- "android.view.inputmethod.flags-aconfig",
- "android.webkit.flags-aconfig",
- "android.widget.flags-aconfig",
- "camera_platform_flags",
- "chre_flags",
- "com.android.hardware.input.input-aconfig",
- "com.android.input.flags-aconfig",
- "com.android.media.flags.bettertogether-aconfig",
- "com.android.net.thread.flags-aconfig",
- "com.android.server.flags.services-aconfig",
- "com.android.text.flags-aconfig",
- "com.android.window.flags.window-aconfig",
- "device_policy_aconfig_flags",
- "display_flags",
- "fold_lock_setting_flags",
- "framework-jobscheduler-job.flags-aconfig",
- "framework_graphics_flags",
- "hwui_flags",
- "power_flags",
- "sdk_sandbox_flags",
- "surfaceflinger_flags",
- "telecom_flags",
- "telephony_flags",
+ aconfig_declarations_groups: [
+ "audio-framework-aconfig",
+ ],
+ java_aconfig_libraries: [
+ // !!! KEEP THIS LIST ALPHABETICAL !!!
+ "aconfig_mediacodec_flags_java_lib",
+ "android.adaptiveauth.flags-aconfig-java",
+ "android.app.flags-aconfig-java",
+ "android.app.ondeviceintelligence-aconfig-java",
+ "android.app.smartspace.flags-aconfig-java",
+ "android.app.usage.flags-aconfig-java",
+ "android.app.wearable.flags-aconfig-java",
+ "android.appwidget.flags-aconfig-java",
+ "android.chre.flags-aconfig-java",
+ "android.companion.flags-aconfig-java",
+ "android.companion.virtual.flags-aconfig-java",
+ "android.companion.virtualdevice.flags-aconfig-java",
+ "android.content.flags-aconfig-java",
+ "android.content.pm.flags-aconfig-java",
+ "android.content.res.flags-aconfig-java",
+ "android.crashrecovery.flags-aconfig-java",
+ "android.credentials.flags-aconfig-java",
+ "android.database.sqlite-aconfig-java",
+ "android.hardware.biometrics.flags-aconfig-java",
+ "android.hardware.devicestate.feature.flags-aconfig-java",
+ "android.hardware.flags-aconfig-java",
+ "android.hardware.radio.flags-aconfig-java",
+ "android.hardware.usb.flags-aconfig-java",
+ "android.location.flags-aconfig-java",
+ "android.media.codec-aconfig-java",
+ "android.media.tv.flags-aconfig-java",
+ "android.multiuser.flags-aconfig-java",
+ "android.net.platform.flags-aconfig-java",
+ "android.net.vcn.flags-aconfig-java",
+ "android.net.wifi.flags-aconfig-java",
+ "android.nfc.flags-aconfig-java",
+ "android.os.flags-aconfig-java",
+ "android.os.vibrator.flags-aconfig-java",
+ "android.permission.flags-aconfig-java",
+ "android.provider.flags-aconfig-java",
+ "android.security.flags-aconfig-java",
+ "android.server.app.flags-aconfig-java",
+ "android.service.autofill.flags-aconfig-java",
+ "android.service.chooser.flags-aconfig-java",
+ "android.service.controls.flags-aconfig-java",
+ "android.service.dreams.flags-aconfig-java",
+ "android.service.notification.flags-aconfig-java",
+ "android.service.appprediction.flags-aconfig-java",
+ "android.service.voice.flags-aconfig-java",
+ "android.speech.flags-aconfig-java",
+ "android.systemserver.flags-aconfig-java",
+ "android.tracing.flags-aconfig-java",
+ "android.view.accessibility.flags-aconfig-java",
+ "android.view.contentcapture.flags-aconfig-java",
+ "android.view.contentprotection.flags-aconfig-java",
+ "android.view.flags-aconfig-java",
+ "android.view.inputmethod.flags-aconfig-java",
+ "android.webkit.flags-aconfig-java",
+ "android.widget.flags-aconfig-java",
+ "backup_flags_lib",
+ "camera_platform_flags_core_java_lib",
+ "com.android.hardware.input-aconfig-java",
+ "com.android.input.flags-aconfig-java",
+ "com.android.internal.foldables.flags-aconfig-java",
+ "com.android.internal.pm.pkg.component.flags-aconfig-java",
+ "com.android.media.flags.bettertogether-aconfig-java",
+ "com.android.media.flags.editing-aconfig-java",
+ "com.android.media.flags.projection-aconfig-java",
+ "com.android.net.thread.platform.flags-aconfig-java",
+ "com.android.server.flags.services-aconfig-java",
+ "com.android.text.flags-aconfig-java",
+ "com.android.window.flags.window-aconfig-java",
+ "device_policy_aconfig_flags_lib",
+ "display_flags_lib",
+ "framework-jobscheduler-job.flags-aconfig-java",
+ "framework_graphics_flags_java_lib",
+ "hwui_flags_java_lib",
+ "power_flags_lib",
+ "sdk_sandbox_flags_lib",
+ "surfaceflinger_flags_java_lib",
+ "telecom_flags_core_java_lib",
+ "telephony_flags_core_java_lib",
+ // !!! KEEP THIS LIST ALPHABETICAL !!!
],
}
filegroup {
name: "framework-minus-apex-aconfig-srcjars",
- srcs: aconfig_srcjars,
+ srcs: [
+ ":framework-minus-apex-aconfig-declarations{.srcjars}",
+ ],
}
// Aconfig declarations and libraries for the core framework
java_defaults {
name: "framework-minus-apex-aconfig-libraries",
// Add java_aconfig_libraries to here to add them to the core framework
- srcs: aconfig_srcjars,
// Add aconfig-annotations-lib as a dependency for the optimization
+ srcs: [
+ ":framework-minus-apex-aconfig-declarations{.srcjars}",
+ ],
libs: ["aconfig-annotations-lib"],
}
@@ -226,6 +165,19 @@ java_aconfig_library {
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+// DeviceStateManager
+aconfig_declarations {
+ name: "android.hardware.devicestate.feature.flags-aconfig",
+ package: "android.hardware.devicestate.feature.flags",
+ srcs: ["core/java/android/hardware/devicestate/feature/*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.hardware.devicestate.feature.flags-aconfig-java",
+ aconfig_declarations: "android.hardware.devicestate.feature.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// Input
aconfig_declarations {
name: "com.android.hardware.input.input-aconfig",
@@ -566,6 +518,21 @@ java_aconfig_library {
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+// MediaProjection
+aconfig_declarations {
+ name: "com.android.media.flags.projection-aconfig",
+ package: "com.android.media.projection.flags",
+ srcs: [
+ "media/java/android/media/flags/projection.aconfig",
+ ],
+}
+
+java_aconfig_library {
+ name: "com.android.media.flags.projection-aconfig-java",
+ aconfig_declarations: "com.android.media.flags.projection-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// Media TV
aconfig_declarations {
name: "android.media.tv.flags-aconfig",
@@ -579,6 +546,19 @@ java_aconfig_library {
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+// OnDeviceIntelligence
+aconfig_declarations {
+ name: "android.app.ondeviceintelligence-aconfig",
+ package: "android.app.ondeviceintelligence.flags",
+ srcs: ["core/java/android/app/ondeviceintelligence/flags/ondevice_intelligence.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.app.ondeviceintelligence-aconfig-java",
+ aconfig_declarations: "android.app.ondeviceintelligence-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// Permissions
aconfig_declarations {
name: "android.permission.flags-aconfig",
@@ -806,8 +786,8 @@ aconfig_declarations {
// Thread network
aconfig_declarations {
- name: "com.android.net.thread.flags-aconfig",
- package: "com.android.net.thread.flags",
+ name: "com.android.net.thread.platform.flags-aconfig",
+ package: "com.android.net.thread.platform.flags",
srcs: ["core/java/android/net/thread/flags.aconfig"],
}
@@ -819,8 +799,8 @@ java_aconfig_library {
}
java_aconfig_library {
- name: "com.android.net.thread.flags-aconfig-java",
- aconfig_declarations: "com.android.net.thread.flags-aconfig",
+ name: "com.android.net.thread.platform.flags-aconfig-java",
+ aconfig_declarations: "com.android.net.thread.platform.flags-aconfig",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
@@ -930,6 +910,8 @@ java_aconfig_library {
aconfig_declarations {
name: "android.service.notification.flags-aconfig",
package: "android.service.notification",
+ exportable: true,
+ container: "system",
srcs: ["core/java/android/service/notification/flags.aconfig"],
}
@@ -939,6 +921,18 @@ java_aconfig_library {
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+java_aconfig_library {
+ name: "android.service.notification.flags-aconfig-export-java",
+ aconfig_declarations: "android.service.notification.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+ mode: "exported",
+ min_sdk_version: "30",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.extservices",
+ ],
+}
+
// Smartspace
aconfig_declarations {
name: "android.app.smartspace.flags-aconfig",
@@ -1005,6 +999,11 @@ java_aconfig_library {
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+cc_aconfig_library {
+ name: "android.tracing.flags_c_lib",
+ aconfig_declarations: "android.tracing.flags-aconfig",
+}
+
// App Widgets
aconfig_declarations {
name: "android.appwidget.flags-aconfig",
diff --git a/Android.bp b/Android.bp
index e12f74fcd7ca..5ada10d19f5d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -386,7 +386,7 @@ java_defaults {
// TODO(b/120066492): remove gps_debug and protolog.conf.json when the build
// system propagates "required" properly.
"gps_debug.conf",
- "protolog.conf.json.gz",
+ "core.protolog.pb",
"framework-res",
// any install dependencies should go into framework-minus-apex-install-dependencies
// rather than here to avoid bloating incremental build time
@@ -508,6 +508,7 @@ java_library {
lint: {
baseline_filename: "lint-baseline.xml",
},
+ jarjar_prefix: "com.android.internal.hidden_from_bootclasspath",
}
java_library {
diff --git a/LSE_APP_COMPAT_OWNERS b/LSE_APP_COMPAT_OWNERS
new file mode 100644
index 000000000000..3db0cd47ce65
--- /dev/null
+++ b/LSE_APP_COMPAT_OWNERS
@@ -0,0 +1,6 @@
+# Owners for the App Compat flags (large_screen_experiences_app_compat)
+mcarli@google.com
+eevlachavas@google.com
+gracielawputri@google.com
+minagranic@google.com
+mariiasand@google.com
diff --git a/Ravenwood.bp b/Ravenwood.bp
index 2babf6a56ab4..2df6d5811d44 100644
--- a/Ravenwood.bp
+++ b/Ravenwood.bp
@@ -78,6 +78,73 @@ java_genrule {
}
java_library {
+ name: "services.core-for-hoststubgen",
+ installable: false, // host only jar.
+ static_libs: [
+ "services.core",
+ ],
+ sdk_version: "core_platform",
+ visibility: ["//visibility:private"],
+}
+
+java_genrule {
+ name: "services.core.ravenwood-base",
+ tools: ["hoststubgen"],
+ cmd: "$(location hoststubgen) " +
+ "@$(location ravenwood/ravenwood-standard-options.txt) " +
+
+ "--debug-log $(location hoststubgen_services.core.log) " +
+ "--stats-file $(location hoststubgen_services.core_stats.csv) " +
+
+ "--out-impl-jar $(location ravenwood.jar) " +
+
+ "--gen-keep-all-file $(location hoststubgen_keep_all.txt) " +
+ "--gen-input-dump-file $(location hoststubgen_dump.txt) " +
+
+ "--in-jar $(location :services.core-for-hoststubgen) " +
+ "--policy-override-file $(location ravenwood/services.core-ravenwood-policies.txt) " +
+ "--annotation-allowed-classes-file $(location ravenwood/ravenwood-annotation-allowed-classes.txt) ",
+ srcs: [
+ ":services.core-for-hoststubgen",
+ "ravenwood/services.core-ravenwood-policies.txt",
+ "ravenwood/ravenwood-standard-options.txt",
+ "ravenwood/ravenwood-annotation-allowed-classes.txt",
+ ],
+ out: [
+ "ravenwood.jar",
+
+ // Following files are created just as FYI.
+ "hoststubgen_keep_all.txt",
+ "hoststubgen_dump.txt",
+
+ "hoststubgen_services.core.log",
+ "hoststubgen_services.core_stats.csv",
+ ],
+ visibility: ["//visibility:private"],
+}
+
+java_genrule {
+ name: "services.core.ravenwood",
+ defaults: ["ravenwood-internal-only-visibility-genrule"],
+ cmd: "cp $(in) $(out)",
+ srcs: [
+ ":services.core.ravenwood-base{ravenwood.jar}",
+ ],
+ out: [
+ "services.core.ravenwood.jar",
+ ],
+}
+
+java_library {
+ name: "services.core.ravenwood-jarjar",
+ installable: false,
+ static_libs: [
+ "services.core.ravenwood",
+ ],
+ jarjar_rules: ":ravenwood-services-jarjar-rules",
+}
+
+java_library {
name: "mockito-ravenwood-prebuilt",
installable: false,
static_libs: [
@@ -93,17 +160,35 @@ java_library {
],
}
+// Jars in "ravenwood-runtime" are set to the classpath, sorted alphabetically.
+// Rename some of the dependencies to make sure they're included in the intended order.
+java_genrule {
+ name: "100-framework-minus-apex.ravenwood",
+ cmd: "cp $(in) $(out)",
+ srcs: [":framework-minus-apex.ravenwood"],
+ out: ["100-framework-minus-apex.ravenwood.jar"],
+ visibility: ["//visibility:private"],
+}
+
+java_genrule {
+ // Use 200 to make sure it comes before the mainline stub ("all-updatable...").
+ name: "200-kxml2-android",
+ cmd: "cp $(in) $(out)",
+ srcs: [":kxml2-android"],
+ out: ["200-kxml2-android.jar"],
+ visibility: ["//visibility:private"],
+}
+
android_ravenwood_libgroup {
name: "ravenwood-runtime",
libs: [
- // Prefixed with "200" to ensure it's sorted early in Tradefed classpath
- // so that we provide a concrete implementation before Mainline stubs
+ "100-framework-minus-apex.ravenwood",
"200-kxml2-android",
"all-updatable-modules-system-stubs",
"android.test.mock.ravenwood",
- "framework-minus-apex.ravenwood",
- "hoststubgen-helper-framework-runtime.ravenwood",
+ "ravenwood-helper-runtime",
"hoststubgen-helper-runtime.ravenwood",
+ "services.core.ravenwood-jarjar",
// Provide runtime versions of utils linked in below
"junit",
diff --git a/WEAR_OWNERS b/WEAR_OWNERS
index da8c83ebcc98..4ffb239e24ce 100644
--- a/WEAR_OWNERS
+++ b/WEAR_OWNERS
@@ -11,3 +11,4 @@ rwmyers@google.com
nalmalki@google.com
shijianli@google.com
latkin@google.com
+djsollen@google.com
diff --git a/apct-tests/perftests/autofill/Android.bp b/apct-tests/perftests/autofill/Android.bp
index 84145beb5b5f..2ff8b4e21a83 100644
--- a/apct-tests/perftests/autofill/Android.bp
+++ b/apct-tests/perftests/autofill/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_input_framework",
// 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"
diff --git a/apct-tests/perftests/blobstore/Android.bp b/apct-tests/perftests/blobstore/Android.bp
index 2590fe3d843f..e9353fe3e854 100644
--- a/apct-tests/perftests/blobstore/Android.bp
+++ b/apct-tests/perftests/blobstore/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_input_framework",
// 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"
diff --git a/apct-tests/perftests/contentcapture/Android.bp b/apct-tests/perftests/contentcapture/Android.bp
index 638403d40ea3..5e559d703dbd 100644
--- a/apct-tests/perftests/contentcapture/Android.bp
+++ b/apct-tests/perftests/contentcapture/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_input_framework",
// 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"
diff --git a/apct-tests/perftests/core/Android.bp b/apct-tests/perftests/core/Android.bp
index e1b3241e051e..e092499cb41f 100644
--- a/apct-tests/perftests/core/Android.bp
+++ b/apct-tests/perftests/core/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_framework_accessibility",
// 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"
diff --git a/apct-tests/perftests/core/apps/overlay/Android.bp b/apct-tests/perftests/core/apps/overlay/Android.bp
index 646530788907..2170fa7f2553 100644
--- a/apct-tests/perftests/core/apps/overlay/Android.bp
+++ b/apct-tests/perftests/core/apps/overlay/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_framework_accessibility",
// 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"
@@ -25,149 +26,150 @@ android_test_helper_app {
name: "Overlay0",
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay0",
- ]
+ ],
}
android_test_helper_app {
name: "Overlay1",
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay1",
- ]
+ ],
}
android_test_helper_app {
name: "Overlay2",
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay2",
- ]
+ ],
}
android_test_helper_app {
name: "Overlay3",
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay3",
- ]
+ ],
}
android_test_helper_app {
name: "Overlay4",
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay4",
- ]
+ ],
}
android_test_helper_app {
name: "Overlay5",
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay5",
- ]
+ ],
}
android_test_helper_app {
name: "Overlay6",
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay6",
- ]
+ ],
}
android_test_helper_app {
name: "Overlay7",
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay7",
- ]
+ ],
}
+
android_test_helper_app {
name: "Overlay8",
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay8",
- ]
+ ],
}
android_test_helper_app {
name: "Overlay9",
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay9",
- ]
+ ],
}
android_test_helper_app {
name: "LargeOverlay0",
- resource_dirs : [ "res_large" ],
+ resource_dirs: ["res_large"],
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay.large0",
- ]
+ ],
}
android_test_helper_app {
name: "LargeOverlay1",
- resource_dirs : [ "res_large" ],
+ resource_dirs: ["res_large"],
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay.large1",
- ]
+ ],
}
android_test_helper_app {
name: "LargeOverlay2",
- resource_dirs : [ "res_large" ],
+ resource_dirs: ["res_large"],
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay.large2",
- ]
+ ],
}
android_test_helper_app {
name: "LargeOverlay3",
- resource_dirs : [ "res_large" ],
+ resource_dirs: ["res_large"],
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay.large3",
- ]
+ ],
}
android_test_helper_app {
name: "LargeOverlay4",
- resource_dirs : [ "res_large" ],
+ resource_dirs: ["res_large"],
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay.large4",
- ]
+ ],
}
android_test_helper_app {
name: "LargeOverlay5",
- resource_dirs : [ "res_large" ],
+ resource_dirs: ["res_large"],
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay.large5",
- ]
+ ],
}
android_test_helper_app {
name: "LargeOverlay6",
- resource_dirs : [ "res_large" ],
+ resource_dirs: ["res_large"],
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay.large6",
- ]
+ ],
}
android_test_helper_app {
name: "LargeOverlay7",
- resource_dirs : [ "res_large" ],
+ resource_dirs: ["res_large"],
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay.large7",
- ]
+ ],
}
android_test_helper_app {
name: "LargeOverlay8",
- resource_dirs : [ "res_large" ],
+ resource_dirs: ["res_large"],
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay.large8",
- ]
+ ],
}
android_test_helper_app {
name: "LargeOverlay9",
- resource_dirs : [ "res_large" ],
+ resource_dirs: ["res_large"],
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay.large9",
- ]
+ ],
}
java_library {
diff --git a/apct-tests/perftests/core/apps/reources_manager/Android.bp b/apct-tests/perftests/core/apps/reources_manager/Android.bp
index 766b8c455d23..96b9d6af5f31 100644
--- a/apct-tests/perftests/core/apps/reources_manager/Android.bp
+++ b/apct-tests/perftests/core/apps/reources_manager/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_framework_accessibility",
// 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"
diff --git a/apct-tests/perftests/core/jni/Android.bp b/apct-tests/perftests/core/jni/Android.bp
index b92b13b5b75a..a9c9526726a3 100644
--- a/apct-tests/perftests/core/jni/Android.bp
+++ b/apct-tests/perftests/core/jni/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_accessibility",
// 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"
diff --git a/apct-tests/perftests/healthconnect/Android.bp b/apct-tests/perftests/healthconnect/Android.bp
index c2d0a6f200a0..c38a24ee05d2 100644
--- a/apct-tests/perftests/healthconnect/Android.bp
+++ b/apct-tests/perftests/healthconnect/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_input_framework",
// 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"
diff --git a/apct-tests/perftests/inputmethod/Android.bp b/apct-tests/perftests/inputmethod/Android.bp
index f2f1f758112e..7fcfdea88d5d 100644
--- a/apct-tests/perftests/inputmethod/Android.bp
+++ b/apct-tests/perftests/inputmethod/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_input_method_framework",
// 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"
diff --git a/apct-tests/perftests/multiuser/Android.bp b/apct-tests/perftests/multiuser/Android.bp
index 45c6b8c5bf16..1653edc77de9 100644
--- a/apct-tests/perftests/multiuser/Android.bp
+++ b/apct-tests/perftests/multiuser/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_input_framework",
// 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"
diff --git a/apct-tests/perftests/multiuser/apps/dummyapp/Android.bp b/apct-tests/perftests/multiuser/apps/dummyapp/Android.bp
index 892c14006f87..022655d5440b 100644
--- a/apct-tests/perftests/multiuser/apps/dummyapp/Android.bp
+++ b/apct-tests/perftests/multiuser/apps/dummyapp/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_input_framework",
// 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"
diff --git a/apct-tests/perftests/packagemanager/Android.bp b/apct-tests/perftests/packagemanager/Android.bp
index b6ea54d3d53f..02fc12cde04b 100644
--- a/apct-tests/perftests/packagemanager/Android.bp
+++ b/apct-tests/perftests/packagemanager/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_input_framework",
// 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"
diff --git a/apct-tests/perftests/packagemanager/apps/query-all/Android.bp b/apct-tests/perftests/packagemanager/apps/query-all/Android.bp
index b2339d5965a0..6984936b7e56 100644
--- a/apct-tests/perftests/packagemanager/apps/query-all/Android.bp
+++ b/apct-tests/perftests/packagemanager/apps/query-all/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_input_framework",
// 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"
@@ -25,299 +26,348 @@ android_test_helper_app {
name: "QueriesAll0",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration0",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll1",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration1",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll2",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration2",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll3",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration3",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll4",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration4",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll5",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration5",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll6",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration6",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll7",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration7",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll8",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration8",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll9",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration9",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll10",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration10",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll11",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration11",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll12",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration12",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll13",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration13",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll14",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration14",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll15",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration15",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll16",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration16",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll17",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration17",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll18",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration18",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll19",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration19",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll20",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration20",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll21",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration21",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll22",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration22",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll23",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration23",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll24",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration24",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll25",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration25",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll26",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration26",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll27",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration27",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll28",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration28",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll29",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration29",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll30",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration30",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll31",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration31",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll32",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration32",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll33",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration33",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll34",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration34",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll35",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration35",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll36",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration36",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll37",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration37",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll38",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration38",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll39",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration39",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll40",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration40",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll41",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration41",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll42",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration42",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll43",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration43",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll44",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration44",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll45",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration45",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll46",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration46",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll47",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration47",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll48",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration48",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll49",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration49",
- ]
+ ],
}
diff --git a/apct-tests/perftests/permission/Android.bp b/apct-tests/perftests/permission/Android.bp
index b80a6af612ec..bc8e7696440a 100644
--- a/apct-tests/perftests/permission/Android.bp
+++ b/apct-tests/perftests/permission/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_input_framework",
// 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"
diff --git a/apct-tests/perftests/permission/apps/usepermissionapp/Android.bp b/apct-tests/perftests/permission/apps/usepermissionapp/Android.bp
index 1ad20b6fff6c..d5039726693d 100644
--- a/apct-tests/perftests/permission/apps/usepermissionapp/Android.bp
+++ b/apct-tests/perftests/permission/apps/usepermissionapp/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_input_framework",
// 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"
diff --git a/apct-tests/perftests/rubidium/Android.bp b/apct-tests/perftests/rubidium/Android.bp
index ebd228f76a1d..4f4fb11fd838 100644
--- a/apct-tests/perftests/rubidium/Android.bp
+++ b/apct-tests/perftests/rubidium/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_input_framework",
// 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"
diff --git a/apct-tests/perftests/settingsprovider/Android.bp b/apct-tests/perftests/settingsprovider/Android.bp
index 43ec0e0b4620..e4aa14cd8a77 100644
--- a/apct-tests/perftests/settingsprovider/Android.bp
+++ b/apct-tests/perftests/settingsprovider/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_input_framework",
// 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"
diff --git a/apct-tests/perftests/surfaceflinger/Android.bp b/apct-tests/perftests/surfaceflinger/Android.bp
index 21d0d44fdd2f..735c725184b1 100644
--- a/apct-tests/perftests/surfaceflinger/Android.bp
+++ b/apct-tests/perftests/surfaceflinger/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_input_framework",
// 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"
diff --git a/apct-tests/perftests/textclassifier/Android.bp b/apct-tests/perftests/textclassifier/Android.bp
index 1011267bcdc3..cf99771e5906 100644
--- a/apct-tests/perftests/textclassifier/Android.bp
+++ b/apct-tests/perftests/textclassifier/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_input_framework",
// 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"
diff --git a/apct-tests/perftests/utils/Android.bp b/apct-tests/perftests/utils/Android.bp
index 6c46a9b58f06..d2653cd148db 100644
--- a/apct-tests/perftests/utils/Android.bp
+++ b/apct-tests/perftests/utils/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_input_framework",
// 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"
diff --git a/apct-tests/perftests/windowmanager/Android.bp b/apct-tests/perftests/windowmanager/Android.bp
index 903cf8ca955a..e9357f4a9d3c 100644
--- a/apct-tests/perftests/windowmanager/Android.bp
+++ b/apct-tests/perftests/windowmanager/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_input_framework",
// 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"
diff --git a/apex/jobscheduler/service/Android.bp b/apex/jobscheduler/service/Android.bp
index 8b55e071e715..558629537253 100644
--- a/apex/jobscheduler/service/Android.bp
+++ b/apex/jobscheduler/service/Android.bp
@@ -28,6 +28,7 @@ java_library {
static_libs: [
"modules-utils-fastxmlserializer",
+ "service-jobscheduler-alarm.flags-aconfig-java",
"service-jobscheduler-job.flags-aconfig-java",
],
diff --git a/apex/jobscheduler/service/aconfig/Android.bp b/apex/jobscheduler/service/aconfig/Android.bp
index 3ba7aa201d27..4db39dc1976b 100644
--- a/apex/jobscheduler/service/aconfig/Android.bp
+++ b/apex/jobscheduler/service/aconfig/Android.bp
@@ -27,3 +27,15 @@ java_aconfig_library {
aconfig_declarations: "service-job.flags-aconfig",
visibility: ["//frameworks/base:__subpackages__"],
}
+
+// Alarm
+aconfig_declarations {
+ name: "alarm_flags",
+ package: "com.android.server.alarm",
+ srcs: ["alarm.aconfig"],
+}
+
+java_aconfig_library {
+ name: "service-jobscheduler-alarm.flags-aconfig-java",
+ aconfig_declarations: "alarm_flags",
+}
diff --git a/apex/jobscheduler/service/aconfig/alarm.aconfig b/apex/jobscheduler/service/aconfig/alarm.aconfig
new file mode 100644
index 000000000000..3b9b4e70b310
--- /dev/null
+++ b/apex/jobscheduler/service/aconfig/alarm.aconfig
@@ -0,0 +1,11 @@
+package: "com.android.server.alarm"
+
+flag {
+ name: "use_frozen_state_to_drop_listener_alarms"
+ namespace: "backstage_power"
+ description: "Use frozen state callback to drop listener alarms for cached apps"
+ bug: "324470945"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 696c3178a4f4..4832ea624bd7 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -893,8 +893,9 @@ public class DeviceIdleController extends SystemService
}
// Fall through when quick doze is not requested.
- if (!mIsOffBody) {
- // Quick doze was not requested and device is on body so turn the device active.
+ if (!mIsOffBody && !mForceIdle) {
+ // Quick doze wasn't requested, doze wasn't forced and device is on body
+ // so turn the device active.
mActiveReason = ACTIVE_REASON_ONBODY;
becomeActiveLocked("on_body", Process.myUid());
}
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 39de0af3c8d0..e728a2c55765 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.alarm;
+import static android.app.ActivityManager.UidFrozenStateChangedCallback.UID_FROZEN_STATE_FROZEN;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.app.AlarmManager.ELAPSED_REALTIME;
import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
@@ -75,6 +76,7 @@ import android.annotation.NonNull;
import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
import android.app.Activity;
+import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
import android.app.AlarmManager;
@@ -103,6 +105,7 @@ import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
@@ -145,6 +148,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.LocalLog;
@@ -293,6 +297,7 @@ public class AlarmManagerService extends SystemService {
private final Injector mInjector;
int mBroadcastRefCount = 0;
+ boolean mUseFrozenStateToDropListenerAlarms;
MetricsHelper mMetricsHelper;
PowerManager.WakeLock mWakeLock;
SparseIntArray mAlarmsPerUid = new SparseIntArray();
@@ -1856,15 +1861,47 @@ public class AlarmManagerService extends SystemService {
@Override
public void onStart() {
mInjector.init();
+ mHandler = new AlarmHandler();
+
mOptsWithFgs.setPendingIntentBackgroundActivityLaunchAllowed(false);
mOptsWithFgsForAlarmClock.setPendingIntentBackgroundActivityLaunchAllowed(false);
mOptsWithoutFgs.setPendingIntentBackgroundActivityLaunchAllowed(false);
mOptsTimeBroadcast.setPendingIntentBackgroundActivityLaunchAllowed(false);
mActivityOptsRestrictBal.setPendingIntentBackgroundActivityLaunchAllowed(false);
mBroadcastOptsRestrictBal.setPendingIntentBackgroundActivityLaunchAllowed(false);
+
mMetricsHelper = new MetricsHelper(getContext(), mLock);
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+ mUseFrozenStateToDropListenerAlarms = Flags.useFrozenStateToDropListenerAlarms();
+ if (mUseFrozenStateToDropListenerAlarms) {
+ final ActivityManager.UidFrozenStateChangedCallback callback = (uids, frozenStates) -> {
+ final int size = frozenStates.length;
+ if (uids.length != size) {
+ Slog.wtf(TAG, "Got different length arrays in frozen state callback!"
+ + " uids.length: " + uids.length + " frozenStates.length: " + size);
+ // Cannot process received data in any meaningful way.
+ return;
+ }
+ final IntArray affectedUids = new IntArray();
+ for (int i = 0; i < size; i++) {
+ if (frozenStates[i] != UID_FROZEN_STATE_FROZEN) {
+ continue;
+ }
+ if (!CompatChanges.isChangeEnabled(EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED,
+ uids[i])) {
+ continue;
+ }
+ affectedUids.add(uids[i]);
+ }
+ if (affectedUids.size() > 0) {
+ removeExactListenerAlarms(affectedUids.toArray());
+ }
+ };
+ final ActivityManager am = getContext().getSystemService(ActivityManager.class);
+ am.registerUidFrozenStateChangedCallback(new HandlerExecutor(mHandler), callback);
+ }
+
mListenerDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
@@ -1880,7 +1917,6 @@ public class AlarmManagerService extends SystemService {
};
synchronized (mLock) {
- mHandler = new AlarmHandler();
mConstants = new Constants(mHandler);
mAlarmStore = new LazyAlarmStore();
@@ -1960,6 +1996,21 @@ public class AlarmManagerService extends SystemService {
publishBinderService(Context.ALARM_SERVICE, mService);
}
+ private void removeExactListenerAlarms(int... whichUids) {
+ synchronized (mLock) {
+ removeAlarmsInternalLocked(a -> {
+ if (!ArrayUtils.contains(whichUids, a.uid) || a.listener == null
+ || a.windowLength != 0) {
+ return false;
+ }
+ Slog.w(TAG, "Alarm " + a.listenerTag + " being removed for "
+ + UserHandle.formatUid(a.uid) + ":" + a.packageName
+ + " because the app got frozen");
+ return true;
+ }, REMOVE_REASON_LISTENER_CACHED);
+ }
+ }
+
void refreshExactAlarmCandidates() {
final String[] candidates = mLocalPermissionManager.getAppOpPermissionPackages(
Manifest.permission.SCHEDULE_EXACT_ALARM);
@@ -3074,6 +3125,14 @@ public class AlarmManagerService extends SystemService {
mConstants.dump(pw);
pw.println();
+ pw.println("Feature Flags:");
+ pw.increaseIndent();
+ pw.print(Flags.FLAG_USE_FROZEN_STATE_TO_DROP_LISTENER_ALARMS,
+ mUseFrozenStateToDropListenerAlarms);
+ pw.decreaseIndent();
+ pw.println();
+ pw.println();
+
if (mConstants.USE_TARE_POLICY == EconomyManager.ENABLED_MODE_ON) {
pw.println("TARE details:");
pw.increaseIndent();
@@ -4959,18 +5018,7 @@ public class AlarmManagerService extends SystemService {
break;
case REMOVE_EXACT_LISTENER_ALARMS_ON_CACHED:
uid = (Integer) msg.obj;
- synchronized (mLock) {
- removeAlarmsInternalLocked(a -> {
- if (a.uid != uid || a.listener == null || a.windowLength != 0) {
- return false;
- }
- // TODO (b/265195908): Change to .w once we have some data on breakages.
- Slog.wtf(TAG, "Alarm " + a.listenerTag + " being removed for "
- + UserHandle.formatUid(a.uid) + ":" + a.packageName
- + " because the app went into cached state");
- return true;
- }, REMOVE_REASON_LISTENER_CACHED);
- }
+ removeExactListenerAlarms(uid);
break;
default:
// nope, just ignore it
@@ -5322,6 +5370,10 @@ public class AlarmManagerService extends SystemService {
@Override
public void handleUidCachedChanged(int uid, boolean cached) {
+ if (mUseFrozenStateToDropListenerAlarms) {
+ // Use ActivityManager#UidFrozenStateChangedCallback instead.
+ return;
+ }
if (!CompatChanges.isChangeEnabled(EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED, uid)) {
return;
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index f819f15b430f..bd00c03741f3 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -3852,7 +3852,7 @@ public class JobSchedulerService extends com.android.server.SystemService
// the other jobs that will use this network.
if (DEBUG) {
Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: piggybacking "
- + batchedJobs.size() + " jobs on " + network
+ + (batchedJobs.size() - unbatchedJobCount) + " jobs on " + network
+ " because of unbatched job");
}
jobsToRun.addAll(batchedJobs);
@@ -3892,8 +3892,12 @@ public class JobSchedulerService extends com.android.server.SystemService
// Some job is going to use the CPU anyway. Might as well run all the other
// CPU-only jobs.
if (DEBUG) {
+ final Integer unbatchedJobCountObj = mUnbatchedJobCount.get(null);
+ final int unbatchedJobCount =
+ unbatchedJobCountObj == null ? 0 : unbatchedJobCountObj;
Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: piggybacking "
- + batchedNonNetworkedJobs.size() + " non-network jobs");
+ + (batchedNonNetworkedJobs.size() - unbatchedJobCount)
+ + " non-network jobs");
}
jobsToRun.addAll(batchedNonNetworkedJobs);
} else if (batchedNonNetworkedJobs.size() >= minReadyCount) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index 6ed42ec990ea..3219f7e5ce20 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -21,6 +21,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
+import static android.net.NetworkCapabilities.TRANSPORT_SATELLITE;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
@@ -139,8 +140,9 @@ public final class ConnectivityController extends RestrictingController implemen
static final SparseIntArray sNetworkTransportAffinities = new SparseIntArray();
static {
sNetworkTransportAffinities.put(TRANSPORT_CELLULAR, TRANSPORT_AFFINITY_AVOID);
- sNetworkTransportAffinities.put(TRANSPORT_WIFI, TRANSPORT_AFFINITY_PREFER);
sNetworkTransportAffinities.put(TRANSPORT_ETHERNET, TRANSPORT_AFFINITY_PREFER);
+ sNetworkTransportAffinities.put(TRANSPORT_SATELLITE, TRANSPORT_AFFINITY_AVOID);
+ sNetworkTransportAffinities.put(TRANSPORT_WIFI, TRANSPORT_AFFINITY_PREFER);
}
private final CcConfig mCcConfig;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
index aec464d0e820..e96d07f44b34 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
@@ -55,6 +55,7 @@ import android.util.SparseArray;
import android.util.SparseArrayMap;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
+import android.util.SparseSetArray;
import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
@@ -158,19 +159,6 @@ public final class FlexibilityController extends StateController {
@GuardedBy("mLock")
private final SparseLongArray mLastSeenConstraintTimesElapsed = new SparseLongArray();
- private DeviceIdleInternal mDeviceIdleInternal;
- private final ArraySet<String> mPowerAllowlistedApps = new ArraySet<>();
-
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- switch (intent.getAction()) {
- case PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED:
- mHandler.post(FlexibilityController.this::updatePowerAllowlistCache);
- break;
- }
- }
- };
@VisibleForTesting
@GuardedBy("mLock")
final FlexibilityTracker mFlexibilityTracker;
@@ -182,6 +170,7 @@ public final class FlexibilityController extends StateController {
private final FcHandler mHandler;
@VisibleForTesting
final PrefetchController mPrefetchController;
+ private final SpecialAppTracker mSpecialAppTracker;
/**
* Stores the beginning of prefetch jobs lifecycle per app as a maximum of
@@ -355,16 +344,16 @@ public final class FlexibilityController extends StateController {
mPercentsToDropConstraints =
FcConfig.DEFAULT_PERCENTS_TO_DROP_FLEXIBLE_CONSTRAINTS;
mPrefetchController = prefetchController;
+ mSpecialAppTracker = new SpecialAppTracker();
if (mFlexibilityEnabled) {
- registerBroadcastReceiver();
+ mSpecialAppTracker.startTracking();
}
}
@Override
public void onSystemServicesReady() {
- mDeviceIdleInternal = LocalServices.getService(DeviceIdleInternal.class);
- mHandler.post(FlexibilityController.this::updatePowerAllowlistCache);
+ mSpecialAppTracker.onSystemServicesReady();
}
@Override
@@ -453,6 +442,7 @@ public final class FlexibilityController extends StateController {
final int userId = UserHandle.getUserId(uid);
mPrefetchLifeCycleStart.delete(userId, packageName);
mJobScoreTrackers.delete(uid, packageName);
+ mSpecialAppTracker.onAppRemoved(userId, packageName);
for (int i = mJobsToCheck.size() - 1; i >= 0; --i) {
final JobStatus js = mJobsToCheck.valueAt(i);
if ((js.getSourceUid() == uid && js.getSourcePackageName().equals(packageName))
@@ -466,6 +456,7 @@ public final class FlexibilityController extends StateController {
@GuardedBy("mLock")
public void onUserRemovedLocked(int userId) {
mPrefetchLifeCycleStart.delete(userId);
+ mSpecialAppTracker.onUserRemoved(userId);
for (int u = mJobScoreTrackers.numMaps() - 1; u >= 0; --u) {
final int uid = mJobScoreTrackers.keyAt(u);
if (UserHandle.getUserId(uid) == userId) {
@@ -496,9 +487,10 @@ public final class FlexibilityController extends StateController {
// Only exclude DEFAULT+ priority jobs for BFGS+ apps
|| (mService.getUidBias(js.getSourceUid()) >= JobInfo.BIAS_BOUND_FOREGROUND_SERVICE
&& js.getEffectivePriority() >= PRIORITY_DEFAULT)
- // For apps in the power allowlist, automatically exclude DEFAULT+ priority jobs.
+ // For special/privileged apps, automatically exclude DEFAULT+ priority jobs.
|| (js.getEffectivePriority() >= PRIORITY_DEFAULT
- && mPowerAllowlistedApps.contains(js.getSourcePackageName()))
+ && mSpecialAppTracker.isSpecialApp(
+ js.getSourceUserId(), js.getSourcePackageName()))
|| hasEnoughSatisfiedConstraintsLocked(js)
|| mService.isCurrentlyRunningLocked(js);
}
@@ -827,39 +819,6 @@ public final class FlexibilityController extends StateController {
mFcConfig.processConstantLocked(properties, key);
}
- private void registerBroadcastReceiver() {
- IntentFilter filter = new IntentFilter(PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED);
- mContext.registerReceiver(mBroadcastReceiver, filter);
- }
-
- private void unregisterBroadcastReceiver() {
- mContext.unregisterReceiver(mBroadcastReceiver);
- }
-
- private void updatePowerAllowlistCache() {
- if (mDeviceIdleInternal == null) {
- return;
- }
-
- // Don't call out to DeviceIdleController with the lock held.
- final String[] allowlistedPkgs = mDeviceIdleInternal.getFullPowerWhitelistExceptIdle();
- final ArraySet<String> changedPkgs = new ArraySet<>();
- synchronized (mLock) {
- changedPkgs.addAll(mPowerAllowlistedApps);
- mPowerAllowlistedApps.clear();
- for (final String pkgName : allowlistedPkgs) {
- mPowerAllowlistedApps.add(pkgName);
- if (changedPkgs.contains(pkgName)) {
- changedPkgs.remove(pkgName);
- } else {
- changedPkgs.add(pkgName);
- }
- }
- mPackagesToCheck.addAll(changedPkgs);
- mHandler.sendEmptyMessage(MSG_CHECK_PACKAGES);
- }
- }
-
@VisibleForTesting
class FlexibilityTracker {
final ArrayList<ArraySet<JobStatus>> mTrackedJobs;
@@ -1343,12 +1302,12 @@ public final class FlexibilityController extends StateController {
mFlexibilityEnabled = true;
mPrefetchController
.registerPrefetchChangedListener(mPrefetchChangedListener);
- registerBroadcastReceiver();
+ mSpecialAppTracker.startTracking();
} else {
mFlexibilityEnabled = false;
mPrefetchController
.unRegisterPrefetchChangedListener(mPrefetchChangedListener);
- unregisterBroadcastReceiver();
+ mSpecialAppTracker.stopTracking();
}
}
break;
@@ -1653,6 +1612,176 @@ public final class FlexibilityController extends StateController {
return mFcConfig;
}
+ private class SpecialAppTracker {
+ /**
+ * Lock for objects inside this class. This should never be held when attempting to acquire
+ * {@link #mLock}. It is fine to acquire this if already holding {@link #mLock}.
+ */
+ private final Object mSatLock = new Object();
+
+ private DeviceIdleInternal mDeviceIdleInternal;
+
+ /** Set of all apps that have been deemed special, keyed by user ID. */
+ private final SparseSetArray<String> mSpecialApps = new SparseSetArray<>();
+ @GuardedBy("mSatLock")
+ private final ArraySet<String> mPowerAllowlistedApps = new ArraySet<>();
+
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ switch (intent.getAction()) {
+ case PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED:
+ mHandler.post(SpecialAppTracker.this::updatePowerAllowlistCache);
+ break;
+ }
+ }
+ };
+
+ public boolean isSpecialApp(final int userId, @NonNull String packageName) {
+ synchronized (mSatLock) {
+ if (mSpecialApps.contains(UserHandle.USER_ALL, packageName)) {
+ return true;
+ }
+ if (mSpecialApps.contains(userId, packageName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isSpecialAppInternal(final int userId, @NonNull String packageName) {
+ synchronized (mSatLock) {
+ if (mPowerAllowlistedApps.contains(packageName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void onAppRemoved(final int userId, String packageName) {
+ synchronized (mSatLock) {
+ // Don't touch the USER_ALL set here. If the app is completely removed from the
+ // device, any list that affects USER_ALL should update and this would eventually
+ // be updated with those lists no longer containing the app.
+ mSpecialApps.remove(userId, packageName);
+ }
+ }
+
+ private void onSystemServicesReady() {
+ mDeviceIdleInternal = LocalServices.getService(DeviceIdleInternal.class);
+
+ synchronized (mLock) {
+ if (mFlexibilityEnabled) {
+ mHandler.post(SpecialAppTracker.this::updatePowerAllowlistCache);
+ }
+ }
+ }
+
+ private void onUserRemoved(final int userId) {
+ synchronized (mSatLock) {
+ mSpecialApps.remove(userId);
+ }
+ }
+
+ private void startTracking() {
+ IntentFilter filter = new IntentFilter(
+ PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED);
+ mContext.registerReceiver(mBroadcastReceiver, filter);
+
+ updatePowerAllowlistCache();
+ }
+
+ private void stopTracking() {
+ mContext.unregisterReceiver(mBroadcastReceiver);
+
+ synchronized (mSatLock) {
+ mPowerAllowlistedApps.clear();
+ mSpecialApps.clear();
+ }
+ }
+
+ /**
+ * Update the processed special app set for the specified user ID, only looking at the
+ * specified set of apps. This method must <b>NEVER</b> be called while holding
+ * {@link #mSatLock}.
+ */
+ private void updateSpecialAppSetUnlocked(final int userId, @NonNull ArraySet<String> pkgs) {
+ // This method may need to acquire mLock, so ensure that mSatLock isn't held to avoid
+ // lock inversion.
+ if (Thread.holdsLock(mSatLock)) {
+ throw new IllegalStateException("Must never hold local mSatLock");
+ }
+ if (pkgs.size() == 0) {
+ return;
+ }
+ final ArraySet<String> changedPkgs = new ArraySet<>();
+
+ synchronized (mSatLock) {
+ for (int i = pkgs.size() - 1; i >= 0; --i) {
+ final String pkgName = pkgs.valueAt(i);
+ if (isSpecialAppInternal(userId, pkgName)) {
+ if (mSpecialApps.add(userId, pkgName)) {
+ changedPkgs.add(pkgName);
+ }
+ } else if (mSpecialApps.remove(userId, pkgName)) {
+ changedPkgs.add(pkgName);
+ }
+ }
+ }
+
+ if (changedPkgs.size() > 0) {
+ synchronized (mLock) {
+ mPackagesToCheck.addAll(changedPkgs);
+ mHandler.sendEmptyMessage(MSG_CHECK_PACKAGES);
+ }
+ }
+ }
+
+ private void updatePowerAllowlistCache() {
+ if (mDeviceIdleInternal == null) {
+ return;
+ }
+
+ // Don't call out to DeviceIdleController with the lock held.
+ final String[] allowlistedPkgs = mDeviceIdleInternal.getFullPowerWhitelistExceptIdle();
+ final ArraySet<String> changedPkgs = new ArraySet<>();
+ synchronized (mSatLock) {
+ changedPkgs.addAll(mPowerAllowlistedApps);
+ mPowerAllowlistedApps.clear();
+ for (String pkgName : allowlistedPkgs) {
+ mPowerAllowlistedApps.add(pkgName);
+ if (!changedPkgs.remove(pkgName)) {
+ // The package wasn't in the previous set of allowlisted apps. Add it
+ // since its state has changed.
+ changedPkgs.add(pkgName);
+ }
+ }
+ }
+
+ // The full allowlist is currently user-agnostic, so use USER_ALL for these packages.
+ updateSpecialAppSetUnlocked(UserHandle.USER_ALL, changedPkgs);
+ }
+
+ public void dump(@NonNull IndentingPrintWriter pw) {
+ pw.println("Special apps:");
+ pw.increaseIndent();
+
+ synchronized (mSatLock) {
+ for (int u = 0; u < mSpecialApps.size(); ++u) {
+ pw.print(mSpecialApps.keyAt(u));
+ pw.print(": ");
+ pw.println(mSpecialApps.valuesAt(u));
+ }
+
+ pw.println();
+ pw.print("Power allowlisted packages: ");
+ pw.println(mPowerAllowlistedApps);
+ }
+
+ pw.decreaseIndent();
+ }
+ }
+
@Override
@GuardedBy("mLock")
public void dumpConstants(IndentingPrintWriter pw) {
@@ -1690,8 +1819,7 @@ public final class FlexibilityController extends StateController {
pw.decreaseIndent();
pw.println();
- pw.print("Power allowlisted packages: ");
- pw.println(mPowerAllowlistedApps);
+ mSpecialAppTracker.dump(pw);
pw.println();
mFlexibilityTracker.dump(pw, predicate, nowElapsed);
diff --git a/api/Android.bp b/api/Android.bp
index 9d2147c32760..8e063667826c 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -113,6 +113,7 @@ combined_apis {
"framework-nfc",
"framework-ondevicepersonalization",
"framework-pdf",
+ "framework-pdf-v",
"framework-permission",
"framework-permission-s",
"framework-profiling",
@@ -302,7 +303,7 @@ packages_to_document = [
// classpath (or sources) somehow.
stubs_defaults {
name: "android-non-updatable-stubs-defaults",
- defaults: ["framework-minus-apex-aconfig-declarations"],
+ aconfig_declarations: ["framework-minus-apex-aconfig-declarations"],
srcs: [":android-non-updatable-stub-sources"],
sdk_version: "none",
system_modules: "none",
diff --git a/boot/hiddenapi/hiddenapi-unsupported.txt b/boot/hiddenapi/hiddenapi-unsupported.txt
index 26dc7003a828..adcc3df2d7fe 100644
--- a/boot/hiddenapi/hiddenapi-unsupported.txt
+++ b/boot/hiddenapi/hiddenapi-unsupported.txt
@@ -133,8 +133,6 @@ Landroid/hardware/input/IInputManager$Stub;->asInterface(Landroid/os/IBinder;)La
Landroid/hardware/location/IContextHubService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/location/IContextHubService;
Landroid/hardware/usb/IUsbManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/location/ICountryListener$Stub;-><init>()V
-Landroid/location/IGeocodeProvider$Stub;-><init>()V
-Landroid/location/IGeocodeProvider$Stub;->asInterface(Landroid/os/IBinder;)Landroid/location/IGeocodeProvider;
Landroid/location/ILocationListener$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/location/ILocationListener$Stub$Proxy;->mRemote:Landroid/os/IBinder;
Landroid/location/ILocationListener$Stub;-><init>()V
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index d79131ca5d7c..3b16cab06bab 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -183,6 +183,8 @@ public class Am extends BaseCommand {
instrument.disableTestApiChecks = false;
} else if (opt.equals("--no-isolated-storage")) {
instrument.disableIsolatedStorage = true;
+ } else if (opt.equals("--no-logcat")) {
+ instrument.captureLogcat = false;
} else if (opt.equals("--user")) {
instrument.userId = parseUserArg(nextArgRequired());
} else if (opt.equals("--abi")) {
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java
index e60593e8b633..e0d949e04a92 100644
--- a/cmds/am/src/com/android/commands/am/Instrument.java
+++ b/cmds/am/src/com/android/commands/am/Instrument.java
@@ -85,6 +85,7 @@ public class Instrument {
public String profileFile = null;
public boolean wait = false;
public boolean rawMode = false;
+ public boolean captureLogcat = true;
boolean protoStd = false; // write proto to stdout
boolean protoFile = false; // write proto to a file
String logPath = null;
@@ -266,16 +267,18 @@ public class Instrument {
proto.write(InstrumentationData.TestStatus.RESULT_CODE, resultCode);
writeBundle(proto, InstrumentationData.TestStatus.RESULTS, results);
- if (resultCode == STATUS_TEST_STARTED) {
- // Logcat -T takes wall clock time (!?)
- mTestStartMs = System.currentTimeMillis();
- } else {
- if (mTestStartMs > 0) {
- proto.write(InstrumentationData.TestStatus.LOGCAT, readLogcat(mTestStartMs));
+ if (captureLogcat) {
+ if (resultCode == STATUS_TEST_STARTED) {
+ // Logcat -T takes wall clock time (!?)
+ mTestStartMs = System.currentTimeMillis();
+ } else {
+ if (mTestStartMs > 0) {
+ proto.write(InstrumentationData.TestStatus.LOGCAT,
+ readLogcat(mTestStartMs));
+ }
+ mTestStartMs = 0;
}
- mTestStartMs = 0;
}
-
proto.end(testStatusToken);
outputProto(proto);
diff --git a/core/TEST_MAPPING b/core/TEST_MAPPING
index f1e4d0ee4906..fd571c95f568 100644
--- a/core/TEST_MAPPING
+++ b/core/TEST_MAPPING
@@ -20,15 +20,5 @@
"core/tests/coretests/src/com/android/internal/inputmethod/.*"
]
}
- ],
- "postsubmit": [
- {
- "name": "CtsContactKeysManagerTestCases",
- "options": [
- {
- "include-filter": "android.provider.cts.contactkeys."
- }
- ]
- }
- ]
+ ]
}
diff --git a/core/api/current.txt b/core/api/current.txt
index 391867d8e7a5..386396c67c71 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -144,10 +144,12 @@ package android {
field public static final String MANAGE_DEVICE_POLICY_AUDIO_OUTPUT = "android.permission.MANAGE_DEVICE_POLICY_AUDIO_OUTPUT";
field public static final String MANAGE_DEVICE_POLICY_AUTOFILL = "android.permission.MANAGE_DEVICE_POLICY_AUTOFILL";
field public static final String MANAGE_DEVICE_POLICY_BACKUP_SERVICE = "android.permission.MANAGE_DEVICE_POLICY_BACKUP_SERVICE";
+ field @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled") public static final String MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL = "android.permission.MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL";
field public static final String MANAGE_DEVICE_POLICY_BLUETOOTH = "android.permission.MANAGE_DEVICE_POLICY_BLUETOOTH";
field public static final String MANAGE_DEVICE_POLICY_BUGREPORT = "android.permission.MANAGE_DEVICE_POLICY_BUGREPORT";
field public static final String MANAGE_DEVICE_POLICY_CALLS = "android.permission.MANAGE_DEVICE_POLICY_CALLS";
field public static final String MANAGE_DEVICE_POLICY_CAMERA = "android.permission.MANAGE_DEVICE_POLICY_CAMERA";
+ field @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled") public static final String MANAGE_DEVICE_POLICY_CAMERA_TOGGLE = "android.permission.MANAGE_DEVICE_POLICY_CAMERA_TOGGLE";
field public static final String MANAGE_DEVICE_POLICY_CERTIFICATES = "android.permission.MANAGE_DEVICE_POLICY_CERTIFICATES";
field public static final String MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE = "android.permission.MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE";
field @FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled") public static final String MANAGE_DEVICE_POLICY_CONTENT_PROTECTION = "android.permission.MANAGE_DEVICE_POLICY_CONTENT_PROTECTION";
@@ -169,6 +171,7 @@ package android {
field @FlaggedApi("android.app.admin.flags.esim_management_enabled") public static final String MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS = "android.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS";
field public static final String MANAGE_DEVICE_POLICY_METERED_DATA = "android.permission.MANAGE_DEVICE_POLICY_METERED_DATA";
field public static final String MANAGE_DEVICE_POLICY_MICROPHONE = "android.permission.MANAGE_DEVICE_POLICY_MICROPHONE";
+ field @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled") public static final String MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE = "android.permission.MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE";
field public static final String MANAGE_DEVICE_POLICY_MOBILE_NETWORK = "android.permission.MANAGE_DEVICE_POLICY_MOBILE_NETWORK";
field public static final String MANAGE_DEVICE_POLICY_MODIFY_USERS = "android.permission.MANAGE_DEVICE_POLICY_MODIFY_USERS";
field public static final String MANAGE_DEVICE_POLICY_MTE = "android.permission.MANAGE_DEVICE_POLICY_MTE";
@@ -454,6 +457,7 @@ package android {
field public static final int allowBackup = 16843392; // 0x1010280
field public static final int allowClearUserData = 16842757; // 0x1010005
field public static final int allowClickWhenDisabled = 16844312; // 0x1010618
+ field @FlaggedApi("android.security.asm_restrictions_enabled") public static final int allowCrossUidActivitySwitchFromBelow;
field public static final int allowEmbedded = 16843765; // 0x10103f5
field public static final int allowGameAngleDriver = 16844376; // 0x1010658
field public static final int allowGameDownscaling = 16844377; // 0x1010659
@@ -687,6 +691,7 @@ package android {
field public static final int defaultHeight = 16844021; // 0x10104f5
field @FlaggedApi("android.content.res.default_locale") public static final int defaultLocale;
field public static final int defaultToDeviceProtectedStorage = 16844036; // 0x1010504
+ field @FlaggedApi("android.nfc.Flags.FLAG_OBSERVE_MODE") public static final int defaultToObserveMode;
field public static final int defaultValue = 16843245; // 0x10101ed
field public static final int defaultWidth = 16844020; // 0x10104f4
field public static final int delay = 16843212; // 0x10101cc
@@ -1491,6 +1496,7 @@ package android {
field @Deprecated public static final int sharedUserLabel = 16843361; // 0x1010261
field public static final int sharedUserMaxSdkVersion = 16844365; // 0x101064d
field public static final int shell = 16844180; // 0x1010594
+ field @FlaggedApi("com.android.text.flags.use_bounds_for_width") public static final int shiftDrawingOffsetForStartOverhang;
field public static final int shortcutDisabledMessage = 16844075; // 0x101052b
field public static final int shortcutId = 16844072; // 0x1010528
field public static final int shortcutLongLabel = 16844074; // 0x101052a
@@ -1887,6 +1893,7 @@ package android {
field public static final int windowFullscreen = 16843277; // 0x101020d
field public static final int windowHideAnimation = 16842935; // 0x10100b7
field public static final int windowIsFloating = 16842839; // 0x1010057
+ field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final int windowIsFrameRatePowerSavingsBalanced;
field public static final int windowIsTranslucent = 16842840; // 0x1010058
field public static final int windowLayoutAffinity = 16844313; // 0x1010619
field public static final int windowLayoutInDisplayCutoutMode = 16844166; // 0x1010586
@@ -2160,11 +2167,6 @@ package android {
field public static final int notification_large_icon_width = 17104901; // 0x1050005
field public static final int system_app_widget_background_radius = 17104904; // 0x1050008
field public static final int system_app_widget_inner_radius = 17104905; // 0x1050009
- field public static final int system_corner_radius_large;
- field public static final int system_corner_radius_medium;
- field public static final int system_corner_radius_small;
- field public static final int system_corner_radius_xlarge;
- field public static final int system_corner_radius_xsmall;
field public static final int thumbnail_height = 17104897; // 0x1050001
field public static final int thumbnail_width = 17104898; // 0x1050002
}
@@ -4423,12 +4425,14 @@ package android.app {
method @Deprecated public void finishFromChild(android.app.Activity);
method @Nullable public android.app.ActionBar getActionBar();
method public final android.app.Application getApplication();
+ method @FlaggedApi("android.security.content_uri_permission_apis") @Nullable public android.app.ComponentCaller getCaller();
method @Nullable public android.content.ComponentName getCallingActivity();
method @Nullable public String getCallingPackage();
method public int getChangingConfigurations();
method public android.content.ComponentName getComponentName();
method public android.transition.Scene getContentScene();
method public android.transition.TransitionManager getContentTransitionManager();
+ method @FlaggedApi("android.security.content_uri_permission_apis") @NonNull public android.app.ComponentCaller getCurrentCaller();
method @Nullable public android.view.View getCurrentFocus();
method @Deprecated public android.app.FragmentManager getFragmentManager();
method @FlaggedApi("android.security.content_uri_permission_apis") @NonNull public android.app.ComponentCaller getInitialCaller();
@@ -4480,6 +4484,7 @@ package android.app {
method @CallSuper public void onActionModeStarted(android.view.ActionMode);
method public void onActivityReenter(int, android.content.Intent);
method protected void onActivityResult(int, int, android.content.Intent);
+ method @FlaggedApi("android.security.content_uri_permission_apis") public void onActivityResult(int, int, @NonNull android.content.Intent, @NonNull android.app.ComponentCaller);
method @Deprecated public void onAttachFragment(android.app.Fragment);
method public void onAttachedToWindow();
method @Deprecated public void onBackPressed();
@@ -4521,6 +4526,7 @@ package android.app {
method public boolean onNavigateUp();
method @Deprecated public boolean onNavigateUpFromChild(android.app.Activity);
method protected void onNewIntent(android.content.Intent);
+ method @FlaggedApi("android.security.content_uri_permission_apis") public void onNewIntent(@NonNull android.content.Intent, @NonNull android.app.ComponentCaller);
method public boolean onOptionsItemSelected(@NonNull android.view.MenuItem);
method public void onOptionsMenuClosed(android.view.Menu);
method public void onPanelClosed(int, @NonNull android.view.Menu);
@@ -4608,6 +4614,7 @@ package android.app {
method public void setImmersive(boolean);
method public void setInheritShowWhenLocked(boolean);
method public void setIntent(android.content.Intent);
+ method @FlaggedApi("android.security.content_uri_permission_apis") public void setIntent(@Nullable android.content.Intent, @Nullable android.app.ComponentCaller);
method public void setLocusContext(@Nullable android.content.LocusId, @Nullable android.os.Bundle);
method public final void setMediaController(android.media.session.MediaController);
method public void setPictureInPictureParams(@NonNull android.app.PictureInPictureParams);
@@ -5329,6 +5336,7 @@ package android.app {
method public int getStartType();
method public int getStartupState();
method @NonNull public java.util.Map<java.lang.Integer,java.lang.Long> getStartupTimestamps();
+ method @FlaggedApi("android.content.pm.stay_stopped") public boolean wasForceStopped();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.ApplicationStartInfo> CREATOR;
field public static final int LAUNCH_MODE_SINGLE_INSTANCE = 2; // 0x2
@@ -5458,11 +5466,15 @@ package android.app {
method public int getDeferralPolicy();
method @Nullable public String getDeliveryGroupMatchingKey();
method public int getDeliveryGroupPolicy();
+ method @FlaggedApi("android.app.bcast_event_timestamps") public long getEventTriggerTimestampMillis();
+ method @FlaggedApi("android.app.bcast_event_timestamps") public long getRemoteEventTriggerTimestampMillis();
method public boolean isShareIdentityEnabled();
method @NonNull public static android.app.BroadcastOptions makeBasic();
method @NonNull public android.app.BroadcastOptions setDeferralPolicy(int);
method @NonNull public android.app.BroadcastOptions setDeliveryGroupMatchingKey(@NonNull String, @NonNull String);
method @NonNull public android.app.BroadcastOptions setDeliveryGroupPolicy(int);
+ method @FlaggedApi("android.app.bcast_event_timestamps") public void setEventTriggerTimestampMillis(long);
+ method @FlaggedApi("android.app.bcast_event_timestamps") public void setRemoteEventTriggerTimestampMillis(long);
method @NonNull public android.app.BroadcastOptions setShareIdentityEnabled(boolean);
method @NonNull public android.os.Bundle toBundle();
field public static final int DEFERRAL_POLICY_DEFAULT = 0; // 0x0
@@ -6090,6 +6102,7 @@ package android.app {
method public void callActivityOnCreate(android.app.Activity, android.os.Bundle, android.os.PersistableBundle);
method public void callActivityOnDestroy(android.app.Activity);
method public void callActivityOnNewIntent(android.app.Activity, android.content.Intent);
+ method @FlaggedApi("android.security.content_uri_permission_apis") public void callActivityOnNewIntent(@NonNull android.app.Activity, @NonNull android.content.Intent, @NonNull android.app.ComponentCaller);
method public void callActivityOnPause(android.app.Activity);
method public void callActivityOnPictureInPictureRequested(@NonNull android.app.Activity);
method public void callActivityOnPostCreate(@NonNull android.app.Activity, @Nullable android.os.Bundle);
@@ -7011,6 +7024,7 @@ package android.app {
method public void deleteNotificationChannelGroup(String);
method public android.service.notification.StatusBarNotification[] getActiveNotifications();
method public android.app.AutomaticZenRule getAutomaticZenRule(String);
+ method @FlaggedApi("android.app.modes_api") public int getAutomaticZenRuleState(@NonNull String);
method public java.util.Map<java.lang.String,android.app.AutomaticZenRule> getAutomaticZenRules();
method public int getBubblePreference();
method @NonNull public android.app.NotificationManager.Policy getConsolidatedNotificationPolicy();
@@ -7225,8 +7239,8 @@ package android.app {
public final class PictureInPictureUiState implements android.os.Parcelable {
method public int describeContents();
- method @FlaggedApi("android.app.enable_pip_ui_state_callback_on_entering") public boolean isEnteringPip();
method public boolean isStashed();
+ method @FlaggedApi("android.app.enable_pip_ui_state_callback_on_entering") public boolean isTransitioningToPip();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.PictureInPictureUiState> CREATOR;
}
@@ -7955,6 +7969,7 @@ package android.app.admin {
field public static final String LOCK_TASK_POLICY = "lockTask";
field public static final String PACKAGES_SUSPENDED_POLICY = "packagesSuspended";
field public static final String PACKAGE_UNINSTALL_BLOCKED_POLICY = "packageUninstallBlocked";
+ field @FlaggedApi("android.app.admin.flags.policy_engine_migration_v2_enabled") public static final String PASSWORD_COMPLEXITY_POLICY = "passwordComplexity";
field public static final String PERMISSION_GRANT_POLICY = "permissionGrant";
field public static final String PERSISTENT_PREFERRED_ACTIVITY_POLICY = "persistentPreferredActivity";
field public static final String RESET_PASSWORD_TOKEN_POLICY = "resetPasswordToken";
@@ -8015,7 +8030,7 @@ package android.app.admin {
method public CharSequence getDeviceOwnerLockScreenInfo();
method @Nullable public String getDevicePolicyManagementRoleHolderPackage();
method public CharSequence getEndUserSessionMessage(@NonNull android.content.ComponentName);
- method @NonNull public String getEnrollmentSpecificId();
+ method @FlaggedApi("android.app.admin.flags.permission_migration_for_zero_trust_api_enabled") @NonNull @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_CERTIFICATES, conditional=true) public String getEnrollmentSpecificId();
method @Nullable @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_FACTORY_RESET, conditional=true) public android.app.admin.FactoryResetProtectionPolicy getFactoryResetProtectionPolicy(@Nullable android.content.ComponentName);
method @Nullable public String getGlobalPrivateDnsHost(@NonNull android.content.ComponentName);
method public int getGlobalPrivateDnsMode(@NonNull android.content.ComponentName);
@@ -8054,7 +8069,7 @@ package android.app.admin {
method @Deprecated public int getPasswordMinimumSymbols(@Nullable android.content.ComponentName);
method @Deprecated public int getPasswordMinimumUpperCase(@Nullable android.content.ComponentName);
method @Deprecated public int getPasswordQuality(@Nullable android.content.ComponentName);
- method @Nullable public android.app.admin.SystemUpdateInfo getPendingSystemUpdate(@NonNull android.content.ComponentName);
+ method @FlaggedApi("android.app.admin.flags.permission_migration_for_zero_trust_api_enabled") @Nullable @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES, conditional=true) public android.app.admin.SystemUpdateInfo getPendingSystemUpdate(@Nullable android.content.ComponentName);
method @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS, conditional=true) public int getPermissionGrantState(@Nullable android.content.ComponentName, @NonNull String, @NonNull String);
method public int getPermissionPolicy(android.content.ComponentName);
method @Nullable public java.util.List<java.lang.String> getPermittedAccessibilityServices(@NonNull android.content.ComponentName);
@@ -8111,6 +8126,7 @@ package android.app.admin {
method public boolean isLogoutEnabled();
method public boolean isManagedProfile(@NonNull android.content.ComponentName);
method public boolean isMasterVolumeMuted(@NonNull android.content.ComponentName);
+ method @FlaggedApi("android.app.admin.flags.is_mte_policy_enforced") public static boolean isMtePolicyEnforced();
method public boolean isNetworkLoggingEnabled(@Nullable android.content.ComponentName);
method public boolean isOrganizationOwnedDeviceWithManagedProfile();
method public boolean isOverrideApnEnabled(@NonNull android.content.ComponentName);
@@ -10073,7 +10089,7 @@ package android.content {
method public CharSequence coerceToText(android.content.Context);
method public String getHtmlText();
method public android.content.Intent getIntent();
- method @FlaggedApi("com.android.window.flags.delegate_unhandled_drags") @Nullable public android.app.PendingIntent getPendingIntent();
+ method @FlaggedApi("com.android.window.flags.delegate_unhandled_drags") @Nullable public android.content.IntentSender getIntentSender();
method public CharSequence getText();
method @Nullable public android.view.textclassifier.TextLinks getTextLinks();
method public android.net.Uri getUri();
@@ -10084,7 +10100,7 @@ package android.content {
method @FlaggedApi("com.android.window.flags.delegate_unhandled_drags") @NonNull public android.content.ClipData.Item build();
method @FlaggedApi("com.android.window.flags.delegate_unhandled_drags") @NonNull public android.content.ClipData.Item.Builder setHtmlText(@Nullable String);
method @FlaggedApi("com.android.window.flags.delegate_unhandled_drags") @NonNull public android.content.ClipData.Item.Builder setIntent(@Nullable android.content.Intent);
- method @FlaggedApi("com.android.window.flags.delegate_unhandled_drags") @NonNull public android.content.ClipData.Item.Builder setPendingIntent(@Nullable android.app.PendingIntent);
+ method @FlaggedApi("com.android.window.flags.delegate_unhandled_drags") @NonNull public android.content.ClipData.Item.Builder setIntentSender(@Nullable android.content.IntentSender);
method @FlaggedApi("com.android.window.flags.delegate_unhandled_drags") @NonNull public android.content.ClipData.Item.Builder setText(@Nullable CharSequence);
method @FlaggedApi("com.android.window.flags.delegate_unhandled_drags") @NonNull public android.content.ClipData.Item.Builder setUri(@Nullable android.net.Uri);
}
@@ -11060,6 +11076,7 @@ package android.content {
method public boolean hasCategory(String);
method public boolean hasExtra(String);
method public boolean hasFileDescriptors();
+ method @FlaggedApi("android.security.enforce_intent_filter_match") public boolean isMismatchingFilter();
method public static android.content.Intent makeMainActivity(android.content.ComponentName);
method public static android.content.Intent makeMainSelectorActivity(String, String);
method public static android.content.Intent makeRestartActivityTask(android.content.ComponentName);
@@ -12383,6 +12400,8 @@ package android.content.pm {
method @NonNull public android.graphics.drawable.Drawable getProfileSwitchingIconDrawable(@NonNull android.os.UserHandle);
method @NonNull public CharSequence getProfileSwitchingLabel(@NonNull android.os.UserHandle);
method @NonNull public java.util.List<android.os.UserHandle> getTargetUserProfiles();
+ method @FlaggedApi("android.app.admin.flags.allow_querying_profile_type") public boolean isManagedProfile(@NonNull android.os.UserHandle);
+ method @FlaggedApi("android.app.admin.flags.allow_querying_profile_type") public boolean isProfile(@NonNull android.os.UserHandle);
method @RequiresPermission(anyOf={android.Manifest.permission.INTERACT_ACROSS_PROFILES, "android.permission.INTERACT_ACROSS_USERS"}) public void startActivity(@NonNull android.content.Intent, @NonNull android.os.UserHandle, @Nullable android.app.Activity);
method @RequiresPermission(anyOf={android.Manifest.permission.INTERACT_ACROSS_PROFILES, "android.permission.INTERACT_ACROSS_USERS"}) public void startActivity(@NonNull android.content.Intent, @NonNull android.os.UserHandle, @Nullable android.app.Activity, @Nullable android.os.Bundle);
method public void startMainActivity(@NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
@@ -12473,8 +12492,11 @@ package android.content.pm {
public class LauncherApps {
method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(String, android.os.UserHandle);
method @NonNull public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getAllPackageInstallerSessions();
+ method @FlaggedApi("android.os.allow_private_profile") @Nullable @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public android.content.IntentSender getAppMarketActivityIntent(@Nullable String, @NonNull android.os.UserHandle);
method public android.content.pm.ApplicationInfo getApplicationInfo(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @FlaggedApi("android.os.allow_private_profile") @Nullable @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public final android.content.pm.LauncherUserInfo getLauncherUserInfo(@NonNull android.os.UserHandle);
method public android.content.pm.LauncherApps.PinItemRequest getPinItemRequest(android.content.Intent);
+ method @FlaggedApi("android.os.allow_private_profile") @NonNull @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public java.util.List<java.lang.String> getPreInstalledSystemPackages(@NonNull android.os.UserHandle);
method public java.util.List<android.os.UserHandle> getProfiles();
method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(android.content.pm.ShortcutInfo, int);
method @Nullable public android.content.IntentSender getShortcutConfigActivityIntent(@NonNull android.content.pm.LauncherActivityInfo);
@@ -12556,6 +12578,14 @@ package android.content.pm {
field public static final int FLAG_MATCH_PINNED_BY_ANY_LAUNCHER = 1024; // 0x400
}
+ @FlaggedApi("android.os.allow_private_profile") public final class LauncherUserInfo implements android.os.Parcelable {
+ method @FlaggedApi("android.os.allow_private_profile") public int describeContents();
+ method @FlaggedApi("android.os.allow_private_profile") public int getUserSerialNumber();
+ method @FlaggedApi("android.os.allow_private_profile") @NonNull public String getUserType();
+ method @FlaggedApi("android.os.allow_private_profile") public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @FlaggedApi("android.os.allow_private_profile") @NonNull public static final android.os.Parcelable.Creator<android.content.pm.LauncherUserInfo> CREATOR;
+ }
+
public final class ModuleInfo implements android.os.Parcelable {
method public int describeContents();
method @Nullable public CharSequence getName();
@@ -13188,7 +13218,7 @@ package android.content.pm {
field public static final String FEATURE_TELEPHONY_RADIO_ACCESS = "android.hardware.telephony.radio.access";
field public static final String FEATURE_TELEPHONY_SUBSCRIPTION = "android.hardware.telephony.subscription";
field @Deprecated public static final String FEATURE_TELEVISION = "android.hardware.type.television";
- field @FlaggedApi("com.android.net.thread.flags.thread_enabled") public static final String FEATURE_THREAD_NETWORK = "android.hardware.thread_network";
+ field @FlaggedApi("com.android.net.thread.flags.thread_enabled_platform") public static final String FEATURE_THREAD_NETWORK = "android.hardware.thread_network";
field public static final String FEATURE_TOUCHSCREEN = "android.hardware.touchscreen";
field public static final String FEATURE_TOUCHSCREEN_MULTITOUCH = "android.hardware.touchscreen.multitouch";
field public static final String FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT = "android.hardware.touchscreen.multitouch.distinct";
@@ -15669,6 +15699,7 @@ package android.graphics {
method public boolean clipOutRect(@NonNull android.graphics.Rect);
method public boolean clipOutRect(float, float, float, float);
method public boolean clipOutRect(int, int, int, int);
+ method @FlaggedApi("com.android.graphics.hwui.flags.clip_shader") public void clipOutShader(@NonNull android.graphics.Shader);
method @Deprecated public boolean clipPath(@NonNull android.graphics.Path, @NonNull android.graphics.Region.Op);
method public boolean clipPath(@NonNull android.graphics.Path);
method @Deprecated public boolean clipRect(@NonNull android.graphics.RectF, @NonNull android.graphics.Region.Op);
@@ -15678,6 +15709,7 @@ package android.graphics {
method @Deprecated public boolean clipRect(float, float, float, float, @NonNull android.graphics.Region.Op);
method public boolean clipRect(float, float, float, float);
method public boolean clipRect(int, int, int, int);
+ method @FlaggedApi("com.android.graphics.hwui.flags.clip_shader") public void clipShader(@NonNull android.graphics.Shader);
method public void concat(@Nullable android.graphics.Matrix);
method @FlaggedApi("com.android.graphics.hwui.flags.matrix_44") public void concat44(@Nullable android.graphics.Matrix44);
method public void disableZ();
@@ -18563,6 +18595,7 @@ package android.hardware {
@FlaggedApi("android.hardware.flags.overlayproperties_class_api") public final class OverlayProperties implements android.os.Parcelable {
method @FlaggedApi("android.hardware.flags.overlayproperties_class_api") public int describeContents();
+ method @FlaggedApi("android.hardware.flags.overlayproperties_class_api") public boolean isCombinationSupported(int, int);
method @FlaggedApi("android.hardware.flags.overlayproperties_class_api") public boolean isMixedColorSpacesSupported();
method @FlaggedApi("android.hardware.flags.overlayproperties_class_api") public void writeToParcel(@NonNull android.os.Parcel, int);
field @FlaggedApi("android.hardware.flags.overlayproperties_class_api") @NonNull public static final android.os.Parcelable.Creator<android.hardware.OverlayProperties> CREATOR;
@@ -19274,6 +19307,7 @@ package android.hardware.camera2 {
method @NonNull public java.util.List<java.lang.Integer> getSupportedExtensions();
method public boolean isCaptureProcessProgressAvailable(int);
method public boolean isPostviewAvailable(int);
+ field @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Float>> EFV_PADDING_ZOOM_FACTOR_RANGE;
field public static final int EXTENSION_AUTOMATIC = 0; // 0x0
field @Deprecated public static final int EXTENSION_BEAUTY = 1; // 0x1
field public static final int EXTENSION_BOKEH = 2; // 0x2
@@ -19295,6 +19329,7 @@ package android.hardware.camera2 {
public abstract static class CameraExtensionSession.ExtensionCaptureCallback {
ctor public CameraExtensionSession.ExtensionCaptureCallback();
method public void onCaptureFailed(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest);
+ method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureFailed(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest, int);
method public void onCaptureProcessProgressed(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest, @IntRange(from=0, to=100) int);
method public void onCaptureProcessStarted(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest);
method public void onCaptureResultAvailable(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest, @NonNull android.hardware.camera2.TotalCaptureResult);
@@ -19875,6 +19910,32 @@ package android.hardware.camera2 {
field public static final int MAX_THUMBNAIL_DIMENSION = 256; // 0x100
}
+ @FlaggedApi("com.android.internal.camera.flags.concert_mode") public final class ExtensionCaptureRequest {
+ ctor public ExtensionCaptureRequest();
+ field @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Boolean> EFV_AUTO_ZOOM;
+ field @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> EFV_MAX_PADDING_ZOOM_FACTOR;
+ field @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> EFV_PADDING_ZOOM_FACTOR;
+ field @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> EFV_ROTATE_VIEWPORT;
+ field @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> EFV_STABILIZATION_MODE;
+ field @FlaggedApi("com.android.internal.camera.flags.concert_mode") public static final int EFV_STABILIZATION_MODE_GIMBAL = 1; // 0x1
+ field @FlaggedApi("com.android.internal.camera.flags.concert_mode") public static final int EFV_STABILIZATION_MODE_LOCKED = 2; // 0x2
+ field @FlaggedApi("com.android.internal.camera.flags.concert_mode") public static final int EFV_STABILIZATION_MODE_OFF = 0; // 0x0
+ field @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<android.util.Pair<java.lang.Integer,java.lang.Integer>> EFV_TRANSLATE_VIEWPORT;
+ }
+
+ @FlaggedApi("com.android.internal.camera.flags.concert_mode") public final class ExtensionCaptureResult {
+ ctor public ExtensionCaptureResult();
+ field @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Boolean> EFV_AUTO_ZOOM;
+ field @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public static final android.hardware.camera2.CaptureResult.Key<int[]> EFV_AUTO_ZOOM_PADDING_REGION;
+ field @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> EFV_MAX_PADDING_ZOOM_FACTOR;
+ field @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public static final android.hardware.camera2.CaptureResult.Key<int[]> EFV_PADDING_REGION;
+ field @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> EFV_PADDING_ZOOM_FACTOR;
+ field @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> EFV_ROTATE_VIEWPORT;
+ field @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> EFV_STABILIZATION_MODE;
+ field @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public static final android.hardware.camera2.CaptureResult.Key<android.graphics.PointF[]> EFV_TARGET_COORDINATES;
+ field @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public static final android.hardware.camera2.CaptureResult.Key<android.util.Pair<java.lang.Integer,java.lang.Integer>> EFV_TRANSLATE_VIEWPORT;
+ }
+
public class MultiResolutionImageReader implements java.lang.AutoCloseable {
ctor public MultiResolutionImageReader(@NonNull java.util.Collection<android.hardware.camera2.params.MultiResolutionStreamInfo>, int, @IntRange(from=1) int);
method public void close();
@@ -19960,11 +20021,14 @@ package android.hardware.camera2.params {
public final class ExtensionSessionConfiguration {
ctor public ExtensionSessionConfiguration(int, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraExtensionSession.StateCallback);
+ method @FlaggedApi("com.android.internal.camera.flags.extension_10_bit") public void clearColorSpace();
+ method @FlaggedApi("com.android.internal.camera.flags.extension_10_bit") @Nullable public android.graphics.ColorSpace getColorSpace();
method @NonNull public java.util.concurrent.Executor getExecutor();
method public int getExtension();
method @NonNull public java.util.List<android.hardware.camera2.params.OutputConfiguration> getOutputConfigurations();
method @Nullable public android.hardware.camera2.params.OutputConfiguration getPostviewOutputConfiguration();
method @NonNull public android.hardware.camera2.CameraExtensionSession.StateCallback getStateCallback();
+ method @FlaggedApi("com.android.internal.camera.flags.extension_10_bit") public void setColorSpace(@NonNull android.graphics.ColorSpace.Named);
method public void setPostviewOutputConfiguration(@Nullable android.hardware.camera2.params.OutputConfiguration);
}
@@ -22497,6 +22561,7 @@ package android.media {
method @NonNull public static android.view.Surface createPersistentInputSurface();
method public int dequeueInputBuffer(long);
method public int dequeueOutputBuffer(@NonNull android.media.MediaCodec.BufferInfo, long);
+ method @FlaggedApi("android.media.codec.null_output_surface") public void detachOutputSurface();
method protected void finalize();
method public void flush();
method @NonNull public String getCanonicalName();
@@ -22520,6 +22585,7 @@ package android.media {
method public void queueInputBuffer(int, int, int, long, int) throws android.media.MediaCodec.CryptoException;
method @FlaggedApi("com.android.media.codec.flags.large_audio_frame") public void queueInputBuffers(int, @NonNull java.util.ArrayDeque<android.media.MediaCodec.BufferInfo>);
method public void queueSecureInputBuffer(int, int, @NonNull android.media.MediaCodec.CryptoInfo, long, int) throws android.media.MediaCodec.CryptoException;
+ method @FlaggedApi("com.android.media.codec.flags.large_audio_frame") public void queueSecureInputBuffers(int, @NonNull java.util.ArrayDeque<android.media.MediaCodec.BufferInfo>, @NonNull java.util.ArrayDeque<android.media.MediaCodec.CryptoInfo>);
method public void release();
method public void releaseOutputBuffer(int, boolean);
method public void releaseOutputBuffer(int, long);
@@ -22544,6 +22610,7 @@ package android.media {
field public static final int BUFFER_FLAG_KEY_FRAME = 1; // 0x1
field public static final int BUFFER_FLAG_PARTIAL_FRAME = 8; // 0x8
field @Deprecated public static final int BUFFER_FLAG_SYNC_FRAME = 1; // 0x1
+ field @FlaggedApi("android.media.codec.null_output_surface") public static final int CONFIGURE_FLAG_DETACHED_SURFACE = 8; // 0x8
field public static final int CONFIGURE_FLAG_ENCODE = 1; // 0x1
field public static final int CONFIGURE_FLAG_USE_BLOCK_MODEL = 2; // 0x2
field public static final int CONFIGURE_FLAG_USE_CRYPTO_ASYNC = 4; // 0x4
@@ -22686,7 +22753,6 @@ package android.media {
public final class MediaCodec.QueueRequest {
method public void queue();
- method @FlaggedApi("com.android.media.codec.flags.large_audio_frame") @NonNull public android.media.MediaCodec.QueueRequest setBufferInfos(@NonNull java.util.ArrayDeque<android.media.MediaCodec.BufferInfo>);
method @NonNull public android.media.MediaCodec.QueueRequest setByteBufferParameter(@NonNull String, @NonNull java.nio.ByteBuffer);
method @NonNull public android.media.MediaCodec.QueueRequest setEncryptedLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, int, @NonNull android.media.MediaCodec.CryptoInfo);
method @NonNull public android.media.MediaCodec.QueueRequest setFlags(int);
@@ -22695,6 +22761,8 @@ package android.media {
method @NonNull public android.media.MediaCodec.QueueRequest setIntegerParameter(@NonNull String, int);
method @NonNull public android.media.MediaCodec.QueueRequest setLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, int);
method @NonNull public android.media.MediaCodec.QueueRequest setLongParameter(@NonNull String, long);
+ method @FlaggedApi("com.android.media.codec.flags.large_audio_frame") @NonNull public android.media.MediaCodec.QueueRequest setMultiFrameEncryptedLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, @NonNull java.util.ArrayDeque<android.media.MediaCodec.BufferInfo>, @NonNull java.util.ArrayDeque<android.media.MediaCodec.CryptoInfo>);
+ method @FlaggedApi("com.android.media.codec.flags.large_audio_frame") @NonNull public android.media.MediaCodec.QueueRequest setMultiFrameLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, @NonNull java.util.ArrayDeque<android.media.MediaCodec.BufferInfo>);
method @NonNull public android.media.MediaCodec.QueueRequest setPresentationTimeUs(long);
method @NonNull public android.media.MediaCodec.QueueRequest setStringParameter(@NonNull String, @NonNull String);
}
@@ -22703,12 +22771,16 @@ package android.media {
method @NonNull public String getCanonicalName();
method public android.media.MediaCodecInfo.CodecCapabilities getCapabilitiesForType(String);
method @NonNull public String getName();
+ method @FlaggedApi("android.media.codec.in_process_sw_audio_codec") public int getSecurityModel();
method public String[] getSupportedTypes();
method public boolean isAlias();
method public boolean isEncoder();
method public boolean isHardwareAccelerated();
method public boolean isSoftwareOnly();
method public boolean isVendor();
+ field @FlaggedApi("android.media.codec.in_process_sw_audio_codec") public static final int SECURITY_MODEL_MEMORY_SAFE = 1; // 0x1
+ field @FlaggedApi("android.media.codec.in_process_sw_audio_codec") public static final int SECURITY_MODEL_SANDBOXED = 0; // 0x0
+ field @FlaggedApi("android.media.codec.in_process_sw_audio_codec") public static final int SECURITY_MODEL_TRUSTED_CONTENT_ONLY = 2; // 0x2
}
public static final class MediaCodecInfo.AudioCapabilities {
@@ -22789,6 +22861,7 @@ package android.media {
field @Deprecated public static final int COLOR_QCOM_FormatYUV420SemiPlanar = 2141391872; // 0x7fa30c00
field @Deprecated public static final int COLOR_TI_FormatYUV420PackedSemiPlanar = 2130706688; // 0x7f000100
field public static final String FEATURE_AdaptivePlayback = "adaptive-playback";
+ field @FlaggedApi("android.media.codec.null_output_surface") public static final String FEATURE_DetachedSurface = "detached-surface";
field @FlaggedApi("android.media.codec.dynamic_color_aspects") public static final String FEATURE_DynamicColorAspects = "dynamic-color-aspects";
field public static final String FEATURE_DynamicTimestamp = "dynamic-timestamp";
field public static final String FEATURE_EncodingStatistics = "encoding-statistics";
@@ -23536,6 +23609,9 @@ package android.media {
field public static final int COLOR_TRANSFER_LINEAR = 1; // 0x1
field public static final int COLOR_TRANSFER_SDR_VIDEO = 3; // 0x3
field public static final int COLOR_TRANSFER_ST2084 = 6; // 0x6
+ field @FlaggedApi("android.media.codec.in_process_sw_audio_codec") public static final int FLAG_SECURITY_MODEL_MEMORY_SAFE = 2; // 0x2
+ field @FlaggedApi("android.media.codec.in_process_sw_audio_codec") public static final int FLAG_SECURITY_MODEL_SANDBOXED = 1; // 0x1
+ field @FlaggedApi("android.media.codec.in_process_sw_audio_codec") public static final int FLAG_SECURITY_MODEL_TRUSTED_CONTENT_ONLY = 4; // 0x4
field public static final String KEY_AAC_DRC_ALBUM_MODE = "aac-drc-album-mode";
field public static final String KEY_AAC_DRC_ATTENUATION_FACTOR = "aac-drc-cut-level";
field public static final String KEY_AAC_DRC_BOOST_FACTOR = "aac-drc-boost-level";
@@ -23617,6 +23693,7 @@ package android.media {
field public static final String KEY_REPEAT_PREVIOUS_FRAME_AFTER = "repeat-previous-frame-after";
field public static final String KEY_ROTATION = "rotation-degrees";
field public static final String KEY_SAMPLE_RATE = "sample-rate";
+ field @FlaggedApi("android.media.codec.in_process_sw_audio_codec") public static final String KEY_SECURITY_MODEL = "security-model";
field public static final String KEY_SLICE_HEIGHT = "slice-height";
field public static final String KEY_SLOW_MOTION_MARKERS = "slow-motion-markers";
field public static final String KEY_STRIDE = "stride";
@@ -24505,6 +24582,7 @@ package android.media {
method @FlaggedApi("com.android.media.flags.enable_screen_off_scanning") @NonNull public android.media.MediaRouter2.ScanToken requestScan(@NonNull android.media.MediaRouter2.ScanRequest);
method public void setOnGetControllerHintsListener(@Nullable android.media.MediaRouter2.OnGetControllerHintsListener);
method public void setRouteListingPreference(@Nullable android.media.RouteListingPreference);
+ method @FlaggedApi("com.android.media.flags.enable_privileged_routing_for_media_routing_control") @RequiresPermission(anyOf={android.Manifest.permission.MEDIA_CONTENT_CONTROL, android.Manifest.permission.MEDIA_ROUTING_CONTROL}) public void setRouteVolume(@NonNull android.media.MediaRoute2Info, int);
method public boolean showSystemOutputSwitcher();
method public void stop();
method public void transferTo(@NonNull android.media.MediaRoute2Info);
@@ -26021,7 +26099,6 @@ package android.media.metrics {
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.media.metrics.MediaItemInfo> CREATOR;
field public static final long DATA_TYPE_AUDIO = 4L; // 0x4L
- field public static final long DATA_TYPE_CUE_POINTS = 128L; // 0x80L
field public static final long DATA_TYPE_DEPTH = 16L; // 0x10L
field public static final long DATA_TYPE_GAIN_MAP = 32L; // 0x20L
field public static final long DATA_TYPE_GAPLESS = 256L; // 0x100L
@@ -26030,6 +26107,7 @@ package android.media.metrics {
field public static final long DATA_TYPE_IMAGE = 1L; // 0x1L
field public static final long DATA_TYPE_METADATA = 8L; // 0x8L
field public static final long DATA_TYPE_SPATIAL_AUDIO = 512L; // 0x200L
+ field public static final long DATA_TYPE_SPEED_SETTING_CUE_POINTS = 128L; // 0x80L
field public static final long DATA_TYPE_VIDEO = 2L; // 0x2L
field public static final int SOURCE_TYPE_CAMERA = 2; // 0x2
field public static final int SOURCE_TYPE_EDITING_SESSION = 3; // 0x3
@@ -34029,6 +34107,9 @@ package android.os {
field public static final int USER_OPERATION_ERROR_MAX_USERS = 6; // 0x6
field public static final int USER_OPERATION_ERROR_UNKNOWN = 1; // 0x1
field public static final int USER_OPERATION_SUCCESS = 0; // 0x0
+ field @FlaggedApi("android.os.allow_private_profile") public static final String USER_TYPE_PROFILE_CLONE = "android.os.usertype.profile.CLONE";
+ field @FlaggedApi("android.os.allow_private_profile") public static final String USER_TYPE_PROFILE_MANAGED = "android.os.usertype.profile.MANAGED";
+ field @FlaggedApi("android.os.allow_private_profile") public static final String USER_TYPE_PROFILE_PRIVATE = "android.os.usertype.profile.PRIVATE";
}
public static class UserManager.UserOperationException extends java.lang.RuntimeException {
@@ -39649,7 +39730,7 @@ package android.security.keystore {
method @Nullable public java.util.Date getKeyValidityStart();
method @NonNull public String getKeystoreAlias();
method public int getMaxUsageCount();
- method @FlaggedApi("android.security.mgf1_digest_setter") @NonNull public java.util.Set<java.lang.String> getMgf1Digests();
+ method @FlaggedApi("android.security.mgf1_digest_setter_v2") @NonNull public java.util.Set<java.lang.String> getMgf1Digests();
method public int getPurposes();
method @NonNull public String[] getSignaturePaddings();
method public int getUserAuthenticationType();
@@ -39657,7 +39738,7 @@ package android.security.keystore {
method public boolean isDevicePropertiesAttestationIncluded();
method @NonNull public boolean isDigestsSpecified();
method public boolean isInvalidatedByBiometricEnrollment();
- method @FlaggedApi("android.security.mgf1_digest_setter") @NonNull public boolean isMgf1DigestsSpecified();
+ method @FlaggedApi("android.security.mgf1_digest_setter_v2") @NonNull public boolean isMgf1DigestsSpecified();
method public boolean isRandomizedEncryptionRequired();
method public boolean isStrongBoxBacked();
method public boolean isUnlockedDeviceRequired();
@@ -39689,7 +39770,7 @@ package android.security.keystore {
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityForOriginationEnd(java.util.Date);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityStart(java.util.Date);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setMaxUsageCount(int);
- method @FlaggedApi("android.security.mgf1_digest_setter") @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setMgf1Digests(@NonNull java.lang.String...);
+ method @FlaggedApi("android.security.mgf1_digest_setter_v2") @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setMgf1Digests(@NonNull java.lang.String...);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setRandomizedEncryptionRequired(boolean);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setSignaturePaddings(java.lang.String...);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUnlockedDeviceRequired(boolean);
@@ -39794,14 +39875,14 @@ package android.security.keystore {
method @Nullable public java.util.Date getKeyValidityForOriginationEnd();
method @Nullable public java.util.Date getKeyValidityStart();
method public int getMaxUsageCount();
- method @FlaggedApi("android.security.mgf1_digest_setter") @NonNull public java.util.Set<java.lang.String> getMgf1Digests();
+ method @FlaggedApi("android.security.mgf1_digest_setter_v2") @NonNull public java.util.Set<java.lang.String> getMgf1Digests();
method public int getPurposes();
method @NonNull public String[] getSignaturePaddings();
method public int getUserAuthenticationType();
method public int getUserAuthenticationValidityDurationSeconds();
method public boolean isDigestsSpecified();
method public boolean isInvalidatedByBiometricEnrollment();
- method @FlaggedApi("android.security.mgf1_digest_setter") @NonNull public boolean isMgf1DigestsSpecified();
+ method @FlaggedApi("android.security.mgf1_digest_setter_v2") @NonNull public boolean isMgf1DigestsSpecified();
method public boolean isRandomizedEncryptionRequired();
method public boolean isUnlockedDeviceRequired();
method public boolean isUserAuthenticationRequired();
@@ -39823,7 +39904,7 @@ package android.security.keystore {
method @NonNull public android.security.keystore.KeyProtection.Builder setKeyValidityForOriginationEnd(java.util.Date);
method @NonNull public android.security.keystore.KeyProtection.Builder setKeyValidityStart(java.util.Date);
method @NonNull public android.security.keystore.KeyProtection.Builder setMaxUsageCount(int);
- method @FlaggedApi("android.security.mgf1_digest_setter") @NonNull public android.security.keystore.KeyProtection.Builder setMgf1Digests(@Nullable java.lang.String...);
+ method @FlaggedApi("android.security.mgf1_digest_setter_v2") @NonNull public android.security.keystore.KeyProtection.Builder setMgf1Digests(@Nullable java.lang.String...);
method @NonNull public android.security.keystore.KeyProtection.Builder setRandomizedEncryptionRequired(boolean);
method @NonNull public android.security.keystore.KeyProtection.Builder setSignaturePaddings(java.lang.String...);
method @NonNull public android.security.keystore.KeyProtection.Builder setUnlockedDeviceRequired(boolean);
@@ -42340,6 +42421,7 @@ package android.telecom {
field public static final int SUPPORTS_SET_INACTIVE = 2; // 0x2
field public static final int SUPPORTS_STREAM = 4; // 0x4
field public static final int SUPPORTS_TRANSFER = 8; // 0x8
+ field @FlaggedApi("com.android.server.telecom.flags.transactional_video_state") public static final int SUPPORTS_VIDEO_CALLING = 16; // 0x10
field public static final int VIDEO_CALL = 2; // 0x2
}
@@ -42375,6 +42457,7 @@ package android.telecom {
method @NonNull public android.os.ParcelUuid getCallId();
method public void requestCallEndpointChange(@NonNull android.telecom.CallEndpoint, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.telecom.CallException>);
method @FlaggedApi("com.android.server.telecom.flags.set_mute_state") public void requestMuteState(boolean, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.telecom.CallException>);
+ method @FlaggedApi("com.android.server.telecom.flags.transactional_video_state") public void requestVideoState(int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.telecom.CallException>);
method public void sendEvent(@NonNull String, @NonNull android.os.Bundle);
method public void setActive(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.telecom.CallException>);
method public void setInactive(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.telecom.CallException>);
@@ -42423,6 +42506,7 @@ package android.telecom {
method public void onCallStreamingFailed(int);
method public void onEvent(@NonNull String, @NonNull android.os.Bundle);
method public void onMuteStateChanged(boolean);
+ method @FlaggedApi("com.android.server.telecom.flags.transactional_video_state") public default void onVideoStateChanged(int);
}
public final class CallException extends java.lang.RuntimeException implements android.os.Parcelable {
@@ -43179,6 +43263,7 @@ package android.telecom {
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_SMS, android.Manifest.permission.READ_PHONE_NUMBERS}, conditional=true) public String getLine1Number(android.telecom.PhoneAccountHandle);
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_OWN_CALLS) public java.util.List<android.telecom.PhoneAccountHandle> getOwnSelfManagedPhoneAccounts();
method public android.telecom.PhoneAccount getPhoneAccount(android.telecom.PhoneAccountHandle);
+ method @FlaggedApi("com.android.server.telecom.flags.get_registered_phone_accounts") @NonNull public java.util.List<android.telecom.PhoneAccount> getRegisteredPhoneAccounts();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telecom.PhoneAccountHandle> getSelfManagedPhoneAccounts();
method public android.telecom.PhoneAccountHandle getSimCallManager();
method @Nullable public android.telecom.PhoneAccountHandle getSimCallManagerForSubscription(int);
@@ -45726,6 +45811,7 @@ package android.telephony {
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telephony.SubscriptionInfo> getSubscriptionsInGroup(@NonNull android.os.ParcelUuid);
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isActiveSubscriptionId(int);
method public boolean isNetworkRoaming(int);
+ method @FlaggedApi("com.android.internal.telephony.flags.subscription_user_association_query") @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isSubscriptionAssociatedWithUser(int);
method public static boolean isUsableSubscriptionId(int);
method public static boolean isValidSubscriptionId(int);
method public void removeOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
@@ -47475,6 +47561,7 @@ package android.text {
method @FlaggedApi("com.android.text.flags.no_break_no_hyphenation_span") @NonNull public android.text.DynamicLayout.Builder setLineBreakConfig(@NonNull android.graphics.text.LineBreakConfig);
method @NonNull public android.text.DynamicLayout.Builder setLineSpacing(float, @FloatRange(from=0.0) float);
method @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") @NonNull public android.text.DynamicLayout.Builder setMinimumFontMetrics(@Nullable android.graphics.Paint.FontMetrics);
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.text.DynamicLayout.Builder setShiftDrawingOffsetForStartOverhang(boolean);
method @NonNull public android.text.DynamicLayout.Builder setTextDirection(@NonNull android.text.TextDirectionHeuristic);
method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.text.DynamicLayout.Builder setUseBoundsForWidth(boolean);
method @NonNull public android.text.DynamicLayout.Builder setUseLineSpacingFromFallbacks(boolean);
@@ -47678,6 +47765,7 @@ package android.text {
method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @Nullable public final int[] getRightIndents();
method public float getSecondaryHorizontal(int);
method public void getSelectionPath(int, int, android.graphics.Path);
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public boolean getShiftDrawingOffsetForStartOverhang();
method public final float getSpacingAdd();
method public final float getSpacingMultiplier();
method @NonNull public final CharSequence getText();
@@ -47734,6 +47822,7 @@ package android.text {
method @NonNull public android.text.Layout.Builder setMaxLines(@IntRange(from=1) int);
method @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") @NonNull public android.text.Layout.Builder setMinimumFontMetrics(@Nullable android.graphics.Paint.FontMetrics);
method @NonNull public android.text.Layout.Builder setRightIndents(@Nullable int[]);
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.text.Layout.Builder setShiftDrawingOffsetForStartOverhang(boolean);
method @NonNull public android.text.Layout.Builder setTextDirectionHeuristic(@NonNull android.text.TextDirectionHeuristic);
method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.text.Layout.Builder setUseBoundsForWidth(boolean);
}
@@ -48005,6 +48094,7 @@ package android.text {
method @NonNull public android.text.StaticLayout.Builder setLineSpacing(float, @FloatRange(from=0.0) float);
method @NonNull public android.text.StaticLayout.Builder setMaxLines(@IntRange(from=0) int);
method @FlaggedApi("com.android.text.flags.fix_line_height_for_locale") @NonNull public android.text.StaticLayout.Builder setMinimumFontMetrics(@Nullable android.graphics.Paint.FontMetrics);
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.text.StaticLayout.Builder setShiftDrawingOffsetForStartOverhang(boolean);
method public android.text.StaticLayout.Builder setText(CharSequence);
method @NonNull public android.text.StaticLayout.Builder setTextDirection(@NonNull android.text.TextDirectionHeuristic);
method @FlaggedApi("com.android.text.flags.use_bounds_for_width") @NonNull public android.text.StaticLayout.Builder setUseBoundsForWidth(boolean);
@@ -50510,11 +50600,10 @@ package android.view {
method public boolean applyTransactionOnDraw(@NonNull android.view.SurfaceControl.Transaction);
method @Nullable public android.view.SurfaceControl.Transaction buildReparentTransaction(@NonNull android.view.SurfaceControl);
method public default int getBufferTransformHint();
- method @FlaggedApi("com.android.window.flags.get_host_token_api") @Nullable public default android.os.IBinder getHostToken();
+ method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") @NonNull public default android.window.InputTransferToken getInputTransferToken();
method public default void removeOnBufferTransformHintChangedListener(@NonNull android.view.AttachedSurfaceControl.OnBufferTransformHintChangedListener);
method public default void setChildBoundingInsets(@NonNull android.graphics.Rect);
method public default void setTouchableRegion(@Nullable android.graphics.Region);
- method @FlaggedApi("com.android.window.flags.transfer_gesture_to_embedded") public default boolean transferHostTouchGestureToEmbedded(@NonNull android.view.SurfaceControlViewHost.SurfacePackage);
}
@UiThread public static interface AttachedSurfaceControl.OnBufferTransformHintChangedListener {
@@ -52185,6 +52274,7 @@ package android.view {
method @NonNull public android.view.SurfaceControl.Transaction setCrop(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Rect);
method @NonNull public android.view.SurfaceControl.Transaction setDamageRegion(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Region);
method @NonNull public android.view.SurfaceControl.Transaction setDataSpace(@NonNull android.view.SurfaceControl, int);
+ method @FlaggedApi("com.android.graphics.hwui.flags.limited_hdr") @NonNull public android.view.SurfaceControl.Transaction setDesiredHdrHeadroom(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0f) float);
method @FlaggedApi("com.android.window.flags.sdk_desired_present_time") @NonNull public android.view.SurfaceControl.Transaction setDesiredPresentTimeNanos(long);
method @NonNull public android.view.SurfaceControl.Transaction setExtendedRangeBrightness(@NonNull android.view.SurfaceControl, float, float);
method @NonNull public android.view.SurfaceControl.Transaction setFrameRate(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0) float, int);
@@ -52220,6 +52310,7 @@ package android.view {
public class SurfaceControlViewHost {
ctor public SurfaceControlViewHost(@NonNull android.content.Context, @NonNull android.view.Display, @Nullable android.os.IBinder);
+ ctor @FlaggedApi("com.android.window.flags.surface_control_input_receiver") public SurfaceControlViewHost(@NonNull android.content.Context, @NonNull android.view.Display, @Nullable android.window.InputTransferToken);
method @Nullable public android.view.SurfaceControlViewHost.SurfacePackage getSurfacePackage();
method @Nullable public android.view.View getView();
method public void relayout(int, int);
@@ -52231,6 +52322,7 @@ package android.view {
public static final class SurfaceControlViewHost.SurfacePackage implements android.os.Parcelable {
ctor public SurfaceControlViewHost.SurfacePackage(@NonNull android.view.SurfaceControlViewHost.SurfacePackage);
method public int describeContents();
+ method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") @Nullable public android.window.InputTransferToken getInputTransferToken();
method @NonNull public android.view.SurfaceControl getSurfaceControl();
method public void notifyConfigurationChanged(@NonNull android.content.res.Configuration);
method public void notifyDetachedFromWindow();
@@ -52286,6 +52378,7 @@ package android.view {
method @Nullable public android.os.IBinder getHostToken();
method public android.view.SurfaceControl getSurfaceControl();
method public void setChildSurfacePackage(@NonNull android.view.SurfaceControlViewHost.SurfacePackage);
+ method @FlaggedApi("com.android.graphics.hwui.flags.limited_hdr") public void setDesiredHdrHeadroom(@FloatRange(from=0.0f, to=10000.0) float);
method public void setSecure(boolean);
method public void setSurfaceLifecycle(int);
method public void setZOrderMediaOverlay(boolean);
@@ -52421,7 +52514,7 @@ package android.view {
method public final void cancelPendingInputEvents();
method public boolean checkInputConnectionProxy(android.view.View);
method public void clearAnimation();
- method @FlaggedApi("autofill_credman_dev_integration") public void clearCredentialManagerRequest();
+ method @FlaggedApi("android.service.autofill.autofill_credman_dev_integration") public void clearCredentialManagerRequest();
method public void clearFocus();
method public void clearViewTranslationCallback();
method public static int combineMeasuredStates(int, int);
@@ -52531,8 +52624,8 @@ package android.view {
method @FlaggedApi("android.view.flags.sensitive_content_app_protection_api") public final int getContentSensitivity();
method @UiContext public final android.content.Context getContext();
method protected android.view.ContextMenu.ContextMenuInfo getContextMenuInfo();
- method @FlaggedApi("autofill_credman_dev_integration") @Nullable public final android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException> getCredentialManagerCallback();
- method @FlaggedApi("autofill_credman_dev_integration") @Nullable public final android.credentials.GetCredentialRequest getCredentialManagerRequest();
+ method @FlaggedApi("android.service.autofill.autofill_credman_dev_integration") @Nullable public final android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException> getCredentialManagerCallback();
+ method @FlaggedApi("android.service.autofill.autofill_credman_dev_integration") @Nullable public final android.credentials.GetCredentialRequest getCredentialManagerRequest();
method public final boolean getDefaultFocusHighlightEnabled();
method public static int getDefaultSize(int, int);
method public android.view.Display getDisplay();
@@ -52917,7 +53010,7 @@ package android.view {
method public void setContentDescription(CharSequence);
method @FlaggedApi("android.view.flags.sensitive_content_app_protection_api") public final void setContentSensitivity(int);
method public void setContextClickable(boolean);
- method @FlaggedApi("autofill_credman_dev_integration") public void setCredentialManagerRequest(@NonNull android.credentials.GetCredentialRequest, @NonNull android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException>);
+ method @FlaggedApi("android.service.autofill.autofill_credman_dev_integration") public void setCredentialManagerRequest(@NonNull android.credentials.GetCredentialRequest, @NonNull android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException>);
method public void setDefaultFocusHighlightEnabled(boolean);
method @Deprecated public void setDrawingCacheBackgroundColor(@ColorInt int);
method @Deprecated public void setDrawingCacheEnabled(boolean);
@@ -53112,7 +53205,7 @@ package android.view {
field public static final int DRAG_FLAG_GLOBAL_URI_READ = 1; // 0x1
field public static final int DRAG_FLAG_GLOBAL_URI_WRITE = 2; // 0x2
field public static final int DRAG_FLAG_OPAQUE = 512; // 0x200
- field @FlaggedApi("com.android.window.flags.delegate_unhandled_drags") public static final int DRAG_FLAG_START_PENDING_INTENT_ON_UNHANDLED_DRAG = 8192; // 0x2000
+ field @FlaggedApi("com.android.window.flags.delegate_unhandled_drags") public static final int DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG = 8192; // 0x2000
field @Deprecated public static final int DRAWING_CACHE_QUALITY_AUTO = 0; // 0x0
field @Deprecated public static final int DRAWING_CACHE_QUALITY_HIGH = 1048576; // 0x100000
field @Deprecated public static final int DRAWING_CACHE_QUALITY_LOW = 524288; // 0x80000
@@ -53191,7 +53284,7 @@ package android.view {
field protected static final int[] PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
field protected static final int[] PRESSED_STATE_SET;
field protected static final int[] PRESSED_WINDOW_FOCUSED_STATE_SET;
- field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final float REQUESTED_FRAME_RATE_CATEGORY_DEFAULT = 0.0f;
+ field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final float REQUESTED_FRAME_RATE_CATEGORY_DEFAULT = (0.0f/0.0f);
field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final float REQUESTED_FRAME_RATE_CATEGORY_HIGH = -4.0f;
field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final float REQUESTED_FRAME_RATE_CATEGORY_LOW = -2.0f;
field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final float REQUESTED_FRAME_RATE_CATEGORY_NORMAL = -3.0f;
@@ -53796,11 +53889,11 @@ package android.view {
method public abstract int addChildCount(int);
method public abstract void asyncCommit();
method public abstract android.view.ViewStructure asyncNewChild(int);
- method @FlaggedApi("autofill_credman_dev_integration") public void clearCredentialManagerRequest();
+ method @FlaggedApi("android.service.autofill.autofill_credman_dev_integration") public void clearCredentialManagerRequest();
method @Nullable public abstract android.view.autofill.AutofillId getAutofillId();
method public abstract int getChildCount();
- method @FlaggedApi("autofill_credman_dev_integration") @Nullable public android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException> getCredentialManagerCallback();
- method @FlaggedApi("autofill_credman_dev_integration") @Nullable public android.credentials.GetCredentialRequest getCredentialManagerRequest();
+ method @FlaggedApi("android.service.autofill.autofill_credman_dev_integration") @Nullable public android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException> getCredentialManagerCallback();
+ method @FlaggedApi("android.service.autofill.autofill_credman_dev_integration") @Nullable public android.credentials.GetCredentialRequest getCredentialManagerRequest();
method public abstract android.os.Bundle getExtras();
method public abstract CharSequence getHint();
method public abstract CharSequence getText();
@@ -53825,7 +53918,7 @@ package android.view {
method public abstract void setClickable(boolean);
method public abstract void setContentDescription(CharSequence);
method public abstract void setContextClickable(boolean);
- method @FlaggedApi("autofill_credman_dev_integration") public void setCredentialManagerRequest(@NonNull android.credentials.GetCredentialRequest, @NonNull android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException>);
+ method @FlaggedApi("android.service.autofill.autofill_credman_dev_integration") public void setCredentialManagerRequest(@NonNull android.credentials.GetCredentialRequest, @NonNull android.os.OutcomeReceiver<android.credentials.GetCredentialResponse,android.credentials.GetCredentialException>);
method public abstract void setDataIsSensitive(boolean);
method public abstract void setDimens(int, int, int, int, int, int);
method public abstract void setElevation(float);
@@ -54010,6 +54103,7 @@ package android.view {
method public abstract void invalidatePanelMenu(int);
method public final boolean isActive();
method public abstract boolean isFloating();
+ method @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public boolean isFrameRatePowerSavingsBalanced();
method public boolean isNavigationBarContrastEnforced();
method public abstract boolean isShortcutKey(int, android.view.KeyEvent);
method @Deprecated public boolean isStatusBarContrastEnforced();
@@ -54059,6 +54153,7 @@ package android.view {
method public void setFlags(int, int);
method public void setFormat(int);
method @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public void setFrameRateBoostOnTouchEnabled(boolean);
+ method @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public void setFrameRatePowerSavingsBalanced(boolean);
method public void setGravity(int);
method @RequiresPermission(android.Manifest.permission.HIDE_OVERLAY_WINDOWS) public final void setHideOverlayWindows(boolean);
method public void setIcon(@DrawableRes int);
@@ -54366,15 +54461,17 @@ package android.view {
method @Deprecated public android.view.Display getDefaultDisplay();
method @NonNull public default android.view.WindowMetrics getMaximumWindowMetrics();
method public default boolean isCrossWindowBlurEnabled();
- method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") public default void registerBatchedSurfaceControlInputReceiver(int, @NonNull android.os.IBinder, @NonNull android.view.SurfaceControl, @NonNull android.view.Choreographer, @NonNull android.view.SurfaceControlInputReceiver);
+ method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") @NonNull public default android.window.InputTransferToken registerBatchedSurfaceControlInputReceiver(int, @NonNull android.window.InputTransferToken, @NonNull android.view.SurfaceControl, @NonNull android.view.Choreographer, @NonNull android.view.SurfaceControlInputReceiver);
method @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public default void registerTrustedPresentationListener(@NonNull android.os.IBinder, @NonNull android.window.TrustedPresentationThresholds, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
- method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") public default void registerUnbatchedSurfaceControlInputReceiver(int, @NonNull android.os.IBinder, @NonNull android.view.SurfaceControl, @NonNull android.os.Looper, @NonNull android.view.SurfaceControlInputReceiver);
+ method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") @NonNull public default android.window.InputTransferToken registerUnbatchedSurfaceControlInputReceiver(int, @NonNull android.window.InputTransferToken, @NonNull android.view.SurfaceControl, @NonNull android.os.Looper, @NonNull android.view.SurfaceControlInputReceiver);
method public default void removeCrossWindowBlurEnabledListener(@NonNull java.util.function.Consumer<java.lang.Boolean>);
method public default void removeProposedRotationListener(@NonNull java.util.function.IntConsumer);
method @FlaggedApi("com.android.window.flags.screen_recording_callbacks") @RequiresPermission(android.Manifest.permission.DETECT_SCREEN_RECORDING) public default void removeScreenRecordingCallback(@NonNull java.util.function.Consumer<java.lang.Integer>);
method public void removeViewImmediate(android.view.View);
+ method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") public default boolean transferTouchGesture(@NonNull android.window.InputTransferToken, @NonNull android.window.InputTransferToken);
method @FlaggedApi("com.android.window.flags.surface_control_input_receiver") public default void unregisterSurfaceControlInputReceiver(@NonNull android.view.SurfaceControl);
method @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public default void unregisterTrustedPresentationListener(@NonNull java.util.function.Consumer<java.lang.Boolean>);
+ field @FlaggedApi("com.android.window.flags.cover_display_opt_in") public static final int COMPAT_SMALL_COVER_SCREEN_OPT_IN = 1; // 0x1
field public static final String PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE = "android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE";
field public static final String PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED = "android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED";
field @FlaggedApi("com.android.window.flags.untrusted_embedding_state_sharing") public static final String PROPERTY_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING_STATE_SHARING = "android.window.PROPERTY_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING_STATE_SHARING";
@@ -54387,6 +54484,7 @@ package android.view {
field public static final String PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE = "android.window.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE";
field @FlaggedApi("com.android.window.flags.app_compat_properties_api") public static final String PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES = "android.window.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES";
field public static final String PROPERTY_COMPAT_ALLOW_SANDBOXING_VIEW_BOUNDS_APIS = "android.window.PROPERTY_COMPAT_ALLOW_SANDBOXING_VIEW_BOUNDS_APIS";
+ field @FlaggedApi("com.android.window.flags.cover_display_opt_in") public static final String PROPERTY_COMPAT_ALLOW_SMALL_COVER_SCREEN = "android.window.PROPERTY_COMPAT_ALLOW_SMALL_COVER_SCREEN";
field @FlaggedApi("com.android.window.flags.app_compat_properties_api") public static final String PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE = "android.window.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE";
field @FlaggedApi("com.android.window.flags.app_compat_properties_api") public static final String PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE = "android.window.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE";
field public static final String PROPERTY_COMPAT_ENABLE_FAKE_FOCUS = "android.window.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS";
@@ -54427,6 +54525,7 @@ package android.view {
method @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public boolean getFrameRateBoostOnTouchEnabled();
method public final CharSequence getTitle();
method public boolean isFitInsetsIgnoringVisibility();
+ method @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public boolean isFrameRatePowerSavingsBalanced();
method public boolean isHdrConversionEnabled();
method public static boolean mayUseInputMethod(int);
method public void setBlurBehindRadius(@IntRange(from=0) int);
@@ -54437,6 +54536,7 @@ package android.view {
method public void setFitInsetsSides(int);
method public void setFitInsetsTypes(int);
method @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public void setFrameRateBoostOnTouchEnabled(boolean);
+ method @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public void setFrameRatePowerSavingsBalanced(boolean);
method public void setHdrConversionEnabled(boolean);
method public final void setTitle(CharSequence);
method public void setWallpaperTouchEventsEnabled(boolean);
@@ -55199,6 +55299,7 @@ package android.view.accessibility {
field public static final int TYPE_MAGNIFICATION_OVERLAY = 6; // 0x6
field public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5; // 0x5
field public static final int TYPE_SYSTEM = 3; // 0x3
+ field @FlaggedApi("android.view.accessibility.add_type_window_control") public static final int TYPE_WINDOW_CONTROL = 7; // 0x7
}
public class CaptioningManager {
@@ -56244,7 +56345,8 @@ package android.view.inputmethod {
public final class InputMethodManager {
method public boolean acceptStylusHandwritingDelegation(@NonNull android.view.View);
method public boolean acceptStylusHandwritingDelegation(@NonNull android.view.View, @NonNull String);
- method @FlaggedApi("android.view.inputmethod.home_screen_handwriting_delegator") public boolean acceptStylusHandwritingDelegation(@NonNull android.view.View, @NonNull String, int);
+ method @FlaggedApi("android.view.inputmethod.use_zero_jank_proxy") public void acceptStylusHandwritingDelegation(@NonNull android.view.View, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method @FlaggedApi("android.view.inputmethod.home_screen_handwriting_delegator") public void acceptStylusHandwritingDelegation(@NonNull android.view.View, @NonNull String, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method public void dispatchKeyEventFromInputMethod(@Nullable android.view.View, @NonNull android.view.KeyEvent);
method public void displayCompletions(android.view.View, android.view.inputmethod.CompletionInfo[]);
method @Nullable public android.view.inputmethod.InputMethodInfo getCurrentInputMethodInfo();
@@ -60167,6 +60269,7 @@ package android.widget {
}
@FlaggedApi("android.appwidget.flags.draw_data_parcel") public static final class RemoteViews.DrawInstructions {
+ method @FlaggedApi("android.appwidget.flags.draw_data_parcel") public static long getSupportedVersion();
}
@FlaggedApi("android.appwidget.flags.draw_data_parcel") public static final class RemoteViews.DrawInstructions.Builder {
@@ -60819,6 +60922,7 @@ package android.widget {
method public float getShadowDx();
method public float getShadowDy();
method public float getShadowRadius();
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public boolean getShiftDrawingOffsetForStartOverhang();
method public final boolean getShowSoftInputOnFocus();
method public CharSequence getText();
method @NonNull public android.view.textclassifier.TextClassifier getTextClassifier();
@@ -60955,6 +61059,7 @@ package android.widget {
method public void setSearchResultHighlights(@Nullable int...);
method public void setSelectAllOnFocus(boolean);
method public void setShadowLayer(float, float, float, int);
+ method @FlaggedApi("com.android.text.flags.use_bounds_for_width") public void setShiftDrawingOffsetForStartOverhang(boolean);
method public final void setShowSoftInputOnFocus(boolean);
method public void setSingleLine();
method public void setSingleLine(boolean);
@@ -61349,6 +61454,12 @@ package android.window {
field public static final int EDGE_RIGHT = 1; // 0x1
}
+ @FlaggedApi("com.android.window.flags.surface_control_input_receiver") public final class InputTransferToken implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.window.InputTransferToken> CREATOR;
+ }
+
public interface OnBackAnimationCallback extends android.window.OnBackInvokedCallback {
method public default void onBackCancelled();
method public default void onBackProgressed(@NonNull android.window.BackEvent);
@@ -61396,11 +61507,11 @@ package android.window {
@FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public final class TrustedPresentationThresholds implements android.os.Parcelable {
ctor @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public TrustedPresentationThresholds(@FloatRange(from=0.0f, fromInclusive=false, to=1.0f) float, @FloatRange(from=0.0f, fromInclusive=false, to=1.0f) float, @IntRange(from=1) int);
method @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public int describeContents();
+ method @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") @FloatRange(from=0.0f, fromInclusive=false, to=1.0f) public float getMinAlpha();
+ method @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") @FloatRange(from=0.0f, fromInclusive=false, to=1.0f) public float getMinFractionRendered();
+ method @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") @IntRange(from=1) public int getStabilityRequirementMillis();
method @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") public void writeToParcel(@NonNull android.os.Parcel, int);
field @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") @NonNull public static final android.os.Parcelable.Creator<android.window.TrustedPresentationThresholds> CREATOR;
- field @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") @FloatRange(from=0.0f, fromInclusive=false, to=1.0f) public final float minAlpha;
- field @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") @FloatRange(from=0.0f, fromInclusive=false, to=1.0f) public final float minFractionRendered;
- field @FlaggedApi("com.android.window.flags.trusted_presentation_listener_for_window") @IntRange(from=1) public final int stabilityRequirementMs;
}
}
diff --git a/core/api/lint-baseline.txt b/core/api/lint-baseline.txt
index b36b963f99c9..aa5e547800b1 100644
--- a/core/api/lint-baseline.txt
+++ b/core/api/lint-baseline.txt
@@ -245,6 +245,14 @@ BroadcastBehavior: android.telephony.euicc.EuiccManager#ACTION_NOTIFY_CARRIER_SE
Field 'ACTION_NOTIFY_CARRIER_SETUP_INCOMPLETE' is missing @BroadcastBehavior
+CompileTimeConstant: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_GIMBAL:
+ All constants must be defined at compile time: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_GIMBAL
+CompileTimeConstant: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED:
+ All constants must be defined at compile time: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED
+CompileTimeConstant: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_OFF:
+ All constants must be defined at compile time: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_OFF
+
+
DeprecationMismatch: android.accounts.AccountManager#newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, String[], boolean, String, String, String[], android.os.Bundle):
Method android.accounts.AccountManager.newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, String[], boolean, String, String, String[], android.os.Bundle): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
DeprecationMismatch: android.app.Activity#enterPictureInPictureMode():
@@ -383,6 +391,10 @@ DeprecationMismatch: javax.microedition.khronos.egl.EGL10#eglCreatePixmapSurface
Method javax.microedition.khronos.egl.EGL10.eglCreatePixmapSurface(javax.microedition.khronos.egl.EGLDisplay, javax.microedition.khronos.egl.EGLConfig, Object, int[]): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+HiddenAbstractMethod: android.app.Notification.Style#areNotificationsVisiblyDifferent(android.app.Notification.Style):
+ areNotificationsVisiblyDifferent cannot be hidden and abstract when Style has a visible constructor, in case a third-party attempts to subclass it.
+
+
InvalidNullabilityOverride: android.app.Notification.TvExtender#extend(android.app.Notification.Builder) parameter #0:
Invalid nullability on parameter `builder` in method `extend`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
InvalidNullabilityOverride: android.media.midi.MidiUmpDeviceService#onBind(android.content.Intent) parameter #0:
@@ -1087,6 +1099,14 @@ RequiresPermission: android.webkit.WebSettings#setGeolocationEnabled(boolean):
Method 'setGeolocationEnabled' documentation mentions permissions without declaring @RequiresPermission
+StaticUtils: ExtensionCaptureRequest:
+ Fully-static utility classes must not have constructor
+StaticUtils: android.hardware.camera2.ExtensionCaptureRequest:
+ Fully-static utility classes must not have constructor
+StaticUtils: android.hardware.camera2.ExtensionCaptureResult:
+ Fully-static utility classes must not have constructor
+
+
Todo: android.hardware.camera2.params.StreamConfigurationMap:
Documentation mentions 'TODO'
Todo: android.provider.ContactsContract.RawContacts#newEntityIterator(android.database.Cursor):
@@ -1445,6 +1465,14 @@ UnflaggedApi: android.graphics.text.PositionedGlyphs#getItalicOverride(int):
New API must be flagged with @FlaggedApi: method android.graphics.text.PositionedGlyphs.getItalicOverride(int)
UnflaggedApi: android.graphics.text.PositionedGlyphs#getWeightOverride(int):
New API must be flagged with @FlaggedApi: method android.graphics.text.PositionedGlyphs.getWeightOverride(int)
+UnflaggedApi: android.hardware.camera2.ExtensionCaptureRequest:
+ New API must be flagged with @FlaggedApi: class android.hardware.camera2.ExtensionCaptureRequest
+UnflaggedApi: android.hardware.camera2.ExtensionCaptureRequest#ExtensionCaptureRequest():
+ New API must be flagged with @FlaggedApi: constructor android.hardware.camera2.ExtensionCaptureRequest()
+UnflaggedApi: android.hardware.camera2.ExtensionCaptureResult:
+ New API must be flagged with @FlaggedApi: class android.hardware.camera2.ExtensionCaptureResult
+UnflaggedApi: android.hardware.camera2.ExtensionCaptureResult#ExtensionCaptureResult():
+ New API must be flagged with @FlaggedApi: constructor android.hardware.camera2.ExtensionCaptureResult()
UnflaggedApi: android.media.MediaRoute2Info#TYPE_REMOTE_CAR:
New API must be flagged with @FlaggedApi: field android.media.MediaRoute2Info.TYPE_REMOTE_CAR
UnflaggedApi: android.media.MediaRoute2Info#TYPE_REMOTE_COMPUTER:
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 7c4df280eaa7..9c1a8e854e92 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -135,7 +135,7 @@ package android.content.pm {
@FlaggedApi("android.permission.flags.enhanced_confirmation_mode_apis_enabled") public class SignedPackage {
method @NonNull public byte[] getCertificateDigest();
- method @NonNull public String getPkgName();
+ method @NonNull public String getPackageName();
}
}
diff --git a/core/api/module-lib-lint-baseline.txt b/core/api/module-lib-lint-baseline.txt
index 42c4efc139ca..79fbc139d509 100644
--- a/core/api/module-lib-lint-baseline.txt
+++ b/core/api/module-lib-lint-baseline.txt
@@ -497,6 +497,10 @@ DeprecationMismatch: javax.microedition.khronos.egl.EGL10#eglCreatePixmapSurface
Method javax.microedition.khronos.egl.EGL10.eglCreatePixmapSurface(javax.microedition.khronos.egl.EGLDisplay, javax.microedition.khronos.egl.EGLConfig, Object, int[]): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+HiddenAbstractMethod: android.app.Notification.Style#areNotificationsVisiblyDifferent(android.app.Notification.Style):
+ areNotificationsVisiblyDifferent cannot be hidden and abstract when Style has a visible constructor, in case a third-party attempts to subclass it.
+
+
RequiresPermission: android.accounts.AccountManager#getAccountsByTypeAndFeatures(String, String[], android.accounts.AccountManagerCallback<android.accounts.Account[]>, android.os.Handler):
Method 'getAccountsByTypeAndFeatures' documentation mentions permissions without declaring @RequiresPermission
RequiresPermission: android.accounts.AccountManager#hasFeatures(android.accounts.Account, String[], android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler):
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 88f9aff4a336..d9b5d56544d6 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -69,6 +69,8 @@ package android {
field public static final String BIND_MUSIC_RECOGNITION_SERVICE = "android.permission.BIND_MUSIC_RECOGNITION_SERVICE";
field public static final String BIND_NETWORK_RECOMMENDATION_SERVICE = "android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE";
field public static final String BIND_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE";
+ field @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public static final String BIND_ON_DEVICE_INTELLIGENCE_SERVICE = "android.permission.BIND_ON_DEVICE_INTELLIGENCE_SERVICE";
+ field @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public static final String BIND_ON_DEVICE_TRUSTED_SERVICE = "android.permission.BIND_ON_DEVICE_TRUSTED_SERVICE";
field public static final String BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE = "android.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE";
field public static final String BIND_PRINT_RECOMMENDATION_SERVICE = "android.permission.BIND_PRINT_RECOMMENDATION_SERVICE";
field public static final String BIND_REMOTE_LOCKSCREEN_VALIDATION_SERVICE = "android.permission.BIND_REMOTE_LOCKSCREEN_VALIDATION_SERVICE";
@@ -100,6 +102,7 @@ package android {
field public static final String CAMERA_DISABLE_TRANSMIT_LED = "android.permission.CAMERA_DISABLE_TRANSMIT_LED";
field @FlaggedApi("com.android.internal.camera.flags.camera_hsum_permission") public static final String CAMERA_HEADLESS_SYSTEM_USER = "android.permission.CAMERA_HEADLESS_SYSTEM_USER";
field public static final String CAMERA_OPEN_CLOSE_LISTENER = "android.permission.CAMERA_OPEN_CLOSE_LISTENER";
+ field @FlaggedApi("com.android.internal.camera.flags.camera_privacy_allowlist") public static final String CAMERA_PRIVACY_ALLOWLIST = "android.permission.CAMERA_PRIVACY_ALLOWLIST";
field public static final String CAPTURE_AUDIO_HOTWORD = "android.permission.CAPTURE_AUDIO_HOTWORD";
field public static final String CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD = "android.permission.CAPTURE_CONSENTLESS_BUGREPORT_ON_USERDEBUG_BUILD";
field public static final String CAPTURE_MEDIA_OUTPUT = "android.permission.CAPTURE_MEDIA_OUTPUT";
@@ -194,6 +197,8 @@ package android {
field public static final String MANAGE_DEFAULT_APPLICATIONS = "android.permission.MANAGE_DEFAULT_APPLICATIONS";
field public static final String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
field public static final String MANAGE_DEVICE_POLICY_APP_EXEMPTIONS = "android.permission.MANAGE_DEVICE_POLICY_APP_EXEMPTIONS";
+ field @FlaggedApi("android.app.admin.flags.security_log_v2_enabled") public static final String MANAGE_DEVICE_POLICY_AUDIT_LOGGING = "android.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING";
+ field @FlaggedApi("android.app.admin.flags.device_theft_api_enabled") public static final String MANAGE_DEVICE_POLICY_THEFT_DETECTION = "android.permission.MANAGE_DEVICE_POLICY_THEFT_DETECTION";
field @FlaggedApi("android.permission.flags.enhanced_confirmation_mode_apis_enabled") public static final String MANAGE_ENHANCED_CONFIRMATION_STATES = "android.permission.MANAGE_ENHANCED_CONFIRMATION_STATES";
field public static final String MANAGE_ETHERNET_NETWORKS = "android.permission.MANAGE_ETHERNET_NETWORKS";
field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION";
@@ -364,7 +369,7 @@ package android {
field public static final String SET_VOLUME_KEY_LONG_PRESS_LISTENER = "android.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER";
field public static final String SET_WALLPAPER_COMPONENT = "android.permission.SET_WALLPAPER_COMPONENT";
field public static final String SET_WALLPAPER_DIM_AMOUNT = "android.permission.SET_WALLPAPER_DIM_AMOUNT";
- field @FlaggedApi("android.service.chooser.support_nfc_resolver") public static final String SHOW_CUSTOMIZED_RESOLVER = "android.permission.SHOW_CUSTOMIZED_RESOLVER";
+ field @FlaggedApi("android.nfc.enable_nfc_mainline") public static final String SHOW_CUSTOMIZED_RESOLVER = "android.permission.SHOW_CUSTOMIZED_RESOLVER";
field public static final String SHOW_KEYGUARD_MESSAGE = "android.permission.SHOW_KEYGUARD_MESSAGE";
field public static final String SHUTDOWN = "android.permission.SHUTDOWN";
field public static final String SIGNAL_REBOOT_READINESS = "android.permission.SIGNAL_REBOOT_READINESS";
@@ -383,7 +388,7 @@ package android {
field public static final String SYSTEM_APPLICATION_OVERLAY = "android.permission.SYSTEM_APPLICATION_OVERLAY";
field public static final String SYSTEM_CAMERA = "android.permission.SYSTEM_CAMERA";
field public static final String TETHER_PRIVILEGED = "android.permission.TETHER_PRIVILEGED";
- field @FlaggedApi("com.android.net.thread.flags.thread_enabled") public static final String THREAD_NETWORK_PRIVILEGED = "android.permission.THREAD_NETWORK_PRIVILEGED";
+ field @FlaggedApi("com.android.net.thread.flags.thread_enabled_platform") public static final String THREAD_NETWORK_PRIVILEGED = "android.permission.THREAD_NETWORK_PRIVILEGED";
field public static final String TIS_EXTENSION_INTERFACE = "android.permission.TIS_EXTENSION_INTERFACE";
field public static final String TOGGLE_AUTOMOTIVE_PROJECTION = "android.permission.TOGGLE_AUTOMOTIVE_PROJECTION";
field public static final String TRIGGER_LOST_MODE = "android.permission.TRIGGER_LOST_MODE";
@@ -400,6 +405,7 @@ package android {
field public static final String USER_ACTIVITY = "android.permission.USER_ACTIVITY";
field @FlaggedApi("android.hardware.biometrics.face_background_authentication") public static final String USE_BACKGROUND_FACE_AUTHENTICATION = "android.permission.USE_BACKGROUND_FACE_AUTHENTICATION";
field public static final String USE_COLORIZED_NOTIFICATIONS = "android.permission.USE_COLORIZED_NOTIFICATIONS";
+ field @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public static final String USE_ON_DEVICE_INTELLIGENCE = "android.permission.USE_ON_DEVICE_INTELLIGENCE";
field public static final String USE_RESERVED_DISK = "android.permission.USE_RESERVED_DISK";
field public static final String UWB_PRIVILEGED = "android.permission.UWB_PRIVILEGED";
field public static final String WHITELIST_AUTO_REVOKE_PERMISSIONS = "android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS";
@@ -592,6 +598,7 @@ package android.app {
field public static final int FOREGROUND_SERVICE_API_TYPE_MICROPHONE = 6; // 0x6
field public static final int FOREGROUND_SERVICE_API_TYPE_PHONE_CALL = 7; // 0x7
field public static final int FOREGROUND_SERVICE_API_TYPE_USB = 8; // 0x8
+ field @FlaggedApi("android.media.audio.foreground_audio_control") public static final int PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL = 64; // 0x40
field public static final int PROCESS_CAPABILITY_FOREGROUND_CAMERA = 2; // 0x2
field public static final int PROCESS_CAPABILITY_FOREGROUND_LOCATION = 1; // 0x1
field public static final int PROCESS_CAPABILITY_FOREGROUND_MICROPHONE = 4; // 0x4
@@ -622,6 +629,8 @@ package android.app {
method public static String[] getOpStrs();
method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, @NonNull String, @Nullable java.lang.String...);
method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getPackagesForOps(@Nullable String[]);
+ method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getPackagesForOps(@Nullable String[], @NonNull String);
+ method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.permission.PermissionGroupUsage> getPermissionGroupUsageForPrivacyIndicator(boolean);
method public static int opToDefaultMode(@NonNull String);
method @Nullable public static String opToPermission(@NonNull String);
method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setMode(@NonNull String, int, @Nullable String, int);
@@ -1137,7 +1146,6 @@ package android.app {
public static final class StatusBarManager.DisableInfo implements android.os.Parcelable {
method public boolean areAllComponentsEnabled();
method public int describeContents();
- method public boolean isBackDisabled();
method public boolean isNavigateToHomeDisabled();
method public boolean isNotificationPeekingDisabled();
method public boolean isRecentsDisabled();
@@ -1286,6 +1294,10 @@ package android.app.admin {
field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.DevicePolicyDrawableResource> CREATOR;
}
+ public final class DevicePolicyIdentifiers {
+ field @FlaggedApi("android.app.admin.flags.security_log_v2_enabled") public static final String AUDIT_LOGGING_POLICY = "auditLogging";
+ }
+
public class DevicePolicyKeyguardService extends android.app.Service {
ctor public DevicePolicyKeyguardService();
method @Nullable public void dismiss();
@@ -1307,18 +1319,21 @@ package android.app.admin {
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.UserHandle getDeviceOwnerUser();
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public android.app.admin.DevicePolicyState getDevicePolicyState();
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public String getFinancedDeviceKioskRoleHolder();
+ method @FlaggedApi("android.app.admin.flags.device_policy_size_tracking_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public int getMaxPolicyStorageLimit();
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_ADMIN_POLICY}) public java.util.List<java.lang.String> getPermittedAccessibilityServices(int);
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_ADMIN_POLICY}) public java.util.List<java.lang.String> getPermittedInputMethodsForCurrentUser();
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public java.util.List<android.os.UserHandle> getPolicyManagedProfiles(@NonNull android.os.UserHandle);
method @Nullable public android.content.ComponentName getProfileOwner() throws java.lang.IllegalArgumentException;
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public String getProfileOwnerNameAsUser(int) throws java.lang.IllegalArgumentException;
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public int getUserProvisioningState();
+ method @FlaggedApi("android.app.admin.flags.security_log_v2_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) public boolean isAuditLogEnabled();
method public boolean isDeviceManaged();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioned();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioningConfigApplied();
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean isDpcDownloaded();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public boolean isManagedKiosk();
method public boolean isSecondaryLockscreenEnabled(@NonNull android.os.UserHandle);
+ method @FlaggedApi("android.app.admin.flags.device_theft_api_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_THEFT_DETECTION) public boolean isTheftDetectionTriggered();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public boolean isUnattendedManagedKiosk();
method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long);
method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long, boolean);
@@ -1327,8 +1342,11 @@ package android.app.admin {
method @RequiresPermission(android.Manifest.permission.TRIGGER_LOST_MODE) public void sendLostModeLocationUpdate(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public boolean setActiveProfileOwner(@NonNull android.content.ComponentName, String) throws java.lang.IllegalArgumentException;
method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_APP_EXEMPTIONS) public void setApplicationExemptions(@NonNull String, @NonNull java.util.Set<java.lang.Integer>) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @FlaggedApi("android.app.admin.flags.security_log_v2_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) public void setAuditLogEnabled(boolean);
+ method @FlaggedApi("android.app.admin.flags.security_log_v2_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) public void setAuditLogEventCallback(@NonNull java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.util.List<android.app.admin.SecurityLog.SecurityEvent>>);
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setDeviceProvisioningConfigApplied();
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setDpcDownloaded(boolean);
+ method @FlaggedApi("android.app.admin.flags.device_policy_size_tracking_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setMaxPolicyStorageLimit(int);
method @Deprecated @RequiresPermission(value=android.Manifest.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS, conditional=true) public void setProfileOwnerCanAccessDeviceIds(@NonNull android.content.ComponentName);
method public void setSecondaryLockscreenEnabled(@NonNull android.content.ComponentName, boolean);
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setUserProvisioningState(int, @NonNull android.os.UserHandle);
@@ -2174,6 +2192,139 @@ package android.app.job {
}
+package android.app.ondeviceintelligence {
+
+ @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public final class Content implements android.os.Parcelable {
+ ctor public Content(@NonNull android.os.Bundle);
+ method public int describeContents();
+ method @NonNull public android.os.Bundle getData();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.ondeviceintelligence.Content> CREATOR;
+ }
+
+ @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public interface DownloadCallback {
+ method public void onDownloadCompleted(@NonNull android.os.PersistableBundle);
+ method public void onDownloadFailed(int, @Nullable String, @NonNull android.os.PersistableBundle);
+ method public default void onDownloadProgress(long);
+ method public default void onDownloadStarted(long);
+ field public static final int DOWNLOAD_FAILURE_STATUS_DOWNLOADING = 3; // 0x3
+ field public static final int DOWNLOAD_FAILURE_STATUS_NETWORK_FAILURE = 2; // 0x2
+ field public static final int DOWNLOAD_FAILURE_STATUS_NOT_ENOUGH_DISK_SPACE = 1; // 0x1
+ field public static final int DOWNLOAD_FAILURE_STATUS_UNAVAILABLE = 4; // 0x4
+ field public static final int DOWNLOAD_FAILURE_STATUS_UNKNOWN = 0; // 0x0
+ }
+
+ @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public final class Feature implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.os.PersistableBundle getFeatureParams();
+ method public int getId();
+ method @Nullable public String getModelName();
+ method @Nullable public String getName();
+ method public int getType();
+ method public int getVariant();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.ondeviceintelligence.Feature> CREATOR;
+ }
+
+ public static final class Feature.Builder {
+ ctor public Feature.Builder(int, int, int, @NonNull android.os.PersistableBundle);
+ method @NonNull public android.app.ondeviceintelligence.Feature build();
+ method @NonNull public android.app.ondeviceintelligence.Feature.Builder setFeatureParams(@NonNull android.os.PersistableBundle);
+ method @NonNull public android.app.ondeviceintelligence.Feature.Builder setId(int);
+ method @NonNull public android.app.ondeviceintelligence.Feature.Builder setModelName(@NonNull String);
+ method @NonNull public android.app.ondeviceintelligence.Feature.Builder setName(@NonNull String);
+ method @NonNull public android.app.ondeviceintelligence.Feature.Builder setType(int);
+ method @NonNull public android.app.ondeviceintelligence.Feature.Builder setVariant(int);
+ }
+
+ @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public final class FeatureDetails implements android.os.Parcelable {
+ ctor public FeatureDetails(@android.app.ondeviceintelligence.FeatureDetails.Status int, @NonNull android.os.PersistableBundle);
+ ctor public FeatureDetails(@android.app.ondeviceintelligence.FeatureDetails.Status int);
+ method public int describeContents();
+ method @NonNull public android.os.PersistableBundle getFeatureDetailParams();
+ method @android.app.ondeviceintelligence.FeatureDetails.Status public int getStatus();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.ondeviceintelligence.FeatureDetails> CREATOR;
+ field public static final int FEATURE_STATUS_AVAILABLE = 3; // 0x3
+ field public static final int FEATURE_STATUS_DOWNLOADABLE = 1; // 0x1
+ field public static final int FEATURE_STATUS_DOWNLOADING = 2; // 0x2
+ field public static final int FEATURE_STATUS_SERVICE_UNAVAILABLE = 4; // 0x4
+ field public static final int FEATURE_STATUS_UNAVAILABLE = 0; // 0x0
+ }
+
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE_USE, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD}) public static @interface FeatureDetails.Status {
+ }
+
+ @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public final class FilePart implements android.os.Parcelable {
+ ctor public FilePart(@NonNull String, @NonNull android.os.PersistableBundle, @NonNull String) throws java.io.FileNotFoundException;
+ ctor public FilePart(@NonNull String, @NonNull android.os.PersistableBundle, @NonNull java.io.FileInputStream) throws java.io.IOException;
+ method public int describeContents();
+ method @NonNull public java.io.FileInputStream getFileInputStream();
+ method @NonNull public String getFilePartKey();
+ method @NonNull public android.os.PersistableBundle getFilePartParams();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.ondeviceintelligence.FilePart> CREATOR;
+ }
+
+ @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public class OnDeviceIntelligenceManager {
+ method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void getFeature(int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.Feature,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException>);
+ method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void getFeatureDetails(@NonNull android.app.ondeviceintelligence.Feature, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.FeatureDetails,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException>);
+ method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void getVersion(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.LongConsumer);
+ method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void listFeatures(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.util.List<android.app.ondeviceintelligence.Feature>,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException>);
+ method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void processRequest(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.app.ondeviceintelligence.Content, int, @Nullable android.os.CancellationSignal, @Nullable android.app.ondeviceintelligence.ProcessingSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.Content,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException>);
+ method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void processRequestStreaming(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.app.ondeviceintelligence.Content, int, @Nullable android.os.CancellationSignal, @Nullable android.app.ondeviceintelligence.ProcessingSignal, @NonNull java.util.concurrent.Executor, @NonNull android.app.ondeviceintelligence.StreamingResponseReceiver<android.app.ondeviceintelligence.Content,android.app.ondeviceintelligence.Content,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException>);
+ method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void requestFeatureDownload(@NonNull android.app.ondeviceintelligence.Feature, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.app.ondeviceintelligence.DownloadCallback);
+ method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void requestTokenCount(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.app.ondeviceintelligence.Content, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Long,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException>);
+ field public static final String API_VERSION_BUNDLE_KEY = "ApiVersionBundleKey";
+ field public static final int REQUEST_TYPE_EMBEDDINGS = 2; // 0x2
+ field public static final int REQUEST_TYPE_INFERENCE = 0; // 0x0
+ field public static final int REQUEST_TYPE_PREPARE = 1; // 0x1
+ }
+
+ public static class OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException extends java.lang.Exception {
+ ctor public OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException(int, @NonNull String, @NonNull android.os.PersistableBundle);
+ ctor public OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException(int, @NonNull android.os.PersistableBundle);
+ method public int getErrorCode();
+ method @NonNull public android.os.PersistableBundle getErrorParams();
+ field public static final int ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE = 1000; // 0x3e8
+ }
+
+ public static class OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException extends android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException {
+ ctor public OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException(int, @NonNull String, @NonNull android.os.PersistableBundle);
+ ctor public OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException(int, @NonNull android.os.PersistableBundle);
+ field public static final int PROCESSING_ERROR_BAD_DATA = 2; // 0x2
+ field public static final int PROCESSING_ERROR_BAD_REQUEST = 3; // 0x3
+ field public static final int PROCESSING_ERROR_BUSY = 9; // 0x9
+ field public static final int PROCESSING_ERROR_CANCELLED = 7; // 0x7
+ field public static final int PROCESSING_ERROR_COMPUTE_ERROR = 5; // 0x5
+ field public static final int PROCESSING_ERROR_INTERNAL = 14; // 0xe
+ field public static final int PROCESSING_ERROR_IPC_ERROR = 6; // 0x6
+ field public static final int PROCESSING_ERROR_NOT_AVAILABLE = 8; // 0x8
+ field public static final int PROCESSING_ERROR_REQUEST_NOT_SAFE = 4; // 0x4
+ field public static final int PROCESSING_ERROR_REQUEST_TOO_LARGE = 12; // 0xc
+ field public static final int PROCESSING_ERROR_RESPONSE_NOT_SAFE = 11; // 0xb
+ field public static final int PROCESSING_ERROR_SAFETY_ERROR = 10; // 0xa
+ field public static final int PROCESSING_ERROR_SERVICE_UNAVAILABLE = 15; // 0xf
+ field public static final int PROCESSING_ERROR_SUSPENDED = 13; // 0xd
+ field public static final int PROCESSING_ERROR_UNKNOWN = 1; // 0x1
+ }
+
+ @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public final class ProcessingSignal {
+ ctor public ProcessingSignal();
+ method public void sendSignal(@NonNull android.os.PersistableBundle);
+ method public void setOnProcessingSignalCallback(@NonNull java.util.concurrent.Executor, @Nullable android.app.ondeviceintelligence.ProcessingSignal.OnProcessingSignalCallback);
+ }
+
+ public static interface ProcessingSignal.OnProcessingSignalCallback {
+ method public void onSignalReceived(@NonNull android.os.PersistableBundle);
+ }
+
+ @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public interface StreamingResponseReceiver<R, T, E extends java.lang.Throwable> extends android.os.OutcomeReceiver<R,E> {
+ method public void onNewContent(@NonNull T);
+ }
+
+}
+
package android.app.people {
public final class PeopleManager {
@@ -3187,10 +3338,12 @@ package android.app.wearable {
public class WearableSensingManager {
method @FlaggedApi("android.app.wearable.enable_data_request_observer_api") @Nullable public static android.app.wearable.WearableSensingDataRequest getDataRequestFromIntent(@NonNull android.content.Intent);
+ method @FlaggedApi("android.app.wearable.enable_provide_wearable_connection_api") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideConnection(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideData(@NonNull android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideDataStream(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
- method @FlaggedApi("android.app.wearable.enable_provide_wearable_connection_api") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void provideWearableConnection(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("android.app.wearable.enable_data_request_observer_api") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void registerDataRequestObserver(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @FlaggedApi("android.app.wearable.enable_hotword_wearable_sensing_api") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void startHotwordRecognition(@Nullable android.content.ComponentName, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @FlaggedApi("android.app.wearable.enable_hotword_wearable_sensing_api") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void stopHotwordRecognition(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("android.app.wearable.enable_data_request_observer_api") @RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE) public void unregisterDataRequestObserver(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
field public static final int STATUS_ACCESS_DENIED = 5; // 0x5
field @FlaggedApi("android.app.wearable.enable_provide_wearable_connection_api") public static final int STATUS_CHANNEL_ERROR = 7; // 0x7
@@ -3619,6 +3772,7 @@ package android.content {
field public static final String NETD_SERVICE = "netd";
field @Deprecated public static final String NETWORK_SCORE_SERVICE = "network_score";
field public static final String OEM_LOCK_SERVICE = "oem_lock";
+ field @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public static final String ON_DEVICE_INTELLIGENCE_SERVICE = "on_device_intelligence";
field public static final String PERMISSION_CONTROLLER_SERVICE = "permission_controller";
field public static final String PERMISSION_SERVICE = "permission";
field public static final String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block";
@@ -3633,7 +3787,7 @@ package android.content {
field public static final String SYSTEM_CONFIG_SERVICE = "system_config";
field public static final String SYSTEM_UPDATE_SERVICE = "system_update";
field public static final String TETHERING_SERVICE = "tethering";
- field @FlaggedApi("com.android.net.thread.flags.thread_enabled") public static final String THREAD_NETWORK_SERVICE = "thread_network";
+ field @FlaggedApi("com.android.net.thread.flags.thread_enabled_platform") public static final String THREAD_NETWORK_SERVICE = "thread_network";
field public static final String TIME_MANAGER_SERVICE = "time_manager";
field public static final String TRANSLATION_MANAGER_SERVICE = "translation";
field public static final String UI_TRANSLATION_SERVICE = "ui_translation";
@@ -3677,6 +3831,7 @@ package android.content {
field public static final String ACTION_INSTANT_APP_RESOLVER_SETTINGS = "android.intent.action.INSTANT_APP_RESOLVER_SETTINGS";
field @Deprecated public static final String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION";
field public static final String ACTION_LOAD_DATA = "android.intent.action.LOAD_DATA";
+ field @FlaggedApi("android.security.frp_enforcement") public static final String ACTION_MAIN_USER_LOCKSCREEN_KNOWLEDGE_FACTOR_CHANGED = "android.intent.action.MAIN_USER_LOCKSCREEN_KNOWLEDGE_FACTOR_CHANGED";
field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_MANAGE_APP_PERMISSION = "android.intent.action.MANAGE_APP_PERMISSION";
field @Deprecated public static final String ACTION_MANAGE_APP_PERMISSIONS = "android.intent.action.MANAGE_APP_PERMISSIONS";
field @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public static final String ACTION_MANAGE_DEFAULT_APP = "android.intent.action.MANAGE_DEFAULT_APP";
@@ -4670,11 +4825,15 @@ package android.hardware {
method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(@NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void addSensorPrivacyListener(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean areAnySensorPrivacyTogglesEnabled(int);
+ method @FlaggedApi("com.android.internal.camera.flags.camera_privacy_allowlist") @NonNull @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public java.util.Map<java.lang.String,java.lang.Boolean> getCameraPrivacyAllowlist();
+ method @FlaggedApi("com.android.internal.camera.flags.camera_privacy_allowlist") @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public int getSensorPrivacyState(int, int);
+ method @FlaggedApi("com.android.internal.camera.flags.camera_privacy_allowlist") @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isCameraPrivacyEnabled(@NonNull String);
method @Deprecated @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isSensorPrivacyEnabled(int);
method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public boolean isSensorPrivacyEnabled(int, int);
method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void removeSensorPrivacyListener(int, @NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
method @RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) public void removeSensorPrivacyListener(@NonNull android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener);
method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setSensorPrivacy(int, boolean);
+ method @FlaggedApi("com.android.internal.camera.flags.camera_privacy_allowlist") @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setSensorPrivacyState(int, int);
}
public static interface SensorPrivacyManager.OnSensorPrivacyChangedListener {
@@ -4684,6 +4843,7 @@ package android.hardware {
public static class SensorPrivacyManager.OnSensorPrivacyChangedListener.SensorPrivacyChangedParams {
method public int getSensor();
+ method @FlaggedApi("com.android.internal.camera.flags.camera_privacy_allowlist") public int getState();
method public int getToggleType();
method public boolean isEnabled();
}
@@ -4744,9 +4904,13 @@ package android.hardware.camera2.extension {
@FlaggedApi("com.android.internal.camera.flags.concert_mode") public final class CameraOutputSurface {
ctor @FlaggedApi("com.android.internal.camera.flags.concert_mode") public CameraOutputSurface(@NonNull android.view.Surface, @NonNull android.util.Size);
+ method @FlaggedApi("com.android.internal.camera.flags.extension_10_bit") public int getColorSpace();
+ method @FlaggedApi("com.android.internal.camera.flags.extension_10_bit") public long getDynamicRangeProfile();
method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public int getImageFormat();
method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public android.util.Size getSize();
method @FlaggedApi("com.android.internal.camera.flags.concert_mode") @NonNull public android.view.Surface getSurface();
+ method @FlaggedApi("com.android.internal.camera.flags.extension_10_bit") public void setColorSpace(int);
+ method @FlaggedApi("com.android.internal.camera.flags.extension_10_bit") public void setDynamicRangeProfile(long);
}
@FlaggedApi("com.android.internal.camera.flags.concert_mode") public class CharacteristicsMap {
@@ -4799,8 +4963,8 @@ package android.hardware.camera2.extension {
}
@FlaggedApi("com.android.internal.camera.flags.concert_mode") public static interface SessionProcessor.CaptureCallback {
- method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureCompleted(long, int, @NonNull android.hardware.camera2.CaptureResult);
- method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureFailed(int);
+ method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureCompleted(long, int, @NonNull java.util.Map<android.hardware.camera2.CaptureResult.Key,java.lang.Object>);
+ method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureFailed(int, int);
method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureProcessStarted(int);
method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureSequenceAborted(int);
method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureSequenceCompleted(int);
@@ -4823,6 +4987,39 @@ package android.hardware.camera2.params {
}
+package android.hardware.devicestate {
+
+ @FlaggedApi("android.hardware.devicestate.feature.flags.device_state_property_api") public final class DeviceState {
+ method @IntRange(from=0x0) public int getIdentifier();
+ method @NonNull public String getName();
+ method public boolean hasProperties(@NonNull int...);
+ method public boolean hasProperty(int);
+ field public static final int PROPERTY_EMULATED_ONLY = 10; // 0xa
+ field public static final int PROPERTY_EXTENDED_DEVICE_STATE_EXTERNAL_DISPLAY = 15; // 0xf
+ field public static final int PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT = 17; // 0x11
+ field public static final int PROPERTY_FEATURE_REAR_DISPLAY = 16; // 0x10
+ field public static final int PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY = 12; // 0xc
+ field public static final int PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY = 11; // 0xb
+ field public static final int PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED = 1; // 0x1
+ field public static final int PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN = 2; // 0x2
+ field public static final int PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN = 3; // 0x3
+ field public static final int PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP = 13; // 0xd
+ field public static final int PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE = 14; // 0xe
+ }
+
+ @FlaggedApi("android.hardware.devicestate.feature.flags.device_state_property_api") public final class DeviceStateManager {
+ method @NonNull public java.util.List<android.hardware.devicestate.DeviceState> getSupportedDeviceStates();
+ method public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback);
+ method public void unregisterCallback(@NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback);
+ }
+
+ public static interface DeviceStateManager.DeviceStateCallback {
+ method public default void onDeviceStateChanged(@NonNull android.hardware.devicestate.DeviceState);
+ method public default void onSupportedStatesChanged(@NonNull java.util.List<android.hardware.devicestate.DeviceState>);
+ }
+
+}
+
package android.hardware.display {
public final class AmbientBrightnessDayStats implements android.os.Parcelable {
@@ -7001,15 +7198,15 @@ package android.media {
@FlaggedApi("android.media.audiopolicy.enable_fade_manager_configuration") public final class FadeManagerConfiguration implements android.os.Parcelable {
method public int describeContents();
method @NonNull public java.util.List<android.media.AudioAttributes> getAudioAttributesWithVolumeShaperConfigs();
- method public static long getDefaultFadeInDurationMillis();
- method public static long getDefaultFadeOutDurationMillis();
- method public long getFadeInDelayForOffenders();
- method public long getFadeInDurationForAudioAttributes(@NonNull android.media.AudioAttributes);
- method public long getFadeInDurationForUsage(int);
+ method @IntRange(from=1) public static long getDefaultFadeInDurationMillis();
+ method @IntRange(from=1) public static long getDefaultFadeOutDurationMillis();
+ method @IntRange(from=0) public long getFadeInDelayForOffenders();
+ method @IntRange(from=0) public long getFadeInDurationForAudioAttributes(@NonNull android.media.AudioAttributes);
+ method @IntRange(from=0) public long getFadeInDurationForUsage(int);
method @Nullable public android.media.VolumeShaper.Configuration getFadeInVolumeShaperConfigForAudioAttributes(@NonNull android.media.AudioAttributes);
method @Nullable public android.media.VolumeShaper.Configuration getFadeInVolumeShaperConfigForUsage(int);
- method public long getFadeOutDurationForAudioAttributes(@NonNull android.media.AudioAttributes);
- method public long getFadeOutDurationForUsage(int);
+ method @IntRange(from=0) public long getFadeOutDurationForAudioAttributes(@NonNull android.media.AudioAttributes);
+ method @IntRange(from=0) public long getFadeOutDurationForUsage(int);
method @Nullable public android.media.VolumeShaper.Configuration getFadeOutVolumeShaperConfigForAudioAttributes(@NonNull android.media.AudioAttributes);
method @Nullable public android.media.VolumeShaper.Configuration getFadeOutVolumeShaperConfigForUsage(int);
method public int getFadeState();
@@ -7035,7 +7232,7 @@ package android.media {
public static final class FadeManagerConfiguration.Builder {
ctor public FadeManagerConfiguration.Builder();
- ctor public FadeManagerConfiguration.Builder(long, long);
+ ctor public FadeManagerConfiguration.Builder(@IntRange(from=1) long, @IntRange(from=1) long);
ctor public FadeManagerConfiguration.Builder(@NonNull android.media.FadeManagerConfiguration);
method @NonNull public android.media.FadeManagerConfiguration.Builder addFadeableUsage(int);
method @NonNull public android.media.FadeManagerConfiguration.Builder addUnfadeableAudioAttributes(@NonNull android.media.AudioAttributes);
@@ -7046,13 +7243,13 @@ package android.media {
method @NonNull public android.media.FadeManagerConfiguration.Builder clearUnfadeableAudioAttributes();
method @NonNull public android.media.FadeManagerConfiguration.Builder clearUnfadeableContentTypes();
method @NonNull public android.media.FadeManagerConfiguration.Builder clearUnfadeableUids();
- method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInDelayForOffenders(long);
- method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInDurationForAudioAttributes(@NonNull android.media.AudioAttributes, long);
- method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInDurationForUsage(int, long);
+ method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInDelayForOffenders(@IntRange(from=0) long);
+ method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInDurationForAudioAttributes(@NonNull android.media.AudioAttributes, @IntRange(from=0) long);
+ method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInDurationForUsage(int, @IntRange(from=0) long);
method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInVolumeShaperConfigForAudioAttributes(@NonNull android.media.AudioAttributes, @Nullable android.media.VolumeShaper.Configuration);
method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeInVolumeShaperConfigForUsage(int, @Nullable android.media.VolumeShaper.Configuration);
- method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeOutDurationForAudioAttributes(@NonNull android.media.AudioAttributes, long);
- method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeOutDurationForUsage(int, long);
+ method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeOutDurationForAudioAttributes(@NonNull android.media.AudioAttributes, @IntRange(from=0) long);
+ method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeOutDurationForUsage(int, @IntRange(from=0) long);
method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeOutVolumeShaperConfigForAudioAttributes(@NonNull android.media.AudioAttributes, @Nullable android.media.VolumeShaper.Configuration);
method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeOutVolumeShaperConfigForUsage(int, @Nullable android.media.VolumeShaper.Configuration);
method @NonNull public android.media.FadeManagerConfiguration.Builder setFadeState(int);
@@ -7097,7 +7294,6 @@ package android.media {
public final class MediaRouter2 {
method @NonNull public java.util.List<android.media.MediaRoute2Info> getAllRoutes();
method @Nullable public String getClientPackageName();
- method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void setRouteVolume(@NonNull android.media.MediaRoute2Info, int);
method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void startScan();
method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void stopScan();
method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void transfer(@NonNull android.media.MediaRouter2.RoutingController, @NonNull android.media.MediaRoute2Info);
@@ -10225,8 +10421,8 @@ package android.nfc.cardemulation {
@FlaggedApi("android.nfc.enable_nfc_mainline") public final class ApduServiceInfo implements android.os.Parcelable {
ctor @FlaggedApi("android.nfc.enable_nfc_mainline") public ApduServiceInfo(@NonNull android.content.pm.PackageManager, @NonNull android.content.pm.ResolveInfo, boolean) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
- method @FlaggedApi("android.nfc.nfc_read_polling_loop") public void addPollingLoopFilter(@NonNull String);
- method @FlaggedApi("android.nfc.nfc_read_polling_loop") public void addPollingLoopFilterToAutoTransact(@NonNull String);
+ method @FlaggedApi("android.nfc.nfc_read_polling_loop") public void addPollingLoopFilter(@NonNull String, boolean);
+ method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean defaultToObserveMode();
method @FlaggedApi("android.nfc.enable_nfc_mainline") public int describeContents();
method @FlaggedApi("android.nfc.enable_nfc_mainline") public void dump(@NonNull android.os.ParcelFileDescriptor, @NonNull java.io.PrintWriter, @NonNull String[]);
method @FlaggedApi("android.nfc.enable_nfc_mainline") public void dumpDebug(@NonNull android.util.proto.ProtoOutputStream);
@@ -10256,6 +10452,7 @@ package android.nfc.cardemulation {
method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean requiresUnlock();
method @FlaggedApi("android.nfc.enable_nfc_mainline") public void resetOffHostSecureElement();
method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setCategoryOtherServiceEnabled(boolean);
+ method @FlaggedApi("android.nfc.nfc_observe_mode") public void setDefaultToObserveMode(boolean);
method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setDynamicAidGroup(@NonNull android.nfc.cardemulation.AidGroup);
method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setOffHostSecureElement(@NonNull String);
method @FlaggedApi("android.nfc.enable_nfc_mainline") public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -10430,9 +10627,11 @@ package android.os {
method public int getFlags();
method public int getMode();
field public static final int BUGREPORT_FLAG_DEFER_CONSENT = 2; // 0x2
+ field @FlaggedApi("android.app.admin.flags.onboarding_bugreport_v2_enabled") public static final int BUGREPORT_FLAG_KEEP_BUGREPORT_ON_RETRIEVAL = 4; // 0x4
field public static final int BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA = 1; // 0x1
field public static final int BUGREPORT_MODE_FULL = 0; // 0x0
field public static final int BUGREPORT_MODE_INTERACTIVE = 1; // 0x1
+ field @FlaggedApi("android.app.admin.flags.onboarding_bugreport_v2_enabled") public static final int BUGREPORT_MODE_ONBOARDING = 7; // 0x7
field public static final int BUGREPORT_MODE_REMOTE = 2; // 0x2
field public static final int BUGREPORT_MODE_TELEPHONY = 4; // 0x4
field public static final int BUGREPORT_MODE_WEAR = 3; // 0x3
@@ -11082,9 +11281,6 @@ package android.os {
field public static final String USER_TYPE_FULL_GUEST = "android.os.usertype.full.GUEST";
field public static final String USER_TYPE_FULL_SECONDARY = "android.os.usertype.full.SECONDARY";
field public static final String USER_TYPE_FULL_SYSTEM = "android.os.usertype.full.SYSTEM";
- field public static final String USER_TYPE_PROFILE_CLONE = "android.os.usertype.profile.CLONE";
- field public static final String USER_TYPE_PROFILE_MANAGED = "android.os.usertype.profile.MANAGED";
- field @FlaggedApi("android.os.allow_private_profile") public static final String USER_TYPE_PROFILE_PRIVATE = "android.os.usertype.profile.PRIVATE";
field public static final String USER_TYPE_SYSTEM_HEADLESS = "android.os.usertype.system.HEADLESS";
}
@@ -11322,6 +11518,7 @@ package android.permission {
method public long getLastAccessTimeMillis();
method @NonNull public String getPackageName();
method @NonNull public String getPermissionGroupName();
+ method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") @NonNull public String getPersistentDeviceId();
method @Nullable public CharSequence getProxyLabel();
method public int getUid();
method public boolean isActive();
@@ -11332,7 +11529,7 @@ package android.permission {
public final class PermissionManager {
method public int checkDeviceIdentifierAccess(@Nullable String, @Nullable String, @Nullable String, int, int);
- method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") public static int checkPermission(@NonNull String, @NonNull String, @NonNull String, int);
+ method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") public int checkPermission(@NonNull String, @NonNull String, @NonNull String);
method @RequiresPermission(value=android.Manifest.permission.UPDATE_APP_OPS_STATS, conditional=true) public int checkPermissionForDataDelivery(@NonNull String, @NonNull android.content.AttributionSource, @Nullable String);
method @RequiresPermission(value=android.Manifest.permission.UPDATE_APP_OPS_STATS, conditional=true) public int checkPermissionForDataDeliveryFromDataSource(@NonNull String, @NonNull android.content.AttributionSource, @Nullable String);
method public int checkPermissionForPreflight(@NonNull String, @NonNull android.content.AttributionSource);
@@ -12299,14 +12496,6 @@ package android.service.carrier {
}
-package android.service.chooser {
-
- @FlaggedApi("android.service.chooser.support_nfc_resolver") public class CustomChoosers {
- method @FlaggedApi("android.service.chooser.support_nfc_resolver") @NonNull public static android.content.Intent createNfcResolverIntent(@NonNull android.content.Intent, @Nullable CharSequence, @NonNull java.util.List<android.content.pm.ResolveInfo>);
- }
-
-}
-
package android.service.cloudsearch {
public abstract class CloudSearchService extends android.app.Service {
@@ -12767,6 +12956,34 @@ package android.service.oemlock {
}
+package android.service.ondeviceintelligence {
+
+ @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public abstract class OnDeviceIntelligenceService extends android.app.Service {
+ ctor public OnDeviceIntelligenceService();
+ method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
+ method public abstract void onDownloadFeature(@NonNull android.app.ondeviceintelligence.Feature, @Nullable android.os.CancellationSignal, @NonNull android.app.ondeviceintelligence.DownloadCallback);
+ method public abstract void onGetFeature(int, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.Feature,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException>);
+ method public abstract void onGetFeatureDetails(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.FeatureDetails,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException>);
+ method public abstract void onGetReadOnlyFeatureFileDescriptorMap(@NonNull android.app.ondeviceintelligence.Feature, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,android.os.ParcelFileDescriptor>>);
+ method public abstract void onGetVersion(@NonNull java.util.function.LongConsumer);
+ method public abstract void onListFeatures(@NonNull android.os.OutcomeReceiver<java.util.List<android.app.ondeviceintelligence.Feature>,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException>);
+ field public static final String SERVICE_INTERFACE = "android.service.ondeviceintelligence.OnDeviceIntelligenceService";
+ }
+
+ @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public abstract class OnDeviceTrustedInferenceService extends android.app.Service {
+ ctor public OnDeviceTrustedInferenceService();
+ method public final void fetchFeatureFileInputStreamMap(@NonNull android.app.ondeviceintelligence.Feature, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,java.io.FileInputStream>>);
+ method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
+ method @NonNull public abstract void onCountTokens(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.app.ondeviceintelligence.Content, @Nullable android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<java.lang.Long,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException>);
+ method @NonNull public abstract void onProcessRequest(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.app.ondeviceintelligence.Content, int, @Nullable android.os.CancellationSignal, @Nullable android.app.ondeviceintelligence.ProcessingSignal, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.Content,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException>);
+ method @NonNull public abstract void onProcessRequestStreaming(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.app.ondeviceintelligence.Content, int, @Nullable android.os.CancellationSignal, @Nullable android.app.ondeviceintelligence.ProcessingSignal, @NonNull android.app.ondeviceintelligence.StreamingResponseReceiver<android.app.ondeviceintelligence.Content,android.app.ondeviceintelligence.Content,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException>);
+ method public final java.io.FileInputStream openFileInput(@NonNull String) throws java.io.FileNotFoundException;
+ method public final void openFileInputAsync(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.io.FileInputStream>) throws java.io.FileNotFoundException;
+ field public static final String SERVICE_INTERFACE = "android.service.ondeviceintelligence.OnDeviceTrustedInferenceService";
+ }
+
+}
+
package android.service.persistentdata {
@FlaggedApi("android.security.frp_enforcement") public class PersistentDataBlockManager {
@@ -13188,6 +13405,7 @@ package android.service.voice {
method @Nullable public android.service.voice.HotwordDetectedResult getHotwordDetectedResult();
method @NonNull public java.util.List<android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra> getKeyphraseRecognitionExtras();
method @Deprecated @Nullable public byte[] getTriggerAudio();
+ method @FlaggedApi("android.app.wearable.enable_hotword_wearable_sensing_api") public boolean isRecognitionStopped();
field public static final int DATA_FORMAT_RAW = 0; // 0x0
field public static final int DATA_FORMAT_TRIGGER_AUDIO = 1; // 0x1
}
@@ -13294,6 +13512,7 @@ package android.service.voice {
method public void onUpdateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, long, @Nullable java.util.function.IntConsumer);
field @Deprecated public static final int INITIALIZATION_STATUS_SUCCESS = 0; // 0x0
field @Deprecated public static final int INITIALIZATION_STATUS_UNKNOWN = 100; // 0x64
+ field @FlaggedApi("android.app.wearable.enable_hotword_wearable_sensing_api") public static final String KEY_SYSTEM_WILL_CLOSE_AUDIO_STREAM_AFTER_CALLBACK = "android.service.voice.HotwordDetectionService.KEY_SYSTEM_WILL_CLOSE_AUDIO_STREAM_AFTER_CALLBACK";
field public static final String SERVICE_INTERFACE = "android.service.voice.HotwordDetectionService";
}
@@ -13358,43 +13577,6 @@ package android.service.voice {
method @NonNull public android.service.voice.HotwordRejectedResult.Builder setConfidenceLevel(int);
}
- @FlaggedApi("android.service.voice.flags.allow_training_data_egress_from_hds") public final class HotwordTrainingAudio implements android.os.Parcelable {
- method public int describeContents();
- method @NonNull public android.media.AudioFormat getAudioFormat();
- method @NonNull public int getAudioType();
- method @NonNull public byte[] getHotwordAudio();
- method public int getHotwordOffsetMillis();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.service.voice.HotwordTrainingAudio> CREATOR;
- field public static final int HOTWORD_OFFSET_UNSET = -1; // 0xffffffff
- }
-
- @FlaggedApi("android.service.voice.flags.allow_training_data_egress_from_hds") public static final class HotwordTrainingAudio.Builder {
- ctor public HotwordTrainingAudio.Builder(@NonNull byte[], @NonNull android.media.AudioFormat);
- method @NonNull public android.service.voice.HotwordTrainingAudio build();
- method @NonNull public android.service.voice.HotwordTrainingAudio.Builder setAudioFormat(@NonNull android.media.AudioFormat);
- method @NonNull public android.service.voice.HotwordTrainingAudio.Builder setAudioType(@NonNull int);
- method @NonNull public android.service.voice.HotwordTrainingAudio.Builder setHotwordAudio(@NonNull byte[]);
- method @NonNull public android.service.voice.HotwordTrainingAudio.Builder setHotwordOffsetMillis(int);
- }
-
- @FlaggedApi("android.service.voice.flags.allow_training_data_egress_from_hds") public final class HotwordTrainingData implements android.os.Parcelable {
- method public int describeContents();
- method public static int getMaxTrainingDataBytes();
- method public int getTimeoutStage();
- method @NonNull public java.util.List<android.service.voice.HotwordTrainingAudio> getTrainingAudioList();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.service.voice.HotwordTrainingData> CREATOR;
- }
-
- @FlaggedApi("android.service.voice.flags.allow_training_data_egress_from_hds") public static final class HotwordTrainingData.Builder {
- ctor public HotwordTrainingData.Builder();
- method @NonNull public android.service.voice.HotwordTrainingData.Builder addTrainingAudio(@NonNull android.service.voice.HotwordTrainingAudio);
- method @NonNull public android.service.voice.HotwordTrainingData build();
- method @NonNull public android.service.voice.HotwordTrainingData.Builder setTimeoutStage(int);
- method @NonNull public android.service.voice.HotwordTrainingData.Builder setTrainingAudioList(@NonNull java.util.List<android.service.voice.HotwordTrainingAudio>);
- }
-
public interface SandboxedDetectionInitializer {
method public static int getMaxCustomInitializationStatus();
method public void onUpdateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, long, @Nullable java.util.function.IntConsumer);
@@ -13434,6 +13616,7 @@ package android.service.voice {
@FlaggedApi("android.service.voice.flags.allow_complex_results_egress_from_vqds") public final class VisualQueryDetectedResult implements android.os.Parcelable {
method public int describeContents();
+ method @Nullable public byte[] getAccessibilityDetectionData();
method public static int getMaxSpeakerId();
method @NonNull public String getPartialQuery();
method public int getSpeakerId();
@@ -13444,6 +13627,7 @@ package android.service.voice {
public static final class VisualQueryDetectedResult.Builder {
ctor public VisualQueryDetectedResult.Builder();
method @NonNull public android.service.voice.VisualQueryDetectedResult build();
+ method @NonNull public android.service.voice.VisualQueryDetectedResult.Builder setAccessibilityDetectionData(@NonNull byte...);
method @NonNull public android.service.voice.VisualQueryDetectedResult.Builder setPartialQuery(@NonNull String);
method @NonNull public android.service.voice.VisualQueryDetectedResult.Builder setSpeakerId(int);
}
@@ -13481,7 +13665,10 @@ package android.service.voice {
}
public class VisualQueryDetector {
+ method @FlaggedApi("android.service.voice.flags.allow_complex_results_egress_from_vqds") public void clearAccessibilityDetectionEnabledListener();
method public void destroy();
+ method @FlaggedApi("android.service.voice.flags.allow_complex_results_egress_from_vqds") public boolean isAccessibilityDetectionEnabled();
+ method @FlaggedApi("android.service.voice.flags.allow_complex_results_egress_from_vqds") public void setAccessibilityDetectionEnabledListener(@NonNull java.util.function.Consumer<java.lang.Boolean>);
method @RequiresPermission(allOf={android.Manifest.permission.CAMERA, android.Manifest.permission.RECORD_AUDIO}) public boolean startRecognition();
method @RequiresPermission(allOf={android.Manifest.permission.CAMERA, android.Manifest.permission.RECORD_AUDIO}) public boolean stopRecognition();
method public void updateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory);
@@ -13576,9 +13763,13 @@ package android.service.wearable {
method @FlaggedApi("android.app.wearable.enable_data_request_observer_api") @BinderThread public void onDataRequestObserverUnregistered(int, @NonNull String, @NonNull android.service.wearable.WearableSensingDataRequester, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @BinderThread public abstract void onDataStreamProvided(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @BinderThread public abstract void onQueryServiceStatus(@NonNull java.util.Set<java.lang.Integer>, @NonNull String, @NonNull java.util.function.Consumer<android.service.ambientcontext.AmbientContextDetectionServiceStatus>);
- method @FlaggedApi("android.app.wearable.enable_provide_wearable_connection_api") @BinderThread public void onSecureWearableConnectionProvided(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @FlaggedApi("android.app.wearable.enable_provide_wearable_connection_api") @BinderThread public void onSecureConnectionProvided(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @BinderThread public abstract void onStartDetection(@NonNull android.app.ambientcontext.AmbientContextEventRequest, @NonNull String, @NonNull java.util.function.Consumer<android.service.ambientcontext.AmbientContextDetectionServiceStatus>, @NonNull java.util.function.Consumer<android.service.ambientcontext.AmbientContextDetectionResult>);
+ method @FlaggedApi("android.app.wearable.enable_hotword_wearable_sensing_api") @BinderThread public void onStartHotwordRecognition(@NonNull java.util.function.Consumer<android.service.voice.HotwordAudioStream>, @NonNull java.util.function.Consumer<java.lang.Integer>);
method public abstract void onStopDetection(@NonNull String);
+ method @FlaggedApi("android.app.wearable.enable_hotword_wearable_sensing_api") @BinderThread public void onStopHotwordAudioStream();
+ method @FlaggedApi("android.app.wearable.enable_hotword_wearable_sensing_api") @BinderThread public void onStopHotwordRecognition(@NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @FlaggedApi("android.app.wearable.enable_hotword_wearable_sensing_api") @BinderThread public void onValidatedByHotwordDetectionService();
field public static final String SERVICE_INTERFACE = "android.service.wearable.WearableSensingService";
}
@@ -13753,12 +13944,24 @@ package android.telecom {
}
public final class DisconnectCause implements android.os.Parcelable {
- ctor @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") public DisconnectCause(int, @NonNull CharSequence, @NonNull CharSequence, @NonNull String, int, int, int, @Nullable android.telephony.ims.ImsReasonInfo);
method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") @Nullable public android.telephony.ims.ImsReasonInfo getImsReasonInfo();
method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") public int getTelephonyDisconnectCause();
method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") public int getTelephonyPreciseDisconnectCause();
}
+ @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") public static final class DisconnectCause.Builder {
+ ctor public DisconnectCause.Builder();
+ method @NonNull public android.telecom.DisconnectCause build();
+ method @NonNull public android.telecom.DisconnectCause.Builder setCode(int);
+ method @NonNull public android.telecom.DisconnectCause.Builder setDescription(@Nullable CharSequence);
+ method @NonNull public android.telecom.DisconnectCause.Builder setImsReasonInfo(@Nullable android.telephony.ims.ImsReasonInfo);
+ method @NonNull public android.telecom.DisconnectCause.Builder setLabel(@Nullable CharSequence);
+ method @NonNull public android.telecom.DisconnectCause.Builder setReason(@NonNull String);
+ method @NonNull public android.telecom.DisconnectCause.Builder setTelephonyDisconnectCause(int);
+ method @NonNull public android.telecom.DisconnectCause.Builder setTelephonyPreciseDisconnectCause(int);
+ method @NonNull public android.telecom.DisconnectCause.Builder setTone(int);
+ }
+
public abstract class InCallService extends android.app.Service {
method @Deprecated public android.telecom.Phone getPhone();
method @Deprecated public void onPhoneCreated(android.telecom.Phone);
@@ -13999,7 +14202,8 @@ package android.telecom {
method @Deprecated public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsForPackage();
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsSupportingScheme(String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isInEmergencyCall();
- method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") @RequiresPermission(allOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isInSelfManagedCall(@NonNull String, @NonNull android.os.UserHandle, boolean);
+ method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") @RequiresPermission(allOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isInSelfManagedCall(@NonNull String, @NonNull android.os.UserHandle);
+ method @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") @RequiresPermission(allOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isInSelfManagedCall(@NonNull String, boolean);
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUserSelectedOutgoingPhoneAccount(@Nullable android.telecom.PhoneAccountHandle);
field public static final String ACTION_CURRENT_TTY_MODE_CHANGED = "android.telecom.action.CURRENT_TTY_MODE_CHANGED";
@@ -14334,9 +14538,9 @@ package android.telephony {
@FlaggedApi("com.android.internal.telephony.flags.use_oem_domain_selection_service") public abstract class DomainSelectionService extends android.app.Service {
ctor public DomainSelectionService();
+ method @NonNull public java.util.concurrent.Executor getCreateExecutor();
method public void onBarringInfoUpdated(int, int, @NonNull android.telephony.BarringInfo);
method @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent);
- method @NonNull public java.util.concurrent.Executor onCreateExecutor();
method public abstract void onDomainSelection(@NonNull android.telephony.DomainSelectionService.SelectionAttributes, @NonNull android.telephony.TransportSelectorCallback);
method public void onServiceStateUpdated(int, int, @NonNull android.telephony.ServiceState);
field public static final int SCAN_TYPE_FULL_SERVICE = 2; // 0x2
@@ -15116,6 +15320,7 @@ package android.telephony {
method @Deprecated public boolean getDataEnabled(int);
method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.ComponentName getDefaultRespondViaMessageApplication();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getDeviceSoftwareVersion(int);
+ method @FlaggedApi("android.permission.flags.get_emergency_role_holder_api_enabled") @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getEmergencyAssistancePackage();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getEmergencyCallbackMode();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEmergencyNumberDbVersion();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimDomain();
diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt
index a6505c8b6093..e7a0ff4830fd 100644
--- a/core/api/system-lint-baseline.txt
+++ b/core/api/system-lint-baseline.txt
@@ -509,6 +509,16 @@ GenericException: android.service.autofill.augmented.FillWindow#finalize():
Methods must not throw generic exceptions (`java.lang.Throwable`)
+HiddenAbstractMethod: android.app.Notification.Style#areNotificationsVisiblyDifferent(android.app.Notification.Style):
+ areNotificationsVisiblyDifferent cannot be hidden and abstract when Style has a visible constructor, in case a third-party attempts to subclass it.
+
+
+InvalidNullabilityOverride: android.service.ondeviceintelligence.OnDeviceIntelligenceService#onBind(android.content.Intent) parameter #0:
+ Invalid nullability on parameter `intent` in method `onBind`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
+InvalidNullabilityOverride: android.service.ondeviceintelligence.OnDeviceTrustedInferenceService#onBind(android.content.Intent) parameter #0:
+ Invalid nullability on parameter `intent` in method `onBind`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
+InvalidNullabilityOverride: android.service.ondeviceintelligence.OnDeviceTrustedInferenceService#openFileInput(String) parameter #0:
+ Invalid nullability on parameter `filename` in method `openFileInput`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
InvalidNullabilityOverride: android.service.textclassifier.TextClassifierService#onUnbind(android.content.Intent) parameter #0:
Invalid nullability on parameter `intent` in method `onUnbind`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
InvalidNullabilityOverride: android.service.voice.HotwordDetectionService#getSystemService(String) parameter #0:
@@ -535,6 +545,10 @@ ListenerLast: android.telephony.satellite.SatelliteManager#stopSatelliteTransmis
Listeners should always be at end of argument list (method `stopSatelliteTransmissionUpdates`)
+MethodNameUnits: android.hardware.camera2.extension.CameraOutputSurface#getColorSpace():
+ Expected method name units to be `Bytes`, was `Space` in `getColorSpace`
+
+
MissingGetterMatchingBuilder: android.service.voice.HotwordTrainingData.Builder#addTrainingAudio(android.service.voice.HotwordTrainingAudio):
android.service.voice.HotwordTrainingData does not declare a `getTrainingAudios()` method matching method android.service.voice.HotwordTrainingData.Builder.addTrainingAudio(android.service.voice.HotwordTrainingAudio)
MissingGetterMatchingBuilder: android.telecom.CallScreeningService.CallResponse.Builder#setShouldScreenCallViaAudioProcessing(boolean):
@@ -561,6 +575,8 @@ MissingNullability: android.service.contentcapture.ContentCaptureService#dump(ja
Missing nullability on parameter `args` in method `dump`
MissingNullability: android.service.notification.NotificationAssistantService#attachBaseContext(android.content.Context) parameter #0:
Missing nullability on parameter `base` in method `attachBaseContext`
+MissingNullability: android.service.ondeviceintelligence.OnDeviceTrustedInferenceService#openFileInput(String):
+ Missing nullability on method `openFileInput` return
MissingNullability: android.telephony.NetworkService#onUnbind(android.content.Intent) parameter #0:
Missing nullability on parameter `intent` in method `onUnbind`
MissingNullability: android.telephony.data.DataService#onUnbind(android.content.Intent) parameter #0:
@@ -1873,6 +1889,8 @@ Todo: android.Manifest.permission#READ_PEOPLE_DATA:
Documentation mentions 'TODO'
Todo: android.app.NotificationManager#isNotificationAssistantAccessGranted(android.content.ComponentName):
Documentation mentions 'TODO'
+Todo: android.app.ondeviceintelligence.OnDeviceIntelligenceManager#requestFeatureDownload(android.app.ondeviceintelligence.Feature, android.app.ondeviceintelligence.CancellationSignal, java.util.concurrent.Executor, android.app.ondeviceintelligence.DownloadCallback):
+ Documentation mentions 'TODO'
Todo: android.hardware.camera2.params.StreamConfigurationMap:
Documentation mentions 'TODO'
Todo: android.hardware.location.ContextHubManager#getNanoAppInstanceInfo(int):
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index cd84c8455439..cdf232cd71ee 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1032,7 +1032,10 @@ package android.content {
}
public class Intent implements java.lang.Cloneable android.os.Parcelable {
+ method @NonNull public android.content.Intent addExtendedFlags(int);
+ method public int getExtendedFlags();
field public static final String ACTION_USER_STOPPED = "android.intent.action.USER_STOPPED";
+ field public static final int EXTENDED_FLAG_FILTER_MISMATCH = 1; // 0x1
}
public class SyncAdapterType implements android.os.Parcelable {
@@ -1519,6 +1522,7 @@ package android.hardware {
public final class SensorPrivacyManager {
method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setSensorPrivacy(int, int, boolean);
+ method @FlaggedApi("com.android.internal.camera.flags.camera_privacy_allowlist") @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setSensorPrivacyState(int, int, int);
}
public static class SensorPrivacyManager.Sources {
@@ -1547,6 +1551,7 @@ package android.hardware.biometrics {
public static class BiometricPrompt.Builder {
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.TEST_BIOMETRIC, "android.permission.USE_BIOMETRIC_INTERNAL"}) public android.hardware.biometrics.BiometricPrompt.Builder setAllowBackgroundAuthentication(boolean);
+ method @FlaggedApi("android.multiuser.enable_biometrics_to_unlock_private_space") @NonNull @RequiresPermission(anyOf={android.Manifest.permission.TEST_BIOMETRIC, "android.permission.USE_BIOMETRIC_INTERNAL"}) public android.hardware.biometrics.BiometricPrompt.Builder setAllowBackgroundAuthentication(boolean, boolean);
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.TEST_BIOMETRIC, "android.permission.USE_BIOMETRIC_INTERNAL"}) public android.hardware.biometrics.BiometricPrompt.Builder setAllowedSensorIds(@NonNull java.util.List<java.lang.Integer>);
}
@@ -1619,22 +1624,26 @@ package android.hardware.camera2.params {
package android.hardware.devicestate {
- public final class DeviceStateManager {
+ @FlaggedApi("android.hardware.devicestate.feature.flags.device_state_property_api") public final class DeviceState {
+ ctor @Deprecated public DeviceState(@IntRange(from=android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE_IDENTIFIER, to=android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE_IDENTIFIER) int, @NonNull String, int);
+ ctor public DeviceState(@IntRange(from=android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE_IDENTIFIER, to=android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE_IDENTIFIER) int, @NonNull String, @NonNull java.util.Set<java.lang.Integer>);
+ field public static final int PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST = 8; // 0x8
+ }
+
+ @FlaggedApi("android.hardware.devicestate.feature.flags.device_state_property_api") public final class DeviceStateManager {
method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void cancelBaseStateOverride();
method @RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true) public void cancelStateRequest();
- method @NonNull public int[] getSupportedStates();
- method public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback);
+ method @Deprecated @NonNull public int[] getSupportedStates();
method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void requestBaseStateOverride(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback);
method @RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true) public void requestState(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback);
- method public void unregisterCallback(@NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback);
- field public static final int MAXIMUM_DEVICE_STATE = 255; // 0xff
- field public static final int MINIMUM_DEVICE_STATE = 0; // 0x0
+ field public static final int MAXIMUM_DEVICE_STATE_IDENTIFIER = 10000; // 0x2710
+ field public static final int MINIMUM_DEVICE_STATE_IDENTIFIER = 0; // 0x0
}
public static interface DeviceStateManager.DeviceStateCallback {
- method public default void onBaseStateChanged(int);
- method public void onStateChanged(int);
- method public default void onSupportedStatesChanged(@NonNull int[]);
+ method @Deprecated public default void onBaseStateChanged(int);
+ method @Deprecated public void onStateChanged(int);
+ method @Deprecated public default void onSupportedStatesChanged(@NonNull int[]);
}
public final class DeviceStateRequest {
@@ -1697,6 +1706,7 @@ package android.hardware.display {
field public static final int SWITCHING_TYPE_WITHIN_GROUPS = 1; // 0x1
field public static final int VIRTUAL_DISPLAY_FLAG_OWN_FOCUS = 16384; // 0x4000
field public static final int VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 512; // 0x200
+ field public static final int VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH = 64; // 0x40
}
}
@@ -1754,6 +1764,10 @@ package android.hardware.input {
field public static final int DEFAULT_POINTER_SPEED = 0; // 0x0
}
+ public class VirtualKeyboard implements java.io.Closeable {
+ method public int getInputDeviceId();
+ }
+
}
package android.hardware.lights {
@@ -2277,9 +2291,7 @@ package android.os {
}
public final class BugreportParams {
- field @FlaggedApi("android.app.admin.flags.onboarding_bugreport_v2_enabled") public static final int BUGREPORT_FLAG_KEEP_BUGREPORT_ON_RETRIEVAL = 4; // 0x4
field @FlaggedApi("android.os.bugreport_mode_max_value") public static final int BUGREPORT_MODE_MAX_VALUE = 7; // 0x7
- field @FlaggedApi("android.app.admin.flags.onboarding_bugreport_v2_enabled") public static final int BUGREPORT_MODE_ONBOARDING = 7; // 0x7
}
public class Build {
@@ -3063,6 +3075,14 @@ package android.service.autofill.augmented {
}
+package android.service.chooser {
+
+ @FlaggedApi("android.service.chooser.enable_chooser_result") public final class ChooserResult implements android.os.Parcelable {
+ ctor public ChooserResult(int, @Nullable android.content.ComponentName, boolean);
+ }
+
+}
+
package android.service.dreams {
public abstract class DreamOverlayService extends android.app.Service {
@@ -3172,6 +3192,7 @@ package android.service.voice {
method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload.Builder setDataFormat(int);
method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload.Builder setHalEventReceivedMillis(long);
method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload.Builder setHotwordDetectedResult(@NonNull android.service.voice.HotwordDetectedResult);
+ method @FlaggedApi("android.app.wearable.enable_hotword_wearable_sensing_api") @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload.Builder setIsRecognitionStopped(boolean);
method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload.Builder setKeyphraseRecognitionExtras(@NonNull java.util.List<android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra>);
}
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index c1181f5b8233..fc4956508cff 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -511,6 +511,10 @@ DeprecationMismatch: javax.microedition.khronos.egl.EGL10#eglCreatePixmapSurface
Method javax.microedition.khronos.egl.EGL10.eglCreatePixmapSurface(javax.microedition.khronos.egl.EGLDisplay, javax.microedition.khronos.egl.EGLConfig, Object, int[]): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
+HiddenAbstractMethod: android.app.Notification.Style#areNotificationsVisiblyDifferent(android.app.Notification.Style):
+ areNotificationsVisiblyDifferent cannot be hidden and abstract when Style has a visible constructor, in case a third-party attempts to subclass it.
+
+
InvalidNullabilityOverride: android.window.WindowProviderService#getSystemService(String) parameter #0:
Invalid nullability on parameter `name` in method `getSystemService`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
InvalidNullabilityOverride: android.window.WindowProviderService#onConfigurationChanged(android.content.res.Configuration) parameter #0:
@@ -1931,6 +1935,8 @@ Todo: android.Manifest.permission#READ_PEOPLE_DATA:
Documentation mentions 'TODO'
Todo: android.app.NotificationManager#isNotificationAssistantAccessGranted(android.content.ComponentName):
Documentation mentions 'TODO'
+Todo: android.app.ondeviceintelligence.OnDeviceIntelligenceManager#requestFeatureDownload(android.app.ondeviceintelligence.Feature, android.app.ondeviceintelligence.CancellationSignal, java.util.concurrent.Executor, android.app.ondeviceintelligence.DownloadCallback):
+ Documentation mentions 'TODO'
Todo: android.hardware.camera2.params.StreamConfigurationMap:
Documentation mentions 'TODO'
Todo: android.hardware.location.ContextHubManager#getNanoAppInstanceInfo(int):
@@ -2017,6 +2023,12 @@ UnflaggedApi: android.content.AttributionSource#AttributionSource(int, int, Stri
New API must be flagged with @FlaggedApi: constructor android.content.AttributionSource(int,int,String,String,android.os.IBinder,String[],android.content.AttributionSource)
UnflaggedApi: android.content.AttributionSource#AttributionSource(int, int, String, String, android.os.IBinder, String[], int, android.content.AttributionSource):
New API must be flagged with @FlaggedApi: constructor android.content.AttributionSource(int,int,String,String,android.os.IBinder,String[],int,android.content.AttributionSource)
+UnflaggedApi: android.content.Intent#EXTENDED_FLAG_FILTER_MISMATCH:
+ New API must be flagged with @FlaggedApi: field android.content.Intent.EXTENDED_FLAG_FILTER_MISMATCH
+UnflaggedApi: android.content.Intent#addExtendedFlags(int):
+ New API must be flagged with @FlaggedApi: method android.content.Intent.addExtendedFlags(int)
+UnflaggedApi: android.content.Intent#getExtendedFlags():
+ New API must be flagged with @FlaggedApi: method android.content.Intent.getExtendedFlags()
UnflaggedApi: android.content.pm.UserInfo#isCommunalProfile():
New API must be flagged with @FlaggedApi: method android.content.pm.UserInfo.isCommunalProfile()
UnflaggedApi: android.content.pm.UserInfo#isPrivateProfile():
diff --git a/core/java/Android.bp b/core/java/Android.bp
index eba500dd32b4..184421518d32 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -126,6 +126,7 @@ filegroup {
srcs: [
"android/os/IExternalVibrationController.aidl",
"android/os/IExternalVibratorService.aidl",
+ "android/os/ExternalVibrationScale.aidl",
],
}
@@ -527,23 +528,19 @@ filegroup {
],
}
-// common protolog sources without classes that rely on Android SDK
+// PackageManager common
filegroup {
- name: "protolog-common-no-android-src",
+ name: "framework-pm-common-shared-srcs",
srcs: [
- ":protolog-common-src",
- ],
- exclude_srcs: [
- "com/android/internal/protolog/common/ProtoLog.java",
+ "com/android/server/pm/pkg/AndroidPackage.java",
+ "com/android/server/pm/pkg/AndroidPackageSplit.java",
],
}
-// PackageManager common
filegroup {
- name: "framework-pm-common-shared-srcs",
+ name: "protolog-impl",
srcs: [
- "com/android/server/pm/pkg/AndroidPackage.java",
- "com/android/server/pm/pkg/AndroidPackageSplit.java",
+ "com/android/internal/protolog/ProtoLogImpl.java",
],
}
@@ -553,7 +550,7 @@ java_library {
srcs: [
"com/android/internal/protolog/ProtoLogImpl.java",
"com/android/internal/protolog/ProtoLogViewerConfigReader.java",
- ":protolog-common-src",
+ ":perfetto_trace_javastream_protos",
],
}
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index f7d75222a6f7..42c32723fd72 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -69,6 +69,7 @@ import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.inputmethod.EditorInfo;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.inputmethod.CancellationGroup;
import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
import com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback;
@@ -1388,7 +1389,9 @@ public abstract class AccessibilityService extends Service {
getFingerprintGestureController().onGesture(gesture);
}
- int getConnectionId() {
+ /** @hide */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public int getConnectionId() {
return mConnectionId;
}
diff --git a/core/java/android/adaptiveauth/flags.aconfig b/core/java/android/adaptiveauth/flags.aconfig
index 39e46bbdfa6a..de4e607b50f1 100644
--- a/core/java/android/adaptiveauth/flags.aconfig
+++ b/core/java/android/adaptiveauth/flags.aconfig
@@ -1,6 +1,13 @@
package: "android.adaptiveauth"
flag {
+ name: "enable_adaptive_auth"
+ namespace: "biometrics"
+ description: "Feature flag for enabling the new adaptive auth service"
+ bug: "285053096"
+}
+
+flag {
name: "report_biometric_auth_attempts"
namespace: "biometrics"
description: "Control the usage of the biometric auth signal in adaptive auth"
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index 4cad58521c09..c58624e9dfab 100644
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -26,6 +26,7 @@ import android.os.Build;
import android.util.LongArray;
import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicReference;
/**
* This is the superclass for classes which provide basic support for animations which can be
@@ -76,7 +77,7 @@ public abstract class Animator implements Cloneable {
* of it in case the list is modified while iterating. The array can be reused to avoid
* allocation on every notification.
*/
- private Object[] mCachedList;
+ private AtomicReference<Object[]> mCachedList = new AtomicReference<>();
/**
* Tracks whether we've notified listeners of the onAnimationStart() event. This can be
@@ -452,7 +453,7 @@ public abstract class Animator implements Cloneable {
if (mPauseListeners != null) {
anim.mPauseListeners = new ArrayList<AnimatorPauseListener>(mPauseListeners);
}
- anim.mCachedList = null;
+ anim.mCachedList.set(null);
anim.mStartListenersCalled = false;
return anim;
} catch (CloneNotSupportedException e) {
@@ -654,13 +655,9 @@ public abstract class Animator implements Cloneable {
int size = list == null ? 0 : list.size();
if (size > 0) {
// Try to reuse mCacheList to store the items of list.
- Object[] array;
- if (mCachedList == null || mCachedList.length < size) {
+ Object[] array = mCachedList.getAndSet(null);
+ if (array == null || array.length < size) {
array = new Object[size];
- } else {
- array = mCachedList;
- // Clear it in case there is some reentrancy
- mCachedList = null;
}
list.toArray(array);
for (int i = 0; i < size; i++) {
@@ -670,7 +667,7 @@ public abstract class Animator implements Cloneable {
array[i] = null;
}
// Store it for the next call so we can reuse this array, if needed.
- mCachedList = array;
+ mCachedList.compareAndSet(null, array);
}
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 23fe731701b6..57c67be7e625 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -844,7 +844,32 @@ public class Activity extends ContextThemeWrapper
private IBinder mToken;
private IBinder mAssistToken;
private IBinder mShareableActivityToken;
+
+ /** Initial caller of the activity. Can be retrieved from {@link #getInitialCaller} */
private ComponentCaller mInitialCaller;
+ /**
+ * Caller associated with the Intent from {@link #getIntent}. Can be retrieved from
+ * {@link #getCaller}.
+ *
+ * <p>The value of this field depends on how the activity set its intent:
+ * - If via {@link #setIntent(Intent)}, the caller will be {@code null}.
+ * - If via {@link #setIntent(Intent, ComponentCaller)}, the caller will be set to the passed
+ * caller.
+ */
+ private ComponentCaller mCaller;
+ /**
+ * Caller associated with an Intent within {@link #onNewIntent} and {@link #onActivityResult}.
+ * Can be retrieved from either of these methods:
+ * - {@link #getCurrentCaller}
+ * - By overriding {@link #onNewIntent(Intent, ComponentCaller)} and getting the second argument
+ * - By overriding {@link #onActivityResult(int, int, Intent, ComponentCaller)} and getting the
+ * fourth argument
+ *
+ * <p>The value of this field will be {@code null} outside of {@link #onNewIntent} and
+ * {@link #onActivityResult}.
+ */
+ private ComponentCaller mCurrentCaller;
+
@UnsupportedAppUsage
private int mIdent;
@UnsupportedAppUsage
@@ -977,6 +1002,9 @@ public class Activity extends ContextThemeWrapper
new ActivityManager.TaskDescription();
private int mLastTaskDescriptionHashCode;
+ @ActivityInfo.ScreenOrientation
+ private int mLastRequestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSET;
+
protected static final int[] FOCUSED_STATE_SET = {com.android.internal.R.attr.state_focused};
@SuppressWarnings("unused")
@@ -1117,23 +1145,71 @@ public class Activity extends ContextThemeWrapper
private static native String getDlWarning();
- /** Return the intent that started this activity. */
+ /**
+ * Returns the intent that started this activity.
+ *
+ * <p>To keep the Intent instance for future use, call {@link #setIntent(Intent)}, and use
+ * this method to retrieve it.
+ */
public Intent getIntent() {
return mIntent;
}
/**
- * Change the intent returned by {@link #getIntent}. This holds a
- * reference to the given intent; it does not copy it. Often used in
- * conjunction with {@link #onNewIntent}.
+ * Changes the intent returned by {@link #getIntent}. This holds a
+ * reference to the given intent; it does not copy it. Often used in
+ * conjunction with {@link #onNewIntent(Intent)}.
*
- * @param newIntent The new Intent object to return from getIntent
+ * @param newIntent The new Intent object to return from {@link #getIntent}
*
* @see #getIntent
- * @see #onNewIntent
+ * @see #onNewIntent(Intent)
*/
public void setIntent(Intent newIntent) {
+ internalSetIntent(newIntent, /* newCaller */ null);
+ }
+
+ /**
+ * Returns the ComponentCaller instance of the app that launched this activity with the intent
+ * from {@link #getIntent()}. To keep the value of the ComponentCaller instance for new intents,
+ * call {@link #setIntent(Intent, ComponentCaller)} instead of {@link #setIntent(Intent)}.
+ *
+ * @return {@link ComponentCaller} instance corresponding to the intent from
+ * {@link #getIntent()}, or {@code null} if the activity was not launched with that
+ * intent
+ *
+ * @see ComponentCaller
+ * @see #getIntent
+ * @see #setIntent(Intent, ComponentCaller)
+ */
+ @FlaggedApi(android.security.Flags.FLAG_CONTENT_URI_PERMISSION_APIS)
+ public @Nullable ComponentCaller getCaller() {
+ return mCaller;
+ }
+
+ /**
+ * Changes the intent returned by {@link #getIntent}, and ComponentCaller returned by
+ * {@link #getCaller}. This holds references to the given intent, and ComponentCaller; it does
+ * not copy them. Often used in conjunction with {@link #onNewIntent(Intent)}. To retrieve the
+ * caller from {@link #onNewIntent(Intent)}, use {@link #getCurrentCaller}, otherwise override
+ * {@link #onNewIntent(Intent, ComponentCaller)}.
+ *
+ * @param newIntent The new Intent object to return from {@link #getIntent}
+ * @param newCaller The new {@link ComponentCaller} object to return from
+ * {@link #getCaller}
+ *
+ * @see #getIntent
+ * @see #onNewIntent(Intent, ComponentCaller)
+ * @see #getCaller
+ */
+ @FlaggedApi(android.security.Flags.FLAG_CONTENT_URI_PERMISSION_APIS)
+ public void setIntent(@Nullable Intent newIntent, @Nullable ComponentCaller newCaller) {
+ internalSetIntent(newIntent, newCaller);
+ }
+
+ private void internalSetIntent(Intent newIntent, ComponentCaller newCaller) {
mIntent = newIntent;
+ mCaller = newCaller;
}
/**
@@ -2284,18 +2360,42 @@ public class Activity extends ContextThemeWrapper
* sometime later when activity becomes active again.
*
* <p>Note that {@link #getIntent} still returns the original Intent. You
- * can use {@link #setIntent} to update it to this new Intent.
+ * can use {@link #setIntent(Intent)} to update it to this new Intent.
*
- * @param intent The new intent that was started for the activity.
+ * @param intent The new intent that was used to start the activity
*
* @see #getIntent
- * @see #setIntent
+ * @see #setIntent(Intent)
* @see #onResume
*/
protected void onNewIntent(Intent intent) {
}
/**
+ * Same as {@link #onNewIntent(Intent)}, but with an extra parameter for the ComponentCaller
+ * instance associated with the app that sent the intent.
+ *
+ * <p>If you want to retrieve the caller without overriding this method, call
+ * {@link #getCurrentCaller} inside your existing {@link #onNewIntent(Intent)}.
+ *
+ * <p>Note that you should only override one {@link #onNewIntent} method.
+ *
+ * @param intent The new intent that was used to start the activity
+ * @param caller The {@link ComponentCaller} instance associated with the app that sent the
+ * intent
+ *
+ * @see ComponentCaller
+ * @see #onNewIntent(Intent)
+ * @see #getCurrentCaller
+ * @see #setIntent(Intent, ComponentCaller)
+ * @see #getCaller
+ */
+ @FlaggedApi(android.security.Flags.FLAG_CONTENT_URI_PERMISSION_APIS)
+ public void onNewIntent(@NonNull Intent intent, @NonNull ComponentCaller caller) {
+ onNewIntent(intent);
+ }
+
+ /**
* The hook for {@link ActivityThread} to save the state of this activity.
*
* Calls {@link #onSaveInstanceState(android.os.Bundle)}
@@ -7044,6 +7144,37 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * Returns the ComponentCaller instance of the app that re-launched this activity with a new
+ * intent via {@link #onNewIntent} or {@link #onActivityResult}.
+ *
+ * <p>Note that this method only works within the {@link #onNewIntent} and
+ * {@link #onActivityResult} methods. If you call this method outside {@link #onNewIntent} and
+ * {@link #onActivityResult}, it will throw an {@link IllegalStateException}.
+ *
+ * <p>You can also retrieve the caller if you override
+ * {@link #onNewIntent(Intent, ComponentCaller)} or
+ * {@link #onActivityResult(int, int, Intent, ComponentCaller)}.
+ *
+ * <p>To keep the ComponentCaller instance for future use, call
+ * {@link #setIntent(Intent, ComponentCaller)}, and use {@link #getCaller} to retrieve it.
+ *
+ * @return {@link ComponentCaller} instance
+ * @throws IllegalStateException if the caller is {@code null}, indicating the method was called
+ * outside {@link #onNewIntent}
+ * @see ComponentCaller
+ * @see #setIntent(Intent, ComponentCaller)
+ * @see #getCaller
+ */
+ @FlaggedApi(android.security.Flags.FLAG_CONTENT_URI_PERMISSION_APIS)
+ public @NonNull ComponentCaller getCurrentCaller() {
+ if (mCurrentCaller == null) {
+ throw new IllegalStateException("The caller is null because #getCurrentCaller should be"
+ + " called within #onNewIntent method");
+ }
+ return mCurrentCaller;
+ }
+
+ /**
* Control whether this activity's main window is visible. This is intended
* only for the special case of an activity that is not going to show a
* UI itself, but can't just finish prior to onResume() because it needs
@@ -7303,6 +7434,31 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * Same as {@link #onActivityResult(int, int, Intent)}, but with an extra parameter for the
+ * ComponentCaller instance associated with the app that sent the result.
+ *
+ * <p>If you want to retrieve the caller without overriding this method, call
+ * {@link #getCurrentCaller} inside your existing {@link #onActivityResult(int, int, Intent)}.
+ *
+ * <p>Note that you should only override one {@link #onActivityResult} method.
+ *
+ * @param requestCode The integer request code originally supplied to
+ * startActivityForResult(), allowing you to identify who this
+ * result came from.
+ * @param resultCode The integer result code returned by the child activity
+ * through its setResult().
+ * @param data An Intent, which can return result data to the caller
+ * (various data can be attached to Intent "extras").
+ * @param caller The {@link ComponentCaller} instance associated with the app that sent the
+ * intent.
+ */
+ @FlaggedApi(android.security.Flags.FLAG_CONTENT_URI_PERMISSION_APIS)
+ public void onActivityResult(int requestCode, int resultCode, @NonNull Intent data,
+ @NonNull ComponentCaller caller) {
+ onActivityResult(requestCode, resultCode, data);
+ }
+
+ /**
* Called when an activity you launched with an activity transition exposes this
* Activity through a returning activity transition, giving you the resultCode
* and any additional data from it. This method will only be called if the activity
@@ -7377,11 +7533,15 @@ public class Activity extends ContextThemeWrapper
* {@link ActivityInfo#screenOrientation ActivityInfo.screenOrientation}.
*/
public void setRequestedOrientation(@ActivityInfo.ScreenOrientation int requestedOrientation) {
+ if (requestedOrientation == mLastRequestedOrientation) {
+ return;
+ }
if (mParent == null) {
ActivityClient.getInstance().setRequestedOrientation(mToken, requestedOrientation);
} else {
mParent.setRequestedOrientation(requestedOrientation);
}
+ mLastRequestedOrientation = requestedOrientation;
}
/**
@@ -7395,6 +7555,9 @@ public class Activity extends ContextThemeWrapper
*/
@ActivityInfo.ScreenOrientation
public int getRequestedOrientation() {
+ if (mLastRequestedOrientation != ActivityInfo.SCREEN_ORIENTATION_UNSET) {
+ return mLastRequestedOrientation;
+ }
if (mParent == null) {
return ActivityClient.getInstance().getRequestedOrientation(mToken);
} else {
@@ -8740,6 +8903,7 @@ public class Activity extends ContextThemeWrapper
if (android.security.Flags.contentUriPermissionApis()) {
mInitialCaller = new ComponentCaller(getActivityToken(), initialCallerInfoAccessToken);
+ mCaller = mInitialCaller;
}
}
@@ -8815,6 +8979,16 @@ public class Activity extends ContextThemeWrapper
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
+ @FlaggedApi(android.security.Flags.FLAG_CONTENT_URI_PERMISSION_APIS)
+ final void performNewIntent(@NonNull Intent intent, @NonNull ComponentCaller caller) {
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "performNewIntent");
+ mCanEnterPictureInPicture = true;
+ mCurrentCaller = caller;
+ onNewIntent(intent, caller);
+ mCurrentCaller = null;
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ }
+
final void performStart(String reason) {
if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "performStart:"
@@ -9135,15 +9309,36 @@ public class Activity extends ContextThemeWrapper
}
}
+ void dispatchActivityResult(String who, int requestCode, int resultCode, Intent data,
+ ComponentCaller caller, String reason) {
+ internalDispatchActivityResult(who, requestCode, resultCode, data, caller, reason);
+ }
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
void dispatchActivityResult(String who, int requestCode, int resultCode, Intent data,
String reason) {
+ if (android.security.Flags.contentUriPermissionApis()) {
+ internalDispatchActivityResult(who, requestCode, resultCode, data,
+ new ComponentCaller(getActivityToken(), /* callerToken */ null), reason);
+ } else {
+ internalDispatchActivityResult(who, requestCode, resultCode, data, null, reason);
+ }
+ }
+
+ private void internalDispatchActivityResult(String who, int requestCode, int resultCode,
+ Intent data, ComponentCaller caller, String reason) {
if (false) Log.v(
TAG, "Dispatching result: who=" + who + ", reqCode=" + requestCode
+ ", resCode=" + resultCode + ", data=" + data);
mFragments.noteStateNotSaved();
if (who == null) {
- onActivityResult(requestCode, resultCode, data);
+ if (android.security.Flags.contentUriPermissionApis()) {
+ mCurrentCaller = caller;
+ onActivityResult(requestCode, resultCode, data, caller);
+ mCurrentCaller = null;
+ } else {
+ onActivityResult(requestCode, resultCode, data);
+ }
} else if (who.startsWith(REQUEST_PERMISSIONS_WHO_PREFIX)) {
who = who.substring(REQUEST_PERMISSIONS_WHO_PREFIX.length());
if (TextUtils.isEmpty(who)) {
diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java
index a59f04bf4f3a..10dc3c6c1360 100644
--- a/core/java/android/app/ActivityClient.java
+++ b/core/java/android/app/ActivityClient.java
@@ -299,6 +299,26 @@ public class ActivityClient {
}
}
+ /** Returns the uid of the app that launched the activity. */
+ public int getActivityCallerUid(IBinder activityToken, IBinder callerToken) {
+ try {
+ return getActivityClientController().getActivityCallerUid(activityToken,
+ callerToken);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /** Returns the package of the app that launched the activity. */
+ public String getActivityCallerPackage(IBinder activityToken, IBinder callerToken) {
+ try {
+ return getActivityClientController().getActivityCallerPackage(activityToken,
+ callerToken);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/** Checks if the app that launched the activity has access to the URI. */
public int checkActivityCallerContentUriPermission(IBinder activityToken, IBinder callerToken,
Uri uri, int modeFlags) {
diff --git a/core/java/android/app/ActivityGroup.java b/core/java/android/app/ActivityGroup.java
index cb06eea2059e..21c67edc607e 100644
--- a/core/java/android/app/ActivityGroup.java
+++ b/core/java/android/app/ActivityGroup.java
@@ -111,7 +111,7 @@ public class ActivityGroup extends Activity {
@Override
void dispatchActivityResult(String who, int requestCode, int resultCode,
- Intent data, String reason) {
+ Intent data, ComponentCaller caller, String reason) {
if (who != null) {
Activity act = mLocalActivityManager.getActivity(who);
/*
@@ -125,7 +125,7 @@ public class ActivityGroup extends Activity {
return;
}
}
- super.dispatchActivityResult(who, requestCode, resultCode, data, reason);
+ super.dispatchActivityResult(who, requestCode, resultCode, data, caller, reason);
}
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index a8d183a1d6dd..f3585229d823 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.activityTypeToString;
import static android.app.WindowConfiguration.windowingModeToString;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.media.audio.Flags.FLAG_FOREGROUND_AUDIO_CONTROL;
import android.Manifest;
import android.annotation.ColorInt;
@@ -794,6 +795,7 @@ public class ActivityManager {
PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK,
PROCESS_CAPABILITY_BFSL,
PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK,
+ PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ProcessCapability {}
@@ -943,6 +945,14 @@ public class ActivityManager {
public static final int PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK = 1 << 5;
/**
+ * @hide
+ * Process can access volume APIs and can request audio focus with GAIN.
+ */
+ @FlaggedApi(FLAG_FOREGROUND_AUDIO_CONTROL)
+ @SystemApi
+ public static final int PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL = 1 << 6;
+
+ /**
* @hide all capabilities, the ORing of all flags in {@link ProcessCapability}.
*
* Don't expose it as TestApi -- we may add new capabilities any time, which could
@@ -953,7 +963,8 @@ public class ActivityManager {
| PROCESS_CAPABILITY_FOREGROUND_MICROPHONE
| PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK
| PROCESS_CAPABILITY_BFSL
- | PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK;
+ | PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK
+ | PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL;
/**
* All implicit capabilities. There are capabilities that process automatically have.
@@ -975,6 +986,7 @@ public class ActivityManager {
pw.print((caps & PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK) != 0 ? 'N' : '-');
pw.print((caps & PROCESS_CAPABILITY_BFSL) != 0 ? 'F' : '-');
pw.print((caps & PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK) != 0 ? 'U' : '-');
+ pw.print((caps & PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL) != 0 ? 'A' : '-');
}
/** @hide */
@@ -986,6 +998,7 @@ public class ActivityManager {
sb.append((caps & PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK) != 0 ? 'N' : '-');
sb.append((caps & PROCESS_CAPABILITY_BFSL) != 0 ? 'F' : '-');
sb.append((caps & PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK) != 0 ? 'U' : '-');
+ sb.append((caps & PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL) != 0 ? 'A' : '-');
}
/**
@@ -5954,14 +5967,19 @@ public class ActivityManager {
}
/**
- * Used by {@link com.android.systemui.theme.ThemeOverlayController} to notify of color
- * palette readiness.
+ * Used by ThemeOverlayController to notify when color
+ * palette is ready.
+ *
+ * @param userId The ID of the user where ThemeOverlayController is ready.
+ *
+ * @throws RemoteException
+ *
* @hide
*/
@RequiresPermission(Manifest.permission.SET_THEME_OVERLAY_CONTROLLER_READY)
- public void setThemeOverlayReady(boolean readiness) {
+ public void setThemeOverlayReady(@UserIdInt int userId) {
try {
- getService().setThemeOverlayReady(readiness);
+ getService().setThemeOverlayReady(userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 0ae2e01a45fa..062b89ed57ba 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -1264,5 +1264,5 @@ public abstract class ActivityManagerInternal {
* palette readiness.
* @hide
*/
- public abstract boolean getThemeOverlayReadiness();
+ public abstract boolean isThemeOverlayReady(int userId);
}
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index e14bf68bde53..2a2c5f05f122 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -20,6 +20,7 @@ import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIO
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_RECEIVER_FOREGROUND;
import static android.view.Display.INVALID_DISPLAY;
@@ -1849,7 +1850,7 @@ public class ActivityOptions extends ComponentOptions {
public int getPendingIntentLaunchFlags() {
// b/243794108: Ignore all flags except the new task flag, to be reconsidered in b/254490217
return mPendingIntentLaunchFlags &
- (FLAG_ACTIVITY_NEW_TASK | FLAG_RECEIVER_FOREGROUND);
+ (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK | FLAG_RECEIVER_FOREGROUND);
}
/**
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index b25d5ebf61a0..22b8d92b507c 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -211,6 +211,7 @@ import android.view.contentcapture.IContentCaptureOptionsCallback;
import android.view.translation.TranslationSpec;
import android.view.translation.UiTranslationSpec;
import android.webkit.WebView;
+import android.window.ActivityWindowInfo;
import android.window.ITaskFragmentOrganizer;
import android.window.SizeConfigurationBuckets;
import android.window.SplashScreen;
@@ -237,7 +238,6 @@ import com.android.internal.util.Preconditions;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.org.conscrypt.TrustedCertificateStore;
import com.android.server.am.MemInfoDumpProto;
-import com.android.window.flags.Flags;
import dalvik.annotation.optimization.NeverCompile;
import dalvik.system.AppSpecializationHooks;
@@ -604,6 +604,9 @@ public final class ActivityThread extends ClientTransactionHandler
boolean hideForNow;
Configuration createdConfig;
Configuration overrideConfig;
+ @NonNull
+ private ActivityWindowInfo mActivityWindowInfo;
+
// Used for consolidating configs before sending on to Activity.
private final Configuration tmpConfig = new Configuration();
// Callback used for updating activity override config and camera compat control state.
@@ -671,7 +674,8 @@ public final class ActivityThread extends ClientTransactionHandler
List<ReferrerIntent> pendingNewIntents, SceneTransitionInfo sceneTransitionInfo,
boolean isForward, ProfilerInfo profilerInfo, ClientTransactionHandler client,
IBinder assistToken, IBinder shareableActivityToken, boolean launchedFromBubble,
- IBinder taskFragmentToken, IBinder initialCallerInfoAccessToken) {
+ IBinder taskFragmentToken, IBinder initialCallerInfoAccessToken,
+ ActivityWindowInfo activityWindowInfo) {
this.token = token;
this.assistToken = assistToken;
this.shareableActivityToken = shareableActivityToken;
@@ -692,6 +696,7 @@ public final class ActivityThread extends ClientTransactionHandler
mSceneTransitionInfo = sceneTransitionInfo;
mLaunchedFromBubble = launchedFromBubble;
mTaskFragmentToken = taskFragmentToken;
+ mActivityWindowInfo = activityWindowInfo;
init();
}
@@ -780,6 +785,11 @@ public final class ActivityThread extends ClientTransactionHandler
return activity != null && activity.mVisibleFromServer;
}
+ @NonNull
+ public ActivityWindowInfo getActivityWindowInfo() {
+ return mActivityWindowInfo;
+ }
+
public String toString() {
ComponentName componentName = intent != null ? intent.getComponent() : null;
return "ActivityRecord{"
@@ -1234,7 +1244,8 @@ public final class ActivityThread extends ClientTransactionHandler
}
@Override
- public final void scheduleTimeoutServiceForType(IBinder token, int startId, int fgsType) {
+ public final void scheduleTimeoutServiceForType(IBinder token, int startId,
+ @ServiceInfo.ForegroundServiceType int fgsType) {
if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER,
"scheduleTimeoutServiceForType. token=" + token);
@@ -3758,15 +3769,11 @@ public final class ActivityThread extends ClientTransactionHandler
if (DEBUG_RESULTS) Slog.v(TAG, "sendActivityResult: id=" + id
+ " req=" + requestCode + " res=" + resultCode + " data=" + data);
final ArrayList<ResultInfo> list = new ArrayList<>();
- list.add(new ResultInfo(id, requestCode, resultCode, data));
+ list.add(new ResultInfo(id, requestCode, resultCode, data, activityToken));
final ClientTransaction clientTransaction = ClientTransaction.obtain(mAppThread);
final ActivityResultItem activityResultItem = ActivityResultItem.obtain(
activityToken, list);
- if (Flags.bundleClientTransactionFlag()) {
- clientTransaction.addTransactionItem(activityResultItem);
- } else {
- clientTransaction.addCallback(activityResultItem);
- }
+ clientTransaction.addTransactionItem(activityResultItem);
try {
mAppThread.scheduleTransaction(clientTransaction);
} catch (RemoteException e) {
@@ -4203,7 +4210,12 @@ public final class ActivityThread extends ClientTransactionHandler
intent.prepareToEnterProcess(isProtectedComponent(r.activityInfo),
r.activity.getAttributionSource());
r.activity.mFragments.noteStateNotSaved();
- mInstrumentation.callActivityOnNewIntent(r.activity, intent);
+ if (android.security.Flags.contentUriPermissionApis()) {
+ ComponentCaller caller = new ComponentCaller(r.token, intent.mCallerToken);
+ mInstrumentation.callActivityOnNewIntent(r.activity, intent, caller);
+ } else {
+ mInstrumentation.callActivityOnNewIntent(r.activity, intent);
+ }
}
}
@@ -4548,11 +4560,7 @@ public final class ActivityThread extends ClientTransactionHandler
final PauseActivityItem pauseActivityItem = PauseActivityItem.obtain(r.token,
r.activity.isFinishing(), /* userLeaving */ true, r.activity.mConfigChangeFlags,
/* dontReport */ false, /* autoEnteringPip */ false);
- if (Flags.bundleClientTransactionFlag()) {
- transaction.addTransactionItem(pauseActivityItem);
- } else {
- transaction.setLifecycleStateRequest(pauseActivityItem);
- }
+ transaction.addTransactionItem(pauseActivityItem);
executeTransaction(transaction);
}
@@ -4560,11 +4568,7 @@ public final class ActivityThread extends ClientTransactionHandler
final ClientTransaction transaction = ClientTransaction.obtain(mAppThread);
final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain(r.token,
/* isForward */ false, /* shouldSendCompatFakeFocus */ false);
- if (Flags.bundleClientTransactionFlag()) {
- transaction.addTransactionItem(resumeActivityItem);
- } else {
- transaction.setLifecycleStateRequest(resumeActivityItem);
- }
+ transaction.addTransactionItem(resumeActivityItem);
executeTransaction(transaction);
}
@@ -5159,7 +5163,8 @@ public final class ActivityThread extends ClientTransactionHandler
}
}
- private void handleTimeoutServiceForType(IBinder token, int startId, int fgsType) {
+ private void handleTimeoutServiceForType(IBinder token, int startId,
+ @ServiceInfo.ForegroundServiceType int fgsType) {
Service s = mServices.get(token);
if (s != null) {
try {
@@ -5794,8 +5799,14 @@ public final class ActivityThread extends ClientTransactionHandler
}
if (DEBUG_RESULTS) Slog.v(TAG,
"Delivering result to activity " + r + " : " + ri);
- r.activity.dispatchActivityResult(ri.mResultWho,
- ri.mRequestCode, ri.mResultCode, ri.mData, reason);
+ if (android.security.Flags.contentUriPermissionApis()) {
+ ComponentCaller caller = new ComponentCaller(r.token, ri.mCallerToken);
+ r.activity.dispatchActivityResult(ri.mResultWho,
+ ri.mRequestCode, ri.mResultCode, ri.mData, caller, reason);
+ } else {
+ r.activity.dispatchActivityResult(ri.mResultWho,
+ ri.mRequestCode, ri.mResultCode, ri.mData, reason);
+ }
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
throw new RuntimeException(
@@ -6178,13 +6189,8 @@ public final class ActivityThread extends ClientTransactionHandler
TransactionExecutorHelper.getLifecycleRequestForCurrentState(r);
// Schedule the transaction.
final ClientTransaction transaction = ClientTransaction.obtain(mAppThread);
- if (Flags.bundleClientTransactionFlag()) {
- transaction.addTransactionItem(activityRelaunchItem);
- transaction.addTransactionItem(lifecycleRequest);
- } else {
- transaction.addCallback(activityRelaunchItem);
- transaction.setLifecycleStateRequest(lifecycleRequest);
- }
+ transaction.addTransactionItem(activityRelaunchItem);
+ transaction.addTransactionItem(lifecycleRequest);
executeTransaction(transaction);
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 2100425a6771..39900a03e560 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -16,7 +16,9 @@
package android.app;
+
import static android.location.flags.Flags.FLAG_LOCATION_BYPASS;
+import static android.media.audio.Flags.foregroundAudioControl;
import static android.permission.flags.Flags.FLAG_OP_ENABLE_MOBILE_DATA_BY_USER;
import static android.view.contentprotection.flags.Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED;
import static android.view.contentprotection.flags.Flags.FLAG_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER_APP_OP_ENABLED;
@@ -70,6 +72,9 @@ import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
+import android.permission.PermissionGroupUsage;
+import android.permission.PermissionUsageHelper;
+import android.permission.flags.Flags;
import android.provider.DeviceConfig;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -95,7 +100,6 @@ import com.android.internal.util.DataClass;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.Parcelling;
import com.android.internal.util.Preconditions;
-import com.android.media.flags.Flags;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@@ -224,6 +228,7 @@ public class AppOpsManager {
private static Boolean sFullLog = null;
final Context mContext;
+ private PermissionUsageHelper mUsageHelper;
@UnsupportedAppUsage
final IAppOpsService mService;
@@ -2072,7 +2077,8 @@ public class AppOpsManager {
* @hide
*/
@SystemApi
- @FlaggedApi(Flags.FLAG_ENABLE_PRIVILEGED_ROUTING_FOR_MEDIA_ROUTING_CONTROL)
+ @FlaggedApi(com.android.media.flags.Flags
+ .FLAG_ENABLE_PRIVILEGED_ROUTING_FOR_MEDIA_ROUTING_CONTROL)
public static final String OPSTR_MEDIA_ROUTING_CONTROL = "android:media_routing_control";
/**
@@ -2238,7 +2244,7 @@ public class AppOpsManager {
*
* @hide
*/
- @FlaggedApi(android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+ @FlaggedApi(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
@SystemApi
public static final String OPSTR_ACCESS_RESTRICTED_SETTINGS =
"android:access_restricted_settings";
@@ -3229,6 +3235,10 @@ public class AppOpsManager {
* @hide
*/
public static @Mode int opToDefaultMode(int op) {
+ if (op == OP_TAKE_AUDIO_FOCUS && foregroundAudioControl()) {
+ // when removing the flag, change the entry in sAppOpInfos for OP_TAKE_AUDIO_FOCUS
+ return AppOpsManager.MODE_FOREGROUND;
+ }
return sAppOpInfos[op].defaultMode;
}
@@ -3423,7 +3433,8 @@ public class AppOpsManager {
}
}
- public static final @android.annotation.NonNull Creator<PackageOps> CREATOR = new Creator<PackageOps>() {
+ public static final @android.annotation.NonNull Creator<PackageOps> CREATOR =
+ new Creator<PackageOps>() {
@Override public PackageOps createFromParcel(Parcel source) {
return new PackageOps(source);
}
@@ -7401,7 +7412,7 @@ public class AppOpsManager {
* @param userId User id of the app whose Op changed.
* @param persistentDeviceId persistent device id whose Op changed.
*/
- @FlaggedApi(android.permission.flags.Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
+ @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
default void onOpChanged(@NonNull String op, @NonNull String packageName, int userId,
@NonNull String persistentDeviceId) {
if (Objects.equals(persistentDeviceId,
@@ -7471,7 +7482,7 @@ public class AppOpsManager {
* @param attributionFlags the attribution flags for this operation.
* @param attributionChainId the unique id of the attribution chain this op is a part of.
*/
- @FlaggedApi(android.permission.flags.Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
+ @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
default void onOpActiveChanged(@NonNull String op, int uid, @NonNull String packageName,
@Nullable String attributionTag, int virtualDeviceId, boolean active,
@AttributionFlags int attributionFlags, int attributionChainId) {
@@ -7525,7 +7536,7 @@ public class AppOpsManager {
* @param flags The flags of this op
* @param result The result of the note.
*/
- @FlaggedApi(android.permission.flags.Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
+ @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
default void onOpNoted(@NonNull String op, int uid, @NonNull String packageName,
@Nullable String attributionTag, int virtualDeviceId, @OpFlags int flags,
@Mode int result) {
@@ -7759,6 +7770,44 @@ public class AppOpsManager {
}
/**
+ * Retrieve current operation state for all applications for a device.
+ *
+ * The mode of the ops returned are set for the package but may not reflect their effective
+ * state due to UID policy or because it's controlled by a different global op.
+ *
+ * Use {@link #unsafeCheckOp(String, int, String)}} or
+ * {@link #noteOp(String, int, String, String, String)} if the effective mode is needed.
+ *
+ * @param ops The set of operations you are interested in, or null if you want all of them.
+ * @param persistentDeviceId The device that the ops are attributed to.
+ *
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(android.permission.flags.Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
+ @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS)
+ public @NonNull List<AppOpsManager.PackageOps> getPackagesForOps(@Nullable String[] ops,
+ @NonNull String persistentDeviceId) {
+ final int[] opCodes;
+ if (ops != null) {
+ final int opCount = ops.length;
+ opCodes = new int[opCount];
+ for (int i = 0; i < opCount; i++) {
+ opCodes[i] = sOpStrToOp.get(ops[i]);
+ }
+ } else {
+ opCodes = null;
+ }
+ final List<AppOpsManager.PackageOps> result;
+ try {
+ result = mService.getPackagesForOpsForDevice(opCodes, persistentDeviceId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ return (result != null) ? result : Collections.emptyList();
+ }
+
+ /**
* Retrieve current operation state for all applications.
*
* The mode of the ops returned are set for the package but may not reflect their effective
@@ -7774,7 +7823,8 @@ public class AppOpsManager {
@UnsupportedAppUsage
public List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops) {
try {
- return mService.getPackagesForOps(ops);
+ return mService.getPackagesForOpsForDevice(ops,
+ VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -8313,14 +8363,26 @@ public class AppOpsManager {
String attributionTag, int virtualDeviceId, boolean active,
@AttributionFlags int attributionFlags, int attributionChainId) {
executor.execute(() -> {
- if (callback instanceof OnOpActiveChangedInternalListener) {
- ((OnOpActiveChangedInternalListener) callback).onOpActiveChanged(op,
- uid, packageName, virtualDeviceId, active);
- }
- if (sAppOpInfos[op].name != null) {
- callback.onOpActiveChanged(sAppOpInfos[op].name, uid, packageName,
- attributionTag, virtualDeviceId, active, attributionFlags,
- attributionChainId);
+ if (Flags.deviceAwarePermissionApisEnabled()) {
+ if (callback instanceof OnOpActiveChangedInternalListener) {
+ ((OnOpActiveChangedInternalListener) callback).onOpActiveChanged(op,
+ uid, packageName, virtualDeviceId, active);
+ }
+ if (sAppOpInfos[op].name != null) {
+ callback.onOpActiveChanged(sAppOpInfos[op].name, uid, packageName,
+ attributionTag, virtualDeviceId, active, attributionFlags,
+ attributionChainId);
+ }
+ } else {
+ if (callback instanceof OnOpActiveChangedInternalListener) {
+ ((OnOpActiveChangedInternalListener) callback).onOpActiveChanged(op,
+ uid, packageName, active);
+ }
+ if (sAppOpInfos[op].name != null) {
+ callback.onOpActiveChanged(sAppOpInfos[op].name, uid, packageName,
+ attributionTag, active, attributionFlags,
+ attributionChainId);
+ }
}
});
}
@@ -8565,9 +8627,13 @@ public class AppOpsManager {
try {
executor.execute(() -> {
if (sAppOpInfos[op].name != null) {
- listener.onOpNoted(sAppOpInfos[op].name, uid, packageName,
- attributionTag, virtualDeviceId,
- flags, mode);
+ if (Flags.deviceAwarePermissionApisEnabled()) {
+ listener.onOpNoted(sAppOpInfos[op].name, uid, packageName,
+ attributionTag, virtualDeviceId, flags, mode);
+ } else {
+ listener.onOpNoted(sAppOpInfos[op].name, uid, packageName,
+ attributionTag, flags, mode);
+ }
}
});
} finally {
@@ -9940,6 +10006,30 @@ public class AppOpsManager {
appOpsNotedForAttribution.set(op);
}
+ /**
+ * Get recent op usage data for CAMERA, MICROPHONE and LOCATION from all connected devices
+ * to power privacy indicator.
+ *
+ * @param includeMicrophoneUsage whether to retrieve microphone usage
+ * @return A list of permission groups currently or recently used by all apps by all users in
+ * the current profile group.
+ *
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ @FlaggedApi(android.permission.flags.Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
+ @RequiresPermission(Manifest.permission.GET_APP_OPS_STATS)
+ public List<PermissionGroupUsage> getPermissionGroupUsageForPrivacyIndicator(
+ boolean includeMicrophoneUsage) {
+ // Lazily initialize the usage helper
+ if (mUsageHelper == null) {
+ mUsageHelper = new PermissionUsageHelper(mContext);
+ }
+
+ return mUsageHelper.getOpUsageDataForAllDevices(includeMicrophoneUsage);
+ }
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 3ec39b5145a7..dd6bc55421ef 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -120,6 +120,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.LauncherIcons;
import android.util.Log;
+import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.Immutable;
@@ -4047,11 +4048,16 @@ public class ApplicationPackageManager extends PackageManager {
@Nullable
private Drawable getArchivedAppIcon(String packageName) {
try {
- return new BitmapDrawable(null,
- mPM.getArchivedAppIcon(packageName, new UserHandle(getUserId()),
- mContext.getPackageName()));
+ Bitmap archivedAppIcon = mPM.getArchivedAppIcon(packageName,
+ new UserHandle(getUserId()),
+ mContext.getPackageName());
+ if (archivedAppIcon == null) {
+ return null;
+ }
+ return new BitmapDrawable(null, archivedAppIcon);
} catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ Slog.e(TAG, "Failed to retrieve archived app icon: " + e.getMessage());
+ return null;
}
}
diff --git a/core/java/android/app/ApplicationStartInfo.java b/core/java/android/app/ApplicationStartInfo.java
index c6712c044539..3715c6e633dc 100644
--- a/core/java/android/app/ApplicationStartInfo.java
+++ b/core/java/android/app/ApplicationStartInfo.java
@@ -22,6 +22,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
import android.icu.text.SimpleDateFormat;
import android.os.Parcel;
import android.os.Parcelable;
@@ -249,7 +250,7 @@ public final class ApplicationStartInfo implements Parcelable {
private @StartType int mStartType;
/**
- * @see #getStartIntent
+ * @see #getIntent
*/
private Intent mStartIntent;
@@ -259,6 +260,11 @@ public final class ApplicationStartInfo implements Parcelable {
private @LaunchMode int mLaunchMode;
/**
+ * @see #wasForceStopped()
+ */
+ private boolean mWasForceStopped;
+
+ /**
* @hide *
*/
@IntDef(
@@ -427,6 +433,15 @@ public final class ApplicationStartInfo implements Parcelable {
}
/**
+ * @see #wasForceStopped()
+ * @param wasForceStopped whether the app had been force-stopped in the past
+ * @hide
+ */
+ public void setForceStopped(boolean wasForceStopped) {
+ mWasForceStopped = wasForceStopped;
+ }
+
+ /**
* Current state of startup.
*
* Can be used to determine whether the object will have additional fields added as it may be
@@ -578,6 +593,20 @@ public final class ApplicationStartInfo implements Parcelable {
return mLaunchMode;
}
+ /**
+ * Informs whether this is the first process launch for an app since it was
+ * {@link ApplicationInfo#FLAG_STOPPED force-stopped} for some reason.
+ * This allows the app to know if it should re-register for any alarms, jobs and other callbacks
+ * that were cleared when the app was force-stopped.
+ *
+ * @return {@code true} if this is the first process launch of the app after having been
+ * stopped, {@code false} otherwise.
+ */
+ @FlaggedApi(android.content.pm.Flags.FLAG_STAY_STOPPED)
+ public boolean wasForceStopped() {
+ return mWasForceStopped;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -603,6 +632,7 @@ public final class ApplicationStartInfo implements Parcelable {
dest.writeInt(mStartType);
dest.writeParcelable(mStartIntent, flags);
dest.writeInt(mLaunchMode);
+ dest.writeBoolean(mWasForceStopped);
}
/** @hide */
@@ -622,6 +652,7 @@ public final class ApplicationStartInfo implements Parcelable {
mStartType = other.mStartType;
mStartIntent = other.mStartIntent;
mLaunchMode = other.mLaunchMode;
+ mWasForceStopped = other.mWasForceStopped;
}
private ApplicationStartInfo(@NonNull Parcel in) {
@@ -643,6 +674,7 @@ public final class ApplicationStartInfo implements Parcelable {
mStartIntent =
in.readParcelable(Intent.class.getClassLoader(), android.content.Intent.class);
mLaunchMode = in.readInt();
+ mWasForceStopped = in.readBoolean();
}
private static String intern(@Nullable String source) {
@@ -720,6 +752,7 @@ public final class ApplicationStartInfo implements Parcelable {
intentOut.close();
}
proto.write(ApplicationStartInfoProto.LAUNCH_MODE, mLaunchMode);
+ proto.write(ApplicationStartInfoProto.WAS_FORCE_STOPPED, mWasForceStopped);
proto.end(token);
}
@@ -799,6 +832,10 @@ public final class ApplicationStartInfo implements Parcelable {
case (int) ApplicationStartInfoProto.LAUNCH_MODE:
mLaunchMode = proto.readInt(ApplicationStartInfoProto.LAUNCH_MODE);
break;
+ case (int) ApplicationStartInfoProto.WAS_FORCE_STOPPED:
+ mWasForceStopped = proto.readBoolean(
+ ApplicationStartInfoProto.WAS_FORCE_STOPPED);
+ break;
}
}
proto.end(token);
@@ -823,6 +860,7 @@ public final class ApplicationStartInfo implements Parcelable {
.append(" reason=").append(reasonToString(mReason))
.append(" startType=").append(startTypeToString(mStartType))
.append(" launchMode=").append(mLaunchMode)
+ .append(" wasForceStopped=").append(mWasForceStopped)
.append('\n');
if (mStartIntent != null) {
sb.append(" intent=").append(mStartIntent.toString())
@@ -878,7 +916,7 @@ public final class ApplicationStartInfo implements Parcelable {
&& mDefiningUid == o.mDefiningUid && mReason == o.mReason
&& mStartupState == o.mStartupState && mStartType == o.mStartType
&& mLaunchMode == o.mLaunchMode && TextUtils.equals(mProcessName, o.mProcessName)
- && timestampsEquals(o);
+ && timestampsEquals(o) && mWasForceStopped == o.mWasForceStopped;
}
@Override
diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java
index f727ee5a95fe..1b5b0fc5d917 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -16,6 +16,8 @@
package android.app;
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -65,6 +67,8 @@ public class BroadcastOptions extends ComponentOptions {
private @Nullable BundleMerger mDeliveryGroupExtrasMerger;
private @Nullable IntentFilter mDeliveryGroupMatchingFilter;
private @DeferralPolicy int mDeferralPolicy;
+ private @CurrentTimeMillisLong long mEventTriggerTimestampMillis;
+ private @CurrentTimeMillisLong long mRemoteEventTriggerTimestampMillis;
/** @hide */
@IntDef(flag = true, prefix = { "FLAG_" }, value = {
@@ -190,6 +194,18 @@ public class BroadcastOptions extends ComponentOptions {
"android:broadcast.idForResponseEvent";
/**
+ * Corresponds to {@link #setEventTriggerTimestampMillis(long)}.
+ */
+ private static final String KEY_EVENT_TRIGGER_TIMESTAMP =
+ "android:broadcast.eventTriggerTimestamp";
+
+ /**
+ * Corresponds to {@link #setRemoteEventTriggerTimestampMillis(long)}.
+ */
+ private static final String KEY_REMOTE_EVENT_TRIGGER_TIMESTAMP =
+ "android:broadcast.remoteEventTriggerTimestamp";
+
+ /**
* Corresponds to {@link #setDeliveryGroupPolicy(int)}.
*/
private static final String KEY_DELIVERY_GROUP_POLICY =
@@ -341,6 +357,8 @@ public class BroadcastOptions extends ComponentOptions {
mRequireNoneOfPermissions = opts.getStringArray(KEY_REQUIRE_NONE_OF_PERMISSIONS);
mRequireCompatChangeId = opts.getLong(KEY_REQUIRE_COMPAT_CHANGE_ID, CHANGE_INVALID);
mIdForResponseEvent = opts.getLong(KEY_ID_FOR_RESPONSE_EVENT);
+ mEventTriggerTimestampMillis = opts.getLong(KEY_EVENT_TRIGGER_TIMESTAMP);
+ mRemoteEventTriggerTimestampMillis = opts.getLong(KEY_REMOTE_EVENT_TRIGGER_TIMESTAMP);
mDeliveryGroupPolicy = opts.getInt(KEY_DELIVERY_GROUP_POLICY,
DELIVERY_GROUP_POLICY_ALL);
mDeliveryGroupMatchingNamespaceFragment = opts.getString(KEY_DELIVERY_GROUP_NAMESPACE);
@@ -787,6 +805,60 @@ public class BroadcastOptions extends ComponentOptions {
}
/**
+ * Set the timestamp for the event that triggered this broadcast, in
+ * {@link System#currentTimeMillis()} timebase.
+ *
+ * <p> For instance, if this broadcast is for a push message, then this timestamp
+ * could correspond to when the device received the message.
+ *
+ * @param timestampMillis the timestamp in {@link System#currentTimeMillis()} timebase that
+ * correspond to the event that triggered this broadcast.
+ */
+ @FlaggedApi(android.app.Flags.FLAG_BCAST_EVENT_TIMESTAMPS)
+ public void setEventTriggerTimestampMillis(@CurrentTimeMillisLong long timestampMillis) {
+ mEventTriggerTimestampMillis = timestampMillis;
+ }
+
+ /**
+ * Return the timestamp for the event that triggered this broadcast, in
+ * {@link System#currentTimeMillis()} timebase.
+ *
+ * @return the timestamp in {@link System#currentTimeMillis()} timebase that was previously
+ * set using {@link #setEventTriggerTimestampMillis(long)}.
+ */
+ @FlaggedApi(android.app.Flags.FLAG_BCAST_EVENT_TIMESTAMPS)
+ public @CurrentTimeMillisLong long getEventTriggerTimestampMillis() {
+ return mEventTriggerTimestampMillis;
+ }
+
+ /**
+ * Set the timestamp for the remote event, if any, that triggered this broadcast, in
+ * {@link System#currentTimeMillis()} timebase.
+ *
+ * <p> For instance, if this broadcast is for a push message, then this timestamp
+ * could correspond to when the message originated remotely.
+ *
+ * @param timestampMillis the timestamp in {@link System#currentTimeMillis()} timebase that
+ * correspond to the remote event that triggered this broadcast.
+ */
+ @FlaggedApi(android.app.Flags.FLAG_BCAST_EVENT_TIMESTAMPS)
+ public void setRemoteEventTriggerTimestampMillis(@CurrentTimeMillisLong long timestampMillis) {
+ mRemoteEventTriggerTimestampMillis = timestampMillis;
+ }
+
+ /**
+ * Return the timestamp for the remote event that triggered this broadcast, in
+ * {@link System#currentTimeMillis()} timebase.
+ *
+ * @return the timestamp in {@link System#currentTimeMillis()} timebase that was previously
+ * set using {@link #setRemoteEventTriggerTimestampMillis(long)}}.
+ */
+ @FlaggedApi(android.app.Flags.FLAG_BCAST_EVENT_TIMESTAMPS)
+ public @CurrentTimeMillisLong long getRemoteEventTriggerTimestampMillis() {
+ return mRemoteEventTriggerTimestampMillis;
+ }
+
+ /**
* Sets deferral policy for this broadcast that specifies how this broadcast
* can be deferred for delivery at some future point.
*/
@@ -1120,6 +1192,12 @@ public class BroadcastOptions extends ComponentOptions {
if (mIdForResponseEvent != 0) {
b.putLong(KEY_ID_FOR_RESPONSE_EVENT, mIdForResponseEvent);
}
+ if (mEventTriggerTimestampMillis > 0) {
+ b.putLong(KEY_EVENT_TRIGGER_TIMESTAMP, mEventTriggerTimestampMillis);
+ }
+ if (mRemoteEventTriggerTimestampMillis > 0) {
+ b.putLong(KEY_REMOTE_EVENT_TRIGGER_TIMESTAMP, mRemoteEventTriggerTimestampMillis);
+ }
if (mDeliveryGroupPolicy != DELIVERY_GROUP_POLICY_ALL) {
b.putInt(KEY_DELIVERY_GROUP_POLICY, mDeliveryGroupPolicy);
}
diff --git a/core/java/android/app/ComponentCaller.java b/core/java/android/app/ComponentCaller.java
index 44e8a0a3a20c..7e6a9ac9ff8e 100644
--- a/core/java/android/app/ComponentCaller.java
+++ b/core/java/android/app/ComponentCaller.java
@@ -17,6 +17,7 @@
package android.app;
import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -24,8 +25,6 @@ import android.net.Uri;
import android.os.IBinder;
import android.os.Process;
-import androidx.annotation.NonNull;
-
import java.util.Objects;
/**
@@ -45,7 +44,7 @@ public final class ComponentCaller {
/**
* @hide
*/
- public ComponentCaller(@NonNull IBinder activityToken, @Nullable IBinder callerToken) {
+ public ComponentCaller(@Nullable IBinder activityToken, @Nullable IBinder callerToken) {
mActivityToken = activityToken;
mCallerToken = callerToken;
}
@@ -83,7 +82,7 @@ public final class ComponentCaller {
* @see Activity#getLaunchedFromUid()
*/
public int getUid() {
- return ActivityClient.getInstance().getLaunchedFromUid(mActivityToken);
+ return ActivityClient.getInstance().getActivityCallerUid(mActivityToken, mCallerToken);
}
/**
@@ -121,7 +120,7 @@ public final class ComponentCaller {
*/
@Nullable
public String getPackage() {
- return ActivityClient.getInstance().getLaunchedFromPackage(mActivityToken);
+ return ActivityClient.getInstance().getActivityCallerPackage(mActivityToken, mCallerToken);
}
/**
@@ -138,16 +137,18 @@ public final class ComponentCaller {
* <li>This is not a real time check, i.e. the permissions have been computed at launch
* time.
* <li>This method will return the correct result for content URIs passed at launch time,
- * specifically the ones from {@link Intent#getData()}, and {@link Intent#getClipData()} in
- * the intent of {@code startActivity(intent)}. For others, it will throw an
- * {@link IllegalArgumentException}.
+ * specifically the ones from {@link Intent#getData()}, {@link Intent#EXTRA_STREAM}, and
+ * {@link Intent#getClipData()} in the intent of {@code startActivity(intent)}. For others,
+ * it will throw an {@link IllegalArgumentException}.
* </ul>
*
* @param uri The content uri that is being checked
* @param modeFlags The access modes to check
* @return {@link PackageManager#PERMISSION_GRANTED} if this activity caller is allowed to
* access that uri, or {@link PackageManager#PERMISSION_DENIED} if it is not
- * @throws IllegalArgumentException if uri is a non-content URI or it wasn't passed at launch
+ * @throws IllegalArgumentException if uri is a non-content URI or it wasn't passed at launch in
+ * {@link Intent#getData()}, {@link Intent#EXTRA_STREAM}, and
+ * {@link Intent#getClipData()}
* @throws SecurityException if you don't have access to uri
*
* @see android.content.Context#checkContentUriPermissionFull(Uri, int, int, int)
diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl
index 05fee72b7e61..9c8fea15c712 100644
--- a/core/java/android/app/IActivityClientController.aidl
+++ b/core/java/android/app/IActivityClientController.aidl
@@ -90,7 +90,9 @@ interface IActivityClientController {
ComponentName getCallingActivity(in IBinder token);
String getCallingPackage(in IBinder token);
int getLaunchedFromUid(in IBinder token);
+ int getActivityCallerUid(in IBinder activityToken, in IBinder callerToken);
String getLaunchedFromPackage(in IBinder token);
+ String getActivityCallerPackage(in IBinder activityToken, in IBinder callerToken);
int checkActivityCallerContentUriPermission(in IBinder activityToken, in IBinder callerToken,
in Uri uri, int modeFlags, int userId);
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index cc0aafde1f5c..7a95720c1cf4 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -554,11 +554,14 @@ interface IActivityManager {
void bootAnimationComplete();
/**
- * Used by {@link com.android.systemui.theme.ThemeOverlayController} to notify of color
- * palette readiness.
+ * Used by {@link com.android.systemui.theme.ThemeOverlayController} to notify when color
+ * palette is ready.
+ *
+ * @param userId The ID of the user where ThemeOverlayController is ready.
+ *
* @throws RemoteException
*/
- void setThemeOverlayReady(boolean readiness);
+ void setThemeOverlayReady(int userId);
@UnsupportedAppUsage
void registerTaskStackListener(in ITaskStackListener listener);
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index b5e355638ae8..8f81ae2ae7d6 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -225,6 +225,7 @@ interface INotificationManager
boolean removeAutomaticZenRule(String id, boolean fromUser);
boolean removeAutomaticZenRules(String packageName, boolean fromUser);
int getRuleInstanceCount(in ComponentName owner);
+ int getAutomaticZenRuleState(String id);
void setAutomaticZenRuleState(String id, in Condition condition);
byte[] getBackupPayload(int user);
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 454d60534476..db216b1af974 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -16,6 +16,7 @@
package android.app;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -144,7 +145,7 @@ public class Instrumentation {
* reflection, but it will serve as noticeable discouragement from
* doing such a thing.
*/
- @android.ravenwood.annotation.RavenwoodReplace
+ @android.ravenwood.annotation.RavenwoodKeep
private void checkInstrumenting(String method) {
// Check if we have an instrumentation context, as init should only get called by
// the system in startup processes that are being instrumented.
@@ -154,16 +155,12 @@ public class Instrumentation {
}
}
- private void checkInstrumenting$ravenwood(String method) {
- // At the moment, Ravenwood doesn't attach a Context, but we're only ever
- // running code as part of tests, so we continue quietly
- }
-
/**
* Returns if it is being called in an instrumentation environment.
*
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public boolean isInstrumenting() {
// Check if we have an instrumentation context, as init should only get called by
// the system in startup processes that are being instrumented.
@@ -327,6 +324,7 @@ public class Instrumentation {
*
* @see #getTargetContext
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public Context getContext() {
return mInstrContext;
}
@@ -351,6 +349,7 @@ public class Instrumentation {
*
* @see #getContext
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public Context getTargetContext() {
return mAppContext;
}
@@ -1624,26 +1623,75 @@ public class Instrumentation {
* @param intent The new intent being received.
*/
public void callActivityOnNewIntent(Activity activity, Intent intent) {
- activity.performNewIntent(intent);
+ if (android.security.Flags.contentUriPermissionApis()) {
+ activity.performNewIntent(intent, new ComponentCaller(activity.getActivityToken(),
+ /* callerToken */ null));
+ } else {
+ activity.performNewIntent(intent);
+ }
+ }
+
+ /**
+ * Same as {@link #callActivityOnNewIntent(Activity, Intent)}, but with an extra parameter for
+ * the {@link ComponentCaller} instance associated with the app that sent the intent.
+ *
+ * @param activity The activity receiving a new Intent.
+ * @param intent The new intent being received.
+ * @param caller The {@link ComponentCaller} instance that launched the activity with the new
+ * intent.
+ */
+ @FlaggedApi(android.security.Flags.FLAG_CONTENT_URI_PERMISSION_APIS)
+ public void callActivityOnNewIntent(@NonNull Activity activity, @NonNull Intent intent,
+ @NonNull ComponentCaller caller) {
+ activity.performNewIntent(intent, caller);
}
/**
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public void callActivityOnNewIntent(Activity activity, ReferrerIntent intent) {
+ @FlaggedApi(android.security.Flags.FLAG_CONTENT_URI_PERMISSION_APIS)
+ public void callActivityOnNewIntent(Activity activity, ReferrerIntent intent,
+ @NonNull ComponentCaller caller) {
+ internalCallActivityOnNewIntent(activity, intent, caller);
+ }
+
+ @FlaggedApi(android.security.Flags.FLAG_CONTENT_URI_PERMISSION_APIS)
+ private void internalCallActivityOnNewIntent(Activity activity, ReferrerIntent intent,
+ @NonNull ComponentCaller caller) {
final String oldReferrer = activity.mReferrer;
try {
if (intent != null) {
activity.mReferrer = intent.mReferrer;
}
- callActivityOnNewIntent(activity, intent != null ? new Intent(intent) : null);
+ Intent newIntent = intent != null ? new Intent(intent) : null;
+ callActivityOnNewIntent(activity, newIntent, caller);
} finally {
activity.mReferrer = oldReferrer;
}
}
/**
+ * @hide
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ public void callActivityOnNewIntent(Activity activity, ReferrerIntent intent) {
+ if (android.security.Flags.contentUriPermissionApis()) {
+ internalCallActivityOnNewIntent(activity, intent, new ComponentCaller(
+ activity.getActivityToken(), /* callerToken */ null));
+ } else {
+ final String oldReferrer = activity.mReferrer;
+ try {
+ if (intent != null) {
+ activity.mReferrer = intent.mReferrer;
+ }
+ callActivityOnNewIntent(activity, intent != null ? new Intent(intent) : null);
+ } finally {
+ activity.mReferrer = oldReferrer;
+ }
+ }
+ }
+
+ /**
* Perform calling of an activity's {@link Activity#onStart}
* method. The default implementation simply calls through to that method.
*
@@ -2352,6 +2400,17 @@ public class Instrumentation {
mThread = thread;
}
+ /**
+ * Only sets the Context up, keeps everything else null.
+ *
+ * @hide
+ */
+ @android.ravenwood.annotation.RavenwoodKeep
+ public final void basicInit(Context context) {
+ mInstrContext = context;
+ mAppContext = context;
+ }
+
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public static void checkStartActivityResult(int res, Object intent) {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index d6e8ae3e5dff..26f85f723bd9 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -80,6 +80,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
@@ -3023,37 +3024,44 @@ public class Notification implements Parcelable
* @hide
*/
public String loadHeaderAppName(Context context) {
- CharSequence name = null;
- // Check if there is a non-empty substitute app name and return that.
- if (extras.containsKey(EXTRA_SUBSTITUTE_APP_NAME)) {
- name = extras.getString(EXTRA_SUBSTITUTE_APP_NAME);
- if (!TextUtils.isEmpty(name)) {
- return name.toString();
+ Trace.beginSection("Notification#loadHeaderAppName");
+
+ try {
+ CharSequence name = null;
+ // Check if there is a non-empty substitute app name and return that.
+ if (extras.containsKey(EXTRA_SUBSTITUTE_APP_NAME)) {
+ name = extras.getString(EXTRA_SUBSTITUTE_APP_NAME);
+ if (!TextUtils.isEmpty(name)) {
+ return name.toString();
+ }
}
- }
- // If not, try getting the app info from extras.
- if (context == null) {
- return null;
- }
- final PackageManager pm = context.getPackageManager();
- if (TextUtils.isEmpty(name)) {
- if (extras.containsKey(EXTRA_BUILDER_APPLICATION_INFO)) {
- final ApplicationInfo info = extras.getParcelable(EXTRA_BUILDER_APPLICATION_INFO,
- ApplicationInfo.class);
- if (info != null) {
- name = pm.getApplicationLabel(info);
+ // If not, try getting the app info from extras.
+ if (context == null) {
+ return null;
+ }
+ final PackageManager pm = context.getPackageManager();
+ if (TextUtils.isEmpty(name)) {
+ if (extras.containsKey(EXTRA_BUILDER_APPLICATION_INFO)) {
+ final ApplicationInfo info = extras.getParcelable(
+ EXTRA_BUILDER_APPLICATION_INFO,
+ ApplicationInfo.class);
+ if (info != null) {
+ name = pm.getApplicationLabel(info);
+ }
}
}
+ // If that's still empty, use the one from the context directly.
+ if (TextUtils.isEmpty(name)) {
+ name = pm.getApplicationLabel(context.getApplicationInfo());
+ }
+ // If there's still nothing, ¯\_(ツ)_/¯
+ if (TextUtils.isEmpty(name)) {
+ return null;
+ }
+ return name.toString();
+ } finally {
+ Trace.endSection();
}
- // If that's still empty, use the one from the context directly.
- if (TextUtils.isEmpty(name)) {
- name = pm.getApplicationLabel(context.getApplicationInfo());
- }
- // If there's still nothing, ¯\_(ツ)_/¯
- if (TextUtils.isEmpty(name)) {
- return null;
- }
- return name.toString();
}
/**
@@ -6722,23 +6730,29 @@ public class Notification implements Parcelable
*/
@NonNull
public static Notification.Builder recoverBuilder(Context context, Notification n) {
- // Re-create notification context so we can access app resources.
- ApplicationInfo applicationInfo = n.extras.getParcelable(
- EXTRA_BUILDER_APPLICATION_INFO, ApplicationInfo.class);
- Context builderContext;
- if (applicationInfo != null) {
- try {
- builderContext = context.createApplicationContext(applicationInfo,
- Context.CONTEXT_RESTRICTED);
- } catch (NameNotFoundException e) {
- Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
- builderContext = context; // try with our context
+ Trace.beginSection("Notification.Builder#recoverBuilder");
+
+ try {
+ // Re-create notification context so we can access app resources.
+ ApplicationInfo applicationInfo = n.extras.getParcelable(
+ EXTRA_BUILDER_APPLICATION_INFO, ApplicationInfo.class);
+ Context builderContext;
+ if (applicationInfo != null) {
+ try {
+ builderContext = context.createApplicationContext(applicationInfo,
+ Context.CONTEXT_RESTRICTED);
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
+ builderContext = context; // try with our context
+ }
+ } else {
+ builderContext = context; // try with given context
}
- } else {
- builderContext = context; // try with given context
- }
- return new Builder(builderContext, n);
+ return new Builder(builderContext, n);
+ } finally {
+ Trace.endSection();
+ }
}
/**
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 9dfb5b0dedbd..d49a2542eed8 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1406,6 +1406,26 @@ public class NotificationManager {
}
/**
+ * Returns the current activation state of an {@link AutomaticZenRule}.
+ *
+ * <p>Returns {@link Condition#STATE_UNKNOWN} if the rule does not exist or the calling
+ * package doesn't have access to it.
+ *
+ * @param id The id of the rule
+ * @return the state of the rule.
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ @Condition.State
+ public int getAutomaticZenRuleState(@NonNull String id) {
+ INotificationManager service = getService();
+ try {
+ return service.getAutomaticZenRuleState(id);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Informs the notification manager that the state of an {@link AutomaticZenRule} has changed.
* Use this method to put the system into Do Not Disturb mode or request that it exits Do Not
* Disturb mode. The calling app must own the provided {@link android.app.AutomaticZenRule}.
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index f92ff83919c7..41b97d0ad5d2 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -31,6 +31,7 @@ per-file Service* = file:/services/core/java/com/android/server/am/OWNERS
per-file SystemServiceRegistry.java = file:/services/core/java/com/android/server/am/OWNERS
per-file *UserSwitchObserver* = file:/services/core/java/com/android/server/am/OWNERS
per-file *UiAutomation* = file:/services/accessibility/OWNERS
+per-file *UiAutomation* = file:/core/java/android/permission/OWNERS
per-file GameManager* = file:/GAME_MANAGER_OWNERS
per-file GameMode* = file:/GAME_MANAGER_OWNERS
per-file GameState* = file:/GAME_MANAGER_OWNERS
@@ -117,6 +118,8 @@ per-file *ScreenCapture* = file:/services/core/java/com/android/server/wm/OWNERS
# Multitasking
per-file multitasking.aconfig = file:/services/core/java/com/android/server/wm/OWNERS
per-file multitasking.aconfig = file:/libs/WindowManager/Shell/OWNERS
+per-file PictureInPicture* = file:/services/core/java/com/android/server/wm/OWNERS
+per-file PictureInPicture* = file:/libs/WindowManager/Shell/OWNERS
# Zygote
per-file *Zygote* = file:/ZYGOTE_OWNERS
diff --git a/core/java/android/app/PictureInPictureUiState.java b/core/java/android/app/PictureInPictureUiState.java
index 39ba54cfeb0f..162953662d39 100644
--- a/core/java/android/app/PictureInPictureUiState.java
+++ b/core/java/android/app/PictureInPictureUiState.java
@@ -31,12 +31,12 @@ import java.util.Objects;
public final class PictureInPictureUiState implements Parcelable {
private final boolean mIsStashed;
- private final boolean mIsEnteringPip;
+ private final boolean mIsTransitioningToPip;
/** {@hide} */
PictureInPictureUiState(Parcel in) {
mIsStashed = in.readBoolean();
- mIsEnteringPip = in.readBoolean();
+ mIsTransitioningToPip = in.readBoolean();
}
/** {@hide} */
@@ -45,9 +45,9 @@ public final class PictureInPictureUiState implements Parcelable {
this(isStashed, false /* isEnteringPip */);
}
- private PictureInPictureUiState(boolean isStashed, boolean isEnteringPip) {
+ private PictureInPictureUiState(boolean isStashed, boolean isTransitioningToPip) {
mIsStashed = isStashed;
- mIsEnteringPip = isEnteringPip;
+ mIsTransitioningToPip = isTransitioningToPip;
}
/**
@@ -77,14 +77,14 @@ public final class PictureInPictureUiState implements Parcelable {
* whether via auto enter PiP or calling
* {@link Activity#enterPictureInPictureMode(PictureInPictureParams)} explicitly, app can expect
* {@link Activity#onPictureInPictureUiStateChanged(PictureInPictureUiState)} callback with
- * {@link #isEnteringPip()} to be {@code true} first,
+ * {@link #isTransitioningToPip()} to be {@code true} first,
* followed by {@link Activity#onPictureInPictureModeChanged(boolean, Configuration)} when it
* fully settles in PiP mode.
*
* When app receives the
* {@link Activity#onPictureInPictureUiStateChanged(PictureInPictureUiState)} callback with
- * {@link #isEnteringPip()} being {@code true}, it's recommended to hide certain UI elements,
- * such as video controls, to archive a clean entering PiP animation.
+ * {@link #isTransitioningToPip()} being {@code true}, it's recommended to hide certain UI
+ * elements, such as video controls, to archive a clean entering PiP animation.
*
* In case an application wants to restore the previously hidden UI elements when exiting
* PiP, it is recommended to do that in
@@ -92,8 +92,8 @@ public final class PictureInPictureUiState implements Parcelable {
* than the beginning of exit PiP animation.
*/
@FlaggedApi(Flags.FLAG_ENABLE_PIP_UI_STATE_CALLBACK_ON_ENTERING)
- public boolean isEnteringPip() {
- return mIsEnteringPip;
+ public boolean isTransitioningToPip() {
+ return mIsTransitioningToPip;
}
@Override
@@ -102,12 +102,12 @@ public final class PictureInPictureUiState implements Parcelable {
if (!(o instanceof PictureInPictureUiState)) return false;
PictureInPictureUiState that = (PictureInPictureUiState) o;
return mIsStashed == that.mIsStashed
- && mIsEnteringPip == that.mIsEnteringPip;
+ && mIsTransitioningToPip == that.mIsTransitioningToPip;
}
@Override
public int hashCode() {
- return Objects.hash(mIsStashed, mIsEnteringPip);
+ return Objects.hash(mIsStashed, mIsTransitioningToPip);
}
@Override
@@ -118,7 +118,7 @@ public final class PictureInPictureUiState implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeBoolean(mIsStashed);
- out.writeBoolean(mIsEnteringPip);
+ out.writeBoolean(mIsTransitioningToPip);
}
public static final @android.annotation.NonNull Creator<PictureInPictureUiState> CREATOR =
@@ -138,7 +138,7 @@ public final class PictureInPictureUiState implements Parcelable {
@FlaggedApi(Flags.FLAG_ENABLE_PIP_UI_STATE_CALLBACK_ON_ENTERING)
public static final class Builder {
private boolean mIsStashed;
- private boolean mIsEnteringPip;
+ private boolean mIsTransitioningToPip;
/** Empty constructor. */
public Builder() {
@@ -154,11 +154,11 @@ public final class PictureInPictureUiState implements Parcelable {
}
/**
- * Sets the {@link #mIsEnteringPip} state.
+ * Sets the {@link #mIsTransitioningToPip} state.
* @return The same {@link Builder} instance.
*/
- public Builder setEnteringPip(boolean isEnteringPip) {
- mIsEnteringPip = isEnteringPip;
+ public Builder setTransitioningToPip(boolean isEnteringPip) {
+ mIsTransitioningToPip = isEnteringPip;
return this;
}
@@ -166,7 +166,7 @@ public final class PictureInPictureUiState implements Parcelable {
* @return The constructed {@link PictureInPictureUiState} instance.
*/
public PictureInPictureUiState build() {
- return new PictureInPictureUiState(mIsStashed, mIsEnteringPip);
+ return new PictureInPictureUiState(mIsStashed, mIsTransitioningToPip);
}
}
}
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 24a51573b48a..625526047212 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -550,7 +550,7 @@ public class ResourcesManager {
@UnsupportedAppUsage
protected @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key,
@Nullable ApkAssetsSupplier apkSupplier) {
- final AssetManager.Builder builder = new AssetManager.Builder();
+ final AssetManager.Builder builder = new AssetManager.Builder().setNoInit();
final ArrayList<ApkKey> apkKeys = extractApkKeys(key);
for (int i = 0, n = apkKeys.size(); i < n; i++) {
@@ -1555,7 +1555,7 @@ public class ResourcesManager {
} else if(overlayPaths == null) {
return ArrayUtils.cloneOrNull(resourceDirs);
} else {
- final ArrayList<String> paths = new ArrayList<>();
+ final var paths = new ArrayList<String>(overlayPaths.length + resourceDirs.length);
for (final String path : overlayPaths) {
paths.add(path);
}
diff --git a/core/java/android/app/ResultInfo.java b/core/java/android/app/ResultInfo.java
index 535f69f0e264..213c38c71543 100644
--- a/core/java/android/app/ResultInfo.java
+++ b/core/java/android/app/ResultInfo.java
@@ -20,6 +20,7 @@ import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Intent;
import android.os.Build;
+import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
@@ -36,14 +37,21 @@ public class ResultInfo implements Parcelable {
public final int mResultCode;
@UnsupportedAppUsage
public final Intent mData;
+ public final IBinder mCallerToken;
@UnsupportedAppUsage
public ResultInfo(String resultWho, int requestCode, int resultCode,
Intent data) {
+ this(resultWho, requestCode, resultCode, data, /* callerToken */ null);
+ }
+
+ public ResultInfo(String resultWho, int requestCode, int resultCode,
+ Intent data, IBinder callerToken) {
mResultWho = resultWho;
mRequestCode = requestCode;
mResultCode = resultCode;
mData = data;
+ mCallerToken = callerToken;
}
public String toString() {
@@ -65,6 +73,7 @@ public class ResultInfo implements Parcelable {
} else {
out.writeInt(0);
}
+ out.writeStrongBinder(mCallerToken);
}
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -88,6 +97,7 @@ public class ResultInfo implements Parcelable {
} else {
mData = null;
}
+ mCallerToken = in.readStrongBinder();
}
@Override
@@ -100,7 +110,8 @@ public class ResultInfo implements Parcelable {
: mData.filterEquals(other.mData);
return intentsEqual && Objects.equals(mResultWho, other.mResultWho)
&& mResultCode == other.mResultCode
- && mRequestCode == other.mRequestCode;
+ && mRequestCode == other.mRequestCode
+ && mCallerToken == other.mCallerToken;
}
@Override
@@ -112,6 +123,7 @@ public class ResultInfo implements Parcelable {
if (mData != null) {
result = 31 * result + mData.filterHashCode();
}
+ result = 31 * result + Objects.hashCode(mCallerToken);
return result;
}
}
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index d4702998ce3e..fe8655c13562 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -1164,7 +1164,7 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
}
/** @hide */
- public final void callOnTimeLimitExceeded(int startId, int fgsType) {
+ public final void callOnTimeLimitExceeded(int startId, @ForegroundServiceType int fgsType) {
// Note, because all the service callbacks (and other similar callbacks, e.g. activity
// callbacks) are delivered using the main handler, it's possible the service is already
// stopped when before this method is called, so we do a double check here.
@@ -1189,10 +1189,11 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
* Callback called when a particular foreground service type has timed out.
*
* @param startId the startId passed to {@link #onStartCommand(Intent, int, int)} when
- * the service started.
- * @param fgsType the foreground service type which caused the timeout.
+ * the service started.
+ * @param fgsType the {@link ServiceInfo.ForegroundServiceType foreground service type} which
+ * caused the timeout.
*/
@FlaggedApi(Flags.FLAG_INTRODUCE_NEW_SERVICE_ONTIMEOUT_CALLBACK)
- public void onTimeout(int startId, int fgsType) {
+ public void onTimeout(int startId, @ForegroundServiceType int fgsType) {
}
}
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index e6e46ddaa420..301fef877d6c 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -1448,7 +1448,6 @@ public class StatusBarManager {
*
* @hide
*/
- @SystemApi
public boolean isBackDisabled() {
return mBack;
}
@@ -1862,38 +1861,38 @@ public class StatusBarManager {
};
@DataClass.Generated(
- time = 1707345957771L,
+ time = 1708625947132L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/app/StatusBarManager.java",
- inputSignatures = "private boolean mStatusBarExpansion\nprivate "
- + "boolean mNavigateHome\nprivate boolean mNotificationPeeking\nprivate "
- + "boolean mRecents\nprivate boolean mBack\nprivate boolean "
- + "mSearch\nprivate boolean mSystemIcons\nprivate boolean mClock\nprivate"
- + " boolean mNotificationIcons\nprivate boolean mRotationSuggestion\n"
+ inputSignatures = "private boolean mStatusBarExpansion\nprivate boolean "
+ + "mNavigateHome\nprivate boolean mNotificationPeeking\nprivate "
+ + "boolean mRecents\nprivate boolean mBack\nprivate boolean mSearch\n"
+ + "private boolean mSystemIcons\nprivate boolean mClock\nprivate "
+ + "boolean mNotificationIcons\nprivate boolean mRotationSuggestion\n"
+ "private boolean mNotificationTicker\npublic "
+ "@android.annotation.SystemApi boolean isStatusBarExpansionDisabled()\n"
+ "public void setStatusBarExpansionDisabled(boolean)\npublic "
- + "@android.annotation.SystemApi boolean isNavigateToHomeDisabled()\n"
- + "public void setNavigationHomeDisabled(boolean)\npublic "
- + "@android.annotation.SystemApi boolean isNotificationPeekingDisabled()\n"
- + "public void setNotificationPeekingDisabled(boolean)\npublic "
+ + "@android.annotation.SystemApi boolean isNavigateToHomeDisabled()\npublic"
+ + " void setNavigationHomeDisabled(boolean)\npublic "
+ + "@android.annotation.SystemApi boolean isNotificationPeekingDisabled()"
+ + "\npublic void setNotificationPeekingDisabled(boolean)\npublic "
+ "@android.annotation.SystemApi boolean isRecentsDisabled()\npublic "
- + "void setRecentsDisabled(boolean)\npublic @android.annotation.SystemApi "
- + "boolean isBackDisabled()\npublic void setBackDisabled(boolean)\npublic "
+ + "void setRecentsDisabled(boolean)\npublic boolean isBackDisabled()"
+ + "\npublic void setBackDisabled(boolean)\npublic "
+ "@android.annotation.SystemApi boolean isSearchDisabled()\npublic "
+ "void setSearchDisabled(boolean)\npublic boolean "
- + "areSystemIconsDisabled()\npublic void setSystemIconsDisabled(boolean)"
- + "\npublic boolean isClockDisabled()\npublic "
- + "void setClockDisabled(boolean)\npublic "
- + "boolean areNotificationIconsDisabled()\npublic "
- + "void setNotificationIconsDisabled(boolean)\npublic boolean "
+ + "areSystemIconsDisabled()\npublic void setSystemIconsDisabled(boolean)\n"
+ + "public boolean isClockDisabled()\npublic "
+ + "void setClockDisabled(boolean)\npublic boolean "
+ + "areNotificationIconsDisabled()\npublic void "
+ + "setNotificationIconsDisabled(boolean)\npublic boolean "
+ "isNotificationTickerDisabled()\npublic void "
+ "setNotificationTickerDisabled(boolean)\npublic "
+ "@android.annotation.TestApi boolean isRotationSuggestionDisabled()\n"
+ "public void setRotationSuggestionDisabled(boolean)\npublic "
- + "@android.annotation.SystemApi boolean areAllComponentsEnabled()\n"
- + "public void setEnableAll()\npublic boolean areAllComponentsDisabled()"
- + "\npublic void setDisableAll()\npublic @android.annotation.NonNull "
+ + "@android.annotation.SystemApi boolean areAllComponentsEnabled()\npublic"
+ + " void setEnableAll()\npublic boolean areAllComponentsDisabled()\n"
+ + "public void setDisableAll()\npublic @android.annotation.NonNull "
+ "@java.lang.Override java.lang.String toString()\npublic "
+ "android.util.Pair<java.lang.Integer,java.lang.Integer> toFlags()\n"
+ "class DisableInfo extends java.lang.Object implements "
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index a045eae9e108..7903f1c0c5c3 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -16,7 +16,7 @@
package android.app;
-import static android.app.Flags.enableNightModeCache;
+import static android.app.Flags.enableNightModeBinderCache;
import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
@@ -916,7 +916,7 @@ public class UiModeManager {
*
* @hide
*/
- @FlaggedApi(Flags.FLAG_ENABLE_NIGHT_MODE_CACHE)
+ @FlaggedApi(Flags.FLAG_ENABLE_NIGHT_MODE_BINDER_CACHE)
public static void invalidateNightModeCache() {
IpcDataCache.invalidateCache(IpcDataCache.MODULE_SYSTEM,
NIGHT_MODE_API);
@@ -938,7 +938,7 @@ public class UiModeManager {
* @see #setNightMode(int)
*/
public @NightMode int getNightMode() {
- if (enableNightModeCache()) {
+ if (enableNightModeBinderCache()) {
return mNightModeCache.query(null);
} else {
return getNightModeFromServer();
diff --git a/core/java/android/app/activity_manager.aconfig b/core/java/android/app/activity_manager.aconfig
index ff23f09f78a5..350b1edf2129 100644
--- a/core/java/android/app/activity_manager.aconfig
+++ b/core/java/android/app/activity_manager.aconfig
@@ -34,3 +34,10 @@ flag {
description: "Add a new callback in Service to indicate a FGS has reached its timeout."
bug: "317799821"
}
+
+flag {
+ name: "bcast_event_timestamps"
+ namespace: "backstage_power"
+ description: "Add APIs for clients to provide broadcast event trigger timestamps"
+ bug: "325136414"
+}
diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java
index 7d5d5c162271..986205a346f7 100644
--- a/core/java/android/app/admin/DeviceAdminInfo.java
+++ b/core/java/android/app/admin/DeviceAdminInfo.java
@@ -16,10 +16,11 @@
package android.app.admin;
+import static android.app.admin.flags.Flags.FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED;
+
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.app.admin.flags.Flags;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
@@ -185,7 +186,7 @@ public final class DeviceAdminInfo implements Parcelable {
* <p>This mode only allows a single secondary user on the device blocking the creation of
* additional secondary users.
*/
- @FlaggedApi(Flags.FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED)
+ @FlaggedApi(FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED)
public static final int HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER = 2;
@IntDef({HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED, HEADLESS_DEVICE_OWNER_MODE_AFFILIATED,
diff --git a/core/java/android/app/admin/DevicePolicyCache.java b/core/java/android/app/admin/DevicePolicyCache.java
index 29f657ec6ba7..16cb4ecc4cca 100644
--- a/core/java/android/app/admin/DevicePolicyCache.java
+++ b/core/java/android/app/admin/DevicePolicyCache.java
@@ -15,6 +15,9 @@
*/
package android.app.admin;
+import static android.app.admin.DevicePolicyManager.CONTENT_PROTECTION_DISABLED;
+import static android.app.admin.DevicePolicyManager.ContentProtectionPolicy;
+
import android.annotation.UserIdInt;
import com.android.server.LocalServices;
@@ -59,6 +62,12 @@ public abstract class DevicePolicyCache {
public abstract int getPermissionPolicy(@UserIdInt int userHandle);
/**
+ * Caches {@link DevicePolicyManager#getContentProtectionPolicy(android.content.ComponentName)}
+ * of the given user.
+ */
+ public abstract @ContentProtectionPolicy int getContentProtectionPolicy(@UserIdInt int userId);
+
+ /**
* True if there is an admin on the device who can grant sensor permissions.
*/
public abstract boolean canAdminGrantSensorsPermissions();
@@ -92,6 +101,11 @@ public abstract class DevicePolicyCache {
}
@Override
+ public @ContentProtectionPolicy int getContentProtectionPolicy(@UserIdInt int userId) {
+ return CONTENT_PROTECTION_DISABLED;
+ }
+
+ @Override
public boolean canAdminGrantSensorsPermissions() {
return false;
}
diff --git a/core/java/android/app/admin/DevicePolicyIdentifiers.java b/core/java/android/app/admin/DevicePolicyIdentifiers.java
index 318b2fb7b980..eeaf0b3706fc 100644
--- a/core/java/android/app/admin/DevicePolicyIdentifiers.java
+++ b/core/java/android/app/admin/DevicePolicyIdentifiers.java
@@ -16,12 +16,13 @@
package android.app.admin;
+import static android.app.admin.flags.Flags.FLAG_POLICY_ENGINE_MIGRATION_V2_ENABLED;
import static android.app.admin.flags.Flags.FLAG_SECURITY_LOG_V2_ENABLED;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.app.admin.flags.Flags;
import android.os.UserManager;
import java.util.Objects;
@@ -53,6 +54,15 @@ public final class DevicePolicyIdentifiers {
public static final String SECURITY_LOGGING_POLICY = "securityLogging";
/**
+ * String identifier for {@link DevicePolicyManager#setAuditLogEnabled}.
+ *
+ * @hide
+ */
+ @FlaggedApi(FLAG_SECURITY_LOG_V2_ENABLED)
+ @SystemApi
+ public static final String AUDIT_LOGGING_POLICY = "auditLogging";
+
+ /**
* String identifier for {@link DevicePolicyManager#setLockTaskPackages}.
*/
public static final String LOCK_TASK_POLICY = "lockTask";
@@ -178,10 +188,16 @@ public final class DevicePolicyIdentifiers {
/**
* String identifier for {@link DevicePolicyManager#setUsbDataSignalingEnabled}.
*/
- @FlaggedApi(Flags.FLAG_POLICY_ENGINE_MIGRATION_V2_ENABLED)
+ @FlaggedApi(FLAG_POLICY_ENGINE_MIGRATION_V2_ENABLED)
public static final String USB_DATA_SIGNALING_POLICY = "usbDataSignaling";
/**
+ * String identifier for {@link DevicePolicyManager#setRequiredPasswordComplexity}.
+ */
+ @FlaggedApi(FLAG_POLICY_ENGINE_MIGRATION_V2_ENABLED)
+ public static final String PASSWORD_COMPLEXITY_POLICY = "passwordComplexity";
+
+ /**
* @hide
*/
public static final String USER_RESTRICTION_PREFIX = "userRestriction_";
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 5c6308f209fe..083705bca09e 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -37,6 +37,7 @@ import static android.Manifest.permission.MANAGE_DEVICE_POLICY_MTE;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_PACKAGE_STATE;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_PROFILE_INTERACTION;
+import static android.Manifest.permission.MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_RESET_PASSWORD;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SCREEN_CAPTURE;
@@ -44,6 +45,7 @@ import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SECURITY_LOGGING;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_STATUS_BAR;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SYSTEM_UPDATES;
+import static android.Manifest.permission.MANAGE_DEVICE_POLICY_THEFT_DETECTION;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_WIFI;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_WIPE_DATA;
@@ -51,8 +53,14 @@ import static android.Manifest.permission.QUERY_ADMIN_POLICY;
import static android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY;
import static android.Manifest.permission.SET_TIME;
import static android.Manifest.permission.SET_TIME_ZONE;
+import static android.app.admin.flags.Flags.FLAG_DEVICE_THEFT_API_ENABLED;
import static android.app.admin.flags.Flags.FLAG_ESIM_MANAGEMENT_ENABLED;
+import static android.app.admin.flags.Flags.FLAG_DEVICE_POLICY_SIZE_TRACKING_ENABLED;
+import static android.app.admin.flags.Flags.FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED;
+import static android.app.admin.flags.Flags.FLAG_PERMISSION_MIGRATION_FOR_ZERO_TRUST_API_ENABLED;
+import static android.app.admin.flags.Flags.FLAG_SECURITY_LOG_V2_ENABLED;
import static android.app.admin.flags.Flags.onboardingBugreportV2Enabled;
+import static android.app.admin.flags.Flags.FLAG_IS_MTE_POLICY_ENFORCED;
import static android.content.Intent.LOCAL_FLAG_FROM_SYSTEM;
import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
@@ -85,7 +93,6 @@ import android.app.Activity;
import android.app.IServiceConnection;
import android.app.KeyguardManager;
import android.app.admin.SecurityLog.SecurityEvent;
-import android.app.admin.flags.Flags;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
@@ -148,6 +155,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.net.NetworkUtilsInternal;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.os.Zygote;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
import com.android.org.conscrypt.TrustedCertificateStore;
@@ -228,7 +236,6 @@ public class DevicePolicyManager {
private final boolean mParentInstance;
private final DevicePolicyResourcesManager mResourcesManager;
-
/** @hide */
public DevicePolicyManager(Context context, IDevicePolicyManager service) {
this(context, service, false);
@@ -2874,7 +2881,7 @@ public class DevicePolicyManager {
* @hide
*/
@SystemApi
- @FlaggedApi(Flags.FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED)
+ @FlaggedApi(FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED)
public static final int STATUS_HEADLESS_ONLY_SYSTEM_USER = 17;
/**
@@ -4115,6 +4122,19 @@ public class DevicePolicyManager {
return MTE_NOT_CONTROLLED_BY_POLICY;
}
+ /**
+ * Get the current MTE state of the device.
+ *
+ * <a href="https://source.android.com/docs/security/test/memory-safety/arm-mte">
+ * Learn more about MTE</a>
+ *
+ * @return whether MTE is currently enabled on the device.
+ */
+ @FlaggedApi(FLAG_IS_MTE_POLICY_ENFORCED)
+ public static boolean isMtePolicyEnforced() {
+ return Zygote.nativeSupportsMemoryTagging();
+ }
+
/** Indicates that content protection is not controlled by policy, allowing user to choose. */
@FlaggedApi(android.view.contentprotection.flags.Flags.FLAG_MANAGE_DEVICE_POLICY_ENABLED)
public static final int CONTENT_PROTECTION_NOT_CONTROLLED_BY_POLICY = 0;
@@ -13415,17 +13435,25 @@ public class DevicePolicyManager {
}
/**
- * Called by device or profile owners to get information about a pending system update.
+ * Get information about a pending system update.
+ *
+ * Can be called by device or profile owners, and starting from Android
+ * {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}, holders of the permission
+ * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES}.
*
* @param admin Which profile or device owner this request is associated with.
* @return Information about a pending system update or {@code null} if no update pending.
- * @throws SecurityException if {@code admin} is not a device or profile owner.
+ * @throws SecurityException if {@code admin} is not a device, profile owner or holders of
+ * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES}.
* @see DeviceAdminReceiver#onSystemUpdatePending(Context, Intent, long)
*/
- public @Nullable SystemUpdateInfo getPendingSystemUpdate(@NonNull ComponentName admin) {
+ @RequiresPermission(value = MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES, conditional = true)
+ @SuppressLint("RequiresPermission")
+ @FlaggedApi(FLAG_PERMISSION_MIGRATION_FOR_ZERO_TRUST_API_ENABLED)
+ public @Nullable SystemUpdateInfo getPendingSystemUpdate(@Nullable ComponentName admin) {
throwIfParentInstance("getPendingSystemUpdate");
try {
- return mService.getPendingSystemUpdate(admin);
+ return mService.getPendingSystemUpdate(admin, mContext.getPackageName());
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -14033,6 +14061,74 @@ public class DevicePolicyManager {
}
/**
+ * Controls whether audit logging is enabled.
+ *
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(FLAG_SECURITY_LOG_V2_ENABLED)
+ @RequiresPermission(permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING)
+ public void setAuditLogEnabled(boolean enabled) {
+ throwIfParentInstance("setAuditLogEnabled");
+ try {
+ mService.setAuditLogEnabled(mContext.getPackageName(), true);
+ } catch (RemoteException re) {
+ re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @return Whether audit logging is enabled.
+ *
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(FLAG_SECURITY_LOG_V2_ENABLED)
+ @RequiresPermission(permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING)
+ public boolean isAuditLogEnabled() {
+ throwIfParentInstance("isAuditLogEnabled");
+ try {
+ return mService.isAuditLogEnabled(mContext.getPackageName());
+ } catch (RemoteException re) {
+ re.rethrowFromSystemServer();
+ // unreachable
+ return false;
+ }
+ }
+
+ /**
+ * Sets audit log event callback. Only one callback per UID is active at any time, when a new
+ * callback is set, the previous one is forgotten. Should only be called when audit log policy
+ * is enforced by the caller. Disabling the policy clears the callback. Each time a new callback
+ * is set, it will first be invoked with all the audit log events available at the time.
+ *
+ * @param callback callback to invoke when new audit log events become available or {@code null}
+ * to clear the callback.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(FLAG_SECURITY_LOG_V2_ENABLED)
+ @RequiresPermission(permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING)
+ public void setAuditLogEventCallback(
+ @NonNull @CallbackExecutor Executor executor,
+ @Nullable Consumer<List<SecurityEvent>> callback) {
+ throwIfParentInstance("setAuditLogEventCallback");
+ final IAuditLogEventsCallback wrappedCallback = callback == null
+ ? null
+ : new IAuditLogEventsCallback.Stub() {
+ @Override
+ public void onNewAuditLogEvents(List<SecurityEvent> events) {
+ executor.execute(() -> callback.accept(events));
+ }
+ };
+ try {
+ mService.setAuditLogEventsCallback(mContext.getPackageName(), wrappedCallback);
+ } catch (RemoteException re) {
+ re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Called by device owner or profile owner of an organization-owned managed profile to retrieve
* all new security logging entries since the last call to this API after device boots.
*
@@ -16494,8 +16590,9 @@ public class DevicePolicyManager {
* The identifier would be consistent even if the work profile is removed and enrolled again
* (to the same organization), or the device is factory reset and re-enrolled.
*
- * Can only be called by the Profile Owner or Device Owner, if the
- * {@link #setOrganizationId(String)} was previously called.
+ * Can only be called by the Profile Owner and Device Owner, and starting from Android
+ * {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}, holders of the permission
+ * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_CERTIFICATES}.
* If {@link #setOrganizationId(String)} was not called, then the returned value will be an
* empty string.
*
@@ -16508,8 +16605,12 @@ public class DevicePolicyManager {
* and must switch to using this method.
*
* @return A stable, enrollment-specific identifier.
- * @throws SecurityException if the caller is not a profile owner or device owner.
+ * @throws SecurityException if the caller is not a profile owner, device owner or holding the
+ * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_CERTIFICATES} permission
*/
+ @RequiresPermission(value = MANAGE_DEVICE_POLICY_CERTIFICATES, conditional = true)
+ @SuppressLint("RequiresPermission")
+ @FlaggedApi(FLAG_PERMISSION_MIGRATION_FOR_ZERO_TRUST_API_ENABLED)
@NonNull public String getEnrollmentSpecificId() {
throwIfParentInstance("getEnrollmentSpecificId");
if (mService == null) {
@@ -17029,6 +17130,26 @@ public class DevicePolicyManager {
}
/**
+ *
+ * Returns whether the device considers itself to be potentially stolen.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(value = MANAGE_DEVICE_POLICY_THEFT_DETECTION)
+ @FlaggedApi(FLAG_DEVICE_THEFT_API_ENABLED)
+ public boolean isTheftDetectionTriggered() {
+ throwIfParentInstance("isTheftDetectionTriggered");
+ if (mService == null) {
+ return false;
+ }
+ try {
+ return mService.isTheftDetectionTriggered(mContext.getPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns a {@link DevicePolicyResourcesManager} containing the required APIs to set, reset,
* and get device policy related resources.
*/
@@ -17348,4 +17469,46 @@ public class DevicePolicyManager {
}
return new HashSet<>();
}
+
+ /**
+ * Controls the maximum storage size allowed for policies associated with an admin.
+ * Setting a limit of -1 effectively removes any storage restrictions.
+ *
+ * @param storageLimit Maximum storage allowed in bytes. Use -1 to disable limits.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
+ @FlaggedApi(FLAG_DEVICE_POLICY_SIZE_TRACKING_ENABLED)
+ public void setMaxPolicyStorageLimit(int storageLimit) {
+ if (mService != null) {
+ try {
+ mService.setMaxPolicyStorageLimit(mContext.getPackageName(), storageLimit);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Retrieves the current maximum storage limit for policies associated with an admin.
+ *
+ * @return The maximum storage limit in bytes, or -1 if no limit is enforced.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
+ @FlaggedApi(FLAG_DEVICE_POLICY_SIZE_TRACKING_ENABLED)
+ public int getMaxPolicyStorageLimit() {
+ if (mService != null) {
+ try {
+ return mService.getMaxPolicyStorageLimit(mContext.getPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return -1;
+ }
} \ No newline at end of file
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index 07ee8de587f8..1aee9fe57466 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -338,4 +338,9 @@ public abstract class DevicePolicyManagerInternal {
* Enforces resolved security logging policy, should only be invoked from device policy engine.
*/
public abstract void enforceSecurityLoggingPolicy(boolean enabled);
+
+ /**
+ * Enforces resolved audit logging policy, should only be invoked from device policy engine.
+ */
+ public abstract void enforceAuditLoggingPolicy(boolean enabled);
}
diff --git a/core/java/android/app/admin/IAuditLogEventsCallback.aidl b/core/java/android/app/admin/IAuditLogEventsCallback.aidl
new file mode 100644
index 000000000000..ab871178a9b9
--- /dev/null
+++ b/core/java/android/app/admin/IAuditLogEventsCallback.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+import android.app.admin.SecurityLog;
+
+/** @hide */
+oneway interface IAuditLogEventsCallback {
+ void onNewAuditLogEvents(in List<SecurityLog.SecurityEvent> events);
+} \ No newline at end of file
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index f72fdc069db5..3a7a891c7995 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -32,6 +32,7 @@ import android.app.admin.SystemUpdatePolicy;
import android.app.admin.PackagePolicy;
import android.app.admin.PasswordMetrics;
import android.app.admin.FactoryResetProtectionPolicy;
+import android.app.admin.IAuditLogEventsCallback;
import android.app.admin.ManagedProfileProvisioningParams;
import android.app.admin.FullyManagedDeviceProvisioningParams;
import android.app.admin.ManagedSubscriptionsPolicy;
@@ -392,7 +393,7 @@ interface IDevicePolicyManager {
boolean getDoNotAskCredentialsOnBoot();
void notifyPendingSystemUpdate(in SystemUpdateInfo info);
- SystemUpdateInfo getPendingSystemUpdate(in ComponentName admin);
+ SystemUpdateInfo getPendingSystemUpdate(in ComponentName admin, in String callerPackage);
void setPermissionPolicy(in ComponentName admin, in String callerPackage, int policy);
int getPermissionPolicy(in ComponentName admin);
@@ -441,6 +442,10 @@ interface IDevicePolicyManager {
long forceNetworkLogs();
long forceSecurityLogs();
+ void setAuditLogEnabled(String callerPackage, boolean enabled);
+ boolean isAuditLogEnabled(String callerPackage);
+ void setAuditLogEventsCallback(String callerPackage, in IAuditLogEventsCallback callback);
+
boolean isUninstallInQueue(String packageName);
void uninstallPackageWithActiveAdmins(String packageName);
@@ -576,6 +581,8 @@ interface IDevicePolicyManager {
void setWifiSsidPolicy(String callerPackageName, in WifiSsidPolicy policy);
WifiSsidPolicy getWifiSsidPolicy(String callerPackageName);
+ boolean isTheftDetectionTriggered(String callerPackageName);
+
List<UserHandle> listForegroundAffiliatedUsers();
void setDrawables(in List<DevicePolicyDrawableResource> drawables);
void resetDrawables(in List<String> drawableIds);
@@ -615,4 +622,7 @@ interface IDevicePolicyManager {
int getContentProtectionPolicy(in ComponentName who, String callerPackageName);
int[] getSubscriptionIds(String callerPackageName);
+
+ void setMaxPolicyStorageLimit(String packageName, int storageLimit);
+ int getMaxPolicyStorageLimit(String packageName);
}
diff --git a/core/java/android/app/admin/SecurityLog.aidl b/core/java/android/app/admin/SecurityLog.aidl
new file mode 100644
index 000000000000..e5ae2df8c21b
--- /dev/null
+++ b/core/java/android/app/admin/SecurityLog.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+/** @hide */
+parcelable SecurityLog.SecurityEvent; \ No newline at end of file
diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java
index ed1b8ca9b5bd..477f2e007b33 100644
--- a/core/java/android/app/admin/SecurityLog.java
+++ b/core/java/android/app/admin/SecurityLog.java
@@ -16,6 +16,8 @@
package android.app.admin;
+import static android.app.admin.flags.Flags.FLAG_BACKUP_SERVICE_SECURITY_LOG_EVENT_ENABLED;
+
import android.Manifest;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
@@ -24,7 +26,6 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.app.admin.flags.Flags;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.os.Build;
@@ -610,7 +611,7 @@ public class SecurityLog {
* <li> [2] backup service state ({@code Integer}, 1 for enabled, 0 for disabled)
* @see DevicePolicyManager#setBackupServiceEnabled(ComponentName, boolean)
*/
- @FlaggedApi(Flags.FLAG_BACKUP_SERVICE_SECURITY_LOG_EVENT_ENABLED)
+ @FlaggedApi(FLAG_BACKUP_SERVICE_SECURITY_LOG_EVENT_ENABLED)
public static final int TAG_BACKUP_SERVICE_TOGGLED =
SecurityLogTags.SECURITY_BACKUP_SERVICE_TOGGLED;
/**
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 30cd1b72fd49..10954aba955c 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -92,6 +92,13 @@ flag {
}
flag {
+ name: "allow_querying_profile_type"
+ namespace: "enterprise"
+ description: "Public APIs to query if a user is a profile and what kind of profile type it is."
+ bug: "323001115"
+}
+
+flag {
name: "quiet_mode_credential_bug_fix"
namespace: "enterprise"
description: "Guards a bugfix that ends the credential input flow if the managed user has not stopped."
@@ -110,6 +117,9 @@ flag {
namespace: "enterprise"
description: "Exempt the default sms app of the context user for suspension when calling setPersonalAppsSuspended"
bug: "309183330"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
flag {
@@ -132,3 +142,10 @@ flag {
description: "Add Headless DO support."
bug: "289515470"
}
+
+flag {
+ name: "is_mte_policy_enforced"
+ namespace: "enterprise"
+ description: "Allow to query whether MTE is enabled or not to check for compliance for enterprise policy"
+ bug: "322777918"
+}
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 7a4a3f9c8f27..fb1b17bd23d4 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -1,5 +1,7 @@
package android.app.assist;
+import static android.service.autofill.Flags.FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION;
+
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -8,6 +10,7 @@ import android.annotation.SystemApi;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
+import android.credentials.CredentialOption;
import android.credentials.GetCredentialException;
import android.credentials.GetCredentialRequest;
import android.credentials.GetCredentialResponse;
@@ -27,6 +30,7 @@ import android.os.PooledStringWriter;
import android.os.RemoteException;
import android.os.SystemClock;
import android.service.autofill.FillRequest;
+import android.service.credentials.CredentialProviderService;
import android.text.InputType;
import android.text.Spanned;
import android.text.TextUtils;
@@ -911,6 +915,7 @@ public class AssistStructure implements Parcelable {
if ((flags&FLAGS_HAS_EXTRAS) != 0) {
mExtras = in.readBundle();
}
+ mGetCredentialRequest = in.readTypedObject(GetCredentialRequest.CREATOR);
}
/**
@@ -1147,6 +1152,7 @@ public class AssistStructure implements Parcelable {
if ((flags&FLAGS_HAS_EXTRAS) != 0) {
out.writeBundle(mExtras);
}
+ out.writeTypedObject(mGetCredentialRequest, flags);
return flags;
}
@@ -1278,20 +1284,16 @@ public class AssistStructure implements Parcelable {
*
* @hide
*/
- @FlaggedApi("autofill_credman_dev_integration")
+ @FlaggedApi(FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION)
@Nullable
public GetCredentialRequest getCredentialManagerRequest() {
return mGetCredentialRequest;
}
/**
- *
- * @return
- *
* @hide
- *
*/
- @FlaggedApi("autofill_credman_dev_integration")
+ @FlaggedApi(FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION)
@Nullable
public OutcomeReceiver<GetCredentialResponse,
GetCredentialException> getCredentialManagerCallback() {
@@ -2258,6 +2260,17 @@ public class AssistStructure implements Parcelable {
@NonNull OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback) {
mNode.mGetCredentialRequest = request;
mNode.mGetCredentialCallback = callback;
+ for (CredentialOption option : request.getCredentialOptions()) {
+ ArrayList<AutofillId> ids = option.getCandidateQueryData()
+ .getParcelableArrayList(
+ CredentialProviderService.EXTRA_AUTOFILL_ID, AutofillId.class);
+ ids = ids != null ? ids : new ArrayList<>();
+ if (!ids.contains(getAutofillId())) {
+ ids.add(getAutofillId());
+ }
+ option.getCandidateQueryData()
+ .putParcelableArrayList(CredentialProviderService.EXTRA_AUTOFILL_ID, ids);
+ }
}
@Override
@@ -2567,7 +2580,7 @@ public class AssistStructure implements Parcelable {
}
AutofillId autofillId = node.getAutofillId();
if (autofillId == null) {
- Log.i(TAG, prefix + " NO autofill ID");
+ Log.i(TAG, prefix + " No autofill ID");
} else {
Log.i(TAG, prefix + " Autofill info: id= " + autofillId
+ ", type=" + node.getAutofillType()
@@ -2582,7 +2595,7 @@ public class AssistStructure implements Parcelable {
}
GetCredentialRequest getCredentialRequest = node.getCredentialManagerRequest();
if (getCredentialRequest == null) {
- Log.i(TAG, prefix + " NO Credential Manager Request");
+ Log.i(TAG, prefix + " No Credential Manager Request");
} else {
Log.i(TAG, prefix + " GetCredentialRequest: no. of options= "
+ getCredentialRequest.getCredentialOptions().size()
diff --git a/core/java/android/app/ondeviceintelligence/Content.aidl b/core/java/android/app/ondeviceintelligence/Content.aidl
new file mode 100644
index 000000000000..40f0ef9a8541
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/Content.aidl
@@ -0,0 +1,22 @@
+/**
+ * Copyright (c) 2024, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.ondeviceintelligence;
+
+/**
+ * @hide
+ */
+parcelable Content;
diff --git a/core/java/android/app/ondeviceintelligence/Content.java b/core/java/android/app/ondeviceintelligence/Content.java
new file mode 100644
index 000000000000..51bd156fc946
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/Content.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Represents content sent to and received from the on-device inference service.
+ * Can contain a collection of text, image, and binary parts or any combination of these.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public final class Content implements Parcelable {
+ //TODO: Improve javadoc after adding validation logic.
+ private static final String TAG = "Content";
+ private final Bundle mData;
+
+ /**
+ * Create a content object using a Bundle of only known types that are read-only.
+ */
+ public Content(@NonNull Bundle data) {
+ Objects.requireNonNull(data);
+ validateBundleData(data);
+ this.mData = data;
+ }
+
+ /**
+ * Returns the Content's data represented as a Bundle.
+ */
+ @NonNull
+ public Bundle getData() {
+ return mData;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeBundle(mData);
+ }
+
+ @Override
+ public int describeContents() {
+ int mask = 0;
+ mask |= mData.describeContents();
+ return mask;
+ }
+
+ @NonNull
+ public static final Creator<Content> CREATOR = new Creator<>() {
+ @Override
+ @NonNull
+ public Content createFromParcel(@NonNull Parcel in) {
+ return new Content(in.readBundle(getClass().getClassLoader()));
+ }
+
+ @Override
+ @NonNull
+ public Content[] newArray(int size) {
+ return new Content[size];
+ }
+ };
+
+ private void validateBundleData(Bundle unused) {
+ // TODO: Validate there are only known types.
+ }
+}
diff --git a/core/java/android/app/ondeviceintelligence/DownloadCallback.java b/core/java/android/app/ondeviceintelligence/DownloadCallback.java
new file mode 100644
index 000000000000..684c71f9144c
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/DownloadCallback.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.PersistableBundle;
+
+import androidx.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Callback functions used for feature downloading via the
+ * {@link OnDeviceIntelligenceManager#requestFeatureDownload}.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public interface DownloadCallback {
+ int DOWNLOAD_FAILURE_STATUS_UNKNOWN = 0;
+
+ /**
+ * Sent when feature download could not succeed due to there being no available disk space on
+ * the device.
+ */
+ int DOWNLOAD_FAILURE_STATUS_NOT_ENOUGH_DISK_SPACE = 1;
+
+ /**
+ * Sent when feature download could not succeed due to a network error.
+ */
+ int DOWNLOAD_FAILURE_STATUS_NETWORK_FAILURE = 2;
+
+ /**
+ * Sent when feature download has been initiated already, hence no need to request download
+ * again. Caller can query {@link OnDeviceIntelligenceManager#getFeatureStatus} to check if
+ * download has been completed.
+ */
+ int DOWNLOAD_FAILURE_STATUS_DOWNLOADING = 3;
+
+ /**
+ * Sent when feature download did not start due to errors (e.g. remote exception of features not
+ * available). Caller can query {@link OnDeviceIntelligenceManager#getFeatureStatus} to check
+ * if feature-status is {@link FeatureDetails#FEATURE_STATUS_DOWNLOADABLE}.
+ */
+ int DOWNLOAD_FAILURE_STATUS_UNAVAILABLE = 4;
+
+ /** @hide */
+ @IntDef(value = {
+ DOWNLOAD_FAILURE_STATUS_UNKNOWN,
+ DOWNLOAD_FAILURE_STATUS_NOT_ENOUGH_DISK_SPACE,
+ DOWNLOAD_FAILURE_STATUS_NETWORK_FAILURE,
+ DOWNLOAD_FAILURE_STATUS_DOWNLOADING,
+ DOWNLOAD_FAILURE_STATUS_UNAVAILABLE
+ }, open = true)
+ @Retention(RetentionPolicy.SOURCE)
+ @interface DownloadFailureStatus {
+ }
+
+ /**
+ * Called when model download started properly.
+ *
+ * @param bytesToDownload the total bytes to be downloaded for this {@link Feature}
+ */
+ default void onDownloadStarted(long bytesToDownload) {
+ }
+
+ /**
+ * Called when model download failed.
+ *
+ * @param failureStatus the download failure status
+ * @param errorMessage the error message associated with the download failure
+ */
+ void onDownloadFailed(
+ @DownloadFailureStatus int failureStatus,
+ @Nullable String errorMessage,
+ @NonNull PersistableBundle errorParams);
+
+ /**
+ * Called when model download is in progress.
+ *
+ * @param totalBytesDownloaded the already downloaded bytes for this {@link Feature}
+ */
+ default void onDownloadProgress(long totalBytesDownloaded) {
+ }
+
+ /**
+ * Called when model download via MDD completed. The remote implementation can populate any
+ * associated download params like file stats etc. in this callback to inform the client.
+ *
+ * @param downloadParams params containing info about the completed download.
+ */
+ void onDownloadCompleted(@NonNull PersistableBundle downloadParams);
+}
diff --git a/location/java/android/location/GeocoderParams.aidl b/core/java/android/app/ondeviceintelligence/Feature.aidl
index 2484e207dae7..18494d754674 100644
--- a/location/java/android/location/GeocoderParams.aidl
+++ b/core/java/android/app/ondeviceintelligence/Feature.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010, The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,6 +14,9 @@
* limitations under the License.
*/
-package android.location;
+package android.app.ondeviceintelligence;
-parcelable GeocoderParams;
+/**
+ * @hide
+ */
+parcelable Feature;
diff --git a/core/java/android/app/ondeviceintelligence/Feature.java b/core/java/android/app/ondeviceintelligence/Feature.java
new file mode 100644
index 000000000000..510735461553
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/Feature.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+
+/**
+ * Represents a typical feature associated with on-device intelligence.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public final class Feature implements Parcelable {
+ // TODO(b/325315604) - Check if we can expose non-hidden IntDefs in Framework.
+ private final int mId;
+ @Nullable
+ private final String mName;
+ @Nullable
+ private final String mModelName;
+ private final int mType;
+ private final int mVariant;
+ @NonNull
+ private final PersistableBundle mFeatureParams;
+
+ /* package-private */ Feature(
+ int id,
+ @Nullable String name,
+ @Nullable String modelName,
+ int type,
+ int variant,
+ @NonNull PersistableBundle featureParams) {
+ this.mId = id;
+ this.mName = name;
+ this.mModelName = modelName;
+ this.mType = type;
+ this.mVariant = variant;
+ this.mFeatureParams = featureParams;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mFeatureParams);
+ }
+
+ /** Returns the unique and immutable identifier of this feature. */
+ public int getId() {
+ return mId;
+ }
+
+ /** Returns human-readable name of this feature. */
+ public @Nullable String getName() {
+ return mName;
+ }
+
+ /** Returns base model name of this feature. */
+ public @Nullable String getModelName() {
+ return mModelName;
+ }
+
+ /** Returns type identifier of this feature. */
+ public int getType() {
+ return mType;
+ }
+
+ /** Returns variant kind for this feature. */
+ public int getVariant() {
+ return mVariant;
+ }
+
+ public @NonNull PersistableBundle getFeatureParams() {
+ return mFeatureParams;
+ }
+
+ @Override
+ public String toString() {
+ return "Feature { " +
+ "id = " + mId + ", " +
+ "name = " + mName + ", " +
+ "modelName = " + mModelName + ", " +
+ "type = " + mType + ", " +
+ "variant = " + mVariant + ", " +
+ "featureParams = " + mFeatureParams +
+ " }";
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ Feature that = (Feature) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && mId == that.mId
+ && java.util.Objects.equals(mName, that.mName)
+ && java.util.Objects.equals(mModelName, that.mModelName)
+ && mType == that.mType
+ && mVariant == that.mVariant
+ && java.util.Objects.equals(mFeatureParams, that.mFeatureParams);
+ }
+
+ @Override
+ public int hashCode() {
+ int _hash = 1;
+ _hash = 31 * _hash + mId;
+ _hash = 31 * _hash + java.util.Objects.hashCode(mName);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mModelName);
+ _hash = 31 * _hash + mType;
+ _hash = 31 * _hash + mVariant;
+ _hash = 31 * _hash + java.util.Objects.hashCode(mFeatureParams);
+ return _hash;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ byte flg = 0;
+ if (mName != null) flg |= 0x2;
+ if (mModelName != null) flg |= 0x4;
+ dest.writeByte(flg);
+ dest.writeInt(mId);
+ if (mName != null) dest.writeString8(mName);
+ if (mModelName != null) dest.writeString8(mModelName);
+ dest.writeInt(mType);
+ dest.writeInt(mVariant);
+ dest.writeTypedObject(mFeatureParams, flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ /* package-private */ Feature(@NonNull Parcel in) {
+ byte flg = in.readByte();
+ int id = in.readInt();
+ String name = (flg & 0x2) == 0 ? null : in.readString();
+ String modelName = (flg & 0x4) == 0 ? null : in.readString();
+ int type = in.readInt();
+ int variant = in.readInt();
+ PersistableBundle featureParams = (PersistableBundle) in.readTypedObject(
+ PersistableBundle.CREATOR);
+
+ this.mId = id;
+ this.mName = name;
+ this.mModelName = modelName;
+ this.mType = type;
+ this.mVariant = variant;
+ this.mFeatureParams = featureParams;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mFeatureParams);
+ }
+
+ public static final @NonNull Parcelable.Creator<Feature> CREATOR
+ = new Parcelable.Creator<Feature>() {
+ @Override
+ public Feature[] newArray(int size) {
+ return new Feature[size];
+ }
+
+ @Override
+ public Feature createFromParcel(@NonNull Parcel in) {
+ return new Feature(in);
+ }
+ };
+
+ /**
+ * A builder for {@link Feature}
+ */
+ @SuppressWarnings("WeakerAccess")
+ public static final class Builder {
+ private int mId;
+ private @Nullable String mName;
+ private @Nullable String mModelName;
+ private int mType;
+ private int mVariant;
+ private @NonNull PersistableBundle mFeatureParams;
+
+ private long mBuilderFieldsSet = 0L;
+
+ public Builder(
+ int id,
+ int type,
+ int variant,
+ @NonNull PersistableBundle featureParams) {
+ mId = id;
+ mType = type;
+ mVariant = variant;
+ mFeatureParams = featureParams;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mFeatureParams);
+ }
+
+ public @NonNull Builder setId(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1;
+ mId = value;
+ return this;
+ }
+
+ public @NonNull Builder setName(@NonNull String value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2;
+ mName = value;
+ return this;
+ }
+
+ public @NonNull Builder setModelName(@NonNull String value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4;
+ mModelName = value;
+ return this;
+ }
+
+ public @NonNull Builder setType(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x8;
+ mType = value;
+ return this;
+ }
+
+ public @NonNull Builder setVariant(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x10;
+ mVariant = value;
+ return this;
+ }
+
+ public @NonNull Builder setFeatureParams(@NonNull PersistableBundle value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x20;
+ mFeatureParams = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull Feature build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x40; // Mark builder used
+
+ Feature o = new Feature(
+ mId,
+ mName,
+ mModelName,
+ mType,
+ mVariant,
+ mFeatureParams);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x40) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+}
diff --git a/core/java/android/app/ondeviceintelligence/FeatureDetails.aidl b/core/java/android/app/ondeviceintelligence/FeatureDetails.aidl
new file mode 100644
index 000000000000..0589bf8bacb9
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/FeatureDetails.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.ondeviceintelligence;
+
+/**
+ * @hide
+ */
+parcelable FeatureDetails;
diff --git a/core/java/android/app/ondeviceintelligence/FeatureDetails.java b/core/java/android/app/ondeviceintelligence/FeatureDetails.java
new file mode 100644
index 000000000000..92f351362f70
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/FeatureDetails.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+
+import androidx.annotation.IntDef;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.text.MessageFormat;
+
+/**
+ * Represents a status of a requested {@link Feature}.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public final class FeatureDetails implements Parcelable {
+ @Status
+ private final int mStatus;
+ @NonNull
+ private final PersistableBundle mFeatureDetailParams;
+
+ /** Invalid or unavailable {@code AiFeature}. */
+ public static final int FEATURE_STATUS_UNAVAILABLE = 0;
+
+ /** Feature can be downloaded on request. */
+ public static final int FEATURE_STATUS_DOWNLOADABLE = 1;
+
+ /** Feature is being downloaded. */
+ public static final int FEATURE_STATUS_DOWNLOADING = 2;
+
+ /** Feature is fully downloaded and ready to use. */
+ public static final int FEATURE_STATUS_AVAILABLE = 3;
+
+ /** Underlying service is unavailable and feature status cannot be fetched. */
+ public static final int FEATURE_STATUS_SERVICE_UNAVAILABLE = 4;
+
+ @IntDef(value = {
+ FEATURE_STATUS_UNAVAILABLE,
+ FEATURE_STATUS_DOWNLOADABLE,
+ FEATURE_STATUS_DOWNLOADING,
+ FEATURE_STATUS_AVAILABLE,
+ FEATURE_STATUS_SERVICE_UNAVAILABLE
+ }, open = true)
+ @Target({ElementType.TYPE_USE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Status {
+ }
+
+ public FeatureDetails(
+ @Status int status,
+ @NonNull PersistableBundle featureDetailParams) {
+ this.mStatus = status;
+ com.android.internal.util.AnnotationValidations.validate(
+ Status.class, null, mStatus);
+ this.mFeatureDetailParams = featureDetailParams;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mFeatureDetailParams);
+ }
+
+ public FeatureDetails(
+ @Status int status) {
+ this.mStatus = status;
+ com.android.internal.util.AnnotationValidations.validate(
+ Status.class, null, mStatus);
+ this.mFeatureDetailParams = new PersistableBundle();
+ }
+
+
+ /**
+ * Returns an integer value associated with the feature status.
+ */
+ public @Status int getStatus() {
+ return mStatus;
+ }
+
+
+ /**
+ * Returns a persistable bundle contain any additional status related params.
+ */
+ public @NonNull PersistableBundle getFeatureDetailParams() {
+ return mFeatureDetailParams;
+ }
+
+ @Override
+ public String toString() {
+ return MessageFormat.format("FeatureDetails '{' status = {0}, "
+ + "persistableBundle = {1} '}'",
+ mStatus,
+ mFeatureDetailParams);
+ }
+
+ @Override
+ public boolean equals(@android.annotation.Nullable Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ FeatureDetails that = (FeatureDetails) o;
+ return mStatus == that.mStatus
+ && java.util.Objects.equals(mFeatureDetailParams, that.mFeatureDetailParams);
+ }
+
+ @Override
+ public int hashCode() {
+ int _hash = 1;
+ _hash = 31 * _hash + mStatus;
+ _hash = 31 * _hash + java.util.Objects.hashCode(mFeatureDetailParams);
+ return _hash;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ dest.writeInt(mStatus);
+ dest.writeTypedObject(mFeatureDetailParams, flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ FeatureDetails(@NonNull android.os.Parcel in) {
+ int status = in.readInt();
+ PersistableBundle persistableBundle = (PersistableBundle) in.readTypedObject(
+ PersistableBundle.CREATOR);
+
+ this.mStatus = status;
+ com.android.internal.util.AnnotationValidations.validate(
+ Status.class, null, mStatus);
+ this.mFeatureDetailParams = persistableBundle;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mFeatureDetailParams);
+ }
+
+
+ public static final @NonNull Parcelable.Creator<FeatureDetails> CREATOR =
+ new Parcelable.Creator<>() {
+ @Override
+ public FeatureDetails[] newArray(int size) {
+ return new FeatureDetails[size];
+ }
+
+ @Override
+ public FeatureDetails createFromParcel(@NonNull android.os.Parcel in) {
+ return new FeatureDetails(in);
+ }
+ };
+
+}
diff --git a/core/java/android/app/ondeviceintelligence/FilePart.java b/core/java/android/app/ondeviceintelligence/FilePart.java
new file mode 100644
index 000000000000..e9fb5f2cb440
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/FilePart.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.FlaggedApi;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+
+import android.annotation.NonNull;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * Represents file data with an associated file descriptor sent to and received from remote
+ * processing. The interface ensures that the underlying file-descriptor is always opened in
+ * read-only mode.
+ *
+ * @hide
+ */
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+@SystemApi
+public final class FilePart implements Parcelable {
+ private final String mPartKey;
+ private final PersistableBundle mPartParams;
+ private final ParcelFileDescriptor mParcelFileDescriptor;
+
+ private FilePart(@NonNull String partKey, @NonNull PersistableBundle partParams,
+ @NonNull ParcelFileDescriptor parcelFileDescriptor) {
+ Objects.requireNonNull(partKey);
+ Objects.requireNonNull(partParams);
+ this.mPartKey = partKey;
+ this.mPartParams = partParams;
+ this.mParcelFileDescriptor = Objects.requireNonNull(parcelFileDescriptor);
+ }
+
+ /**
+ * Create a file part using a filePath and any additional params.
+ */
+ public FilePart(@NonNull String partKey, @NonNull PersistableBundle partParams,
+ @NonNull String filePath)
+ throws FileNotFoundException {
+ this(partKey, partParams, Objects.requireNonNull(ParcelFileDescriptor.open(
+ new File(Objects.requireNonNull(filePath)), ParcelFileDescriptor.MODE_READ_ONLY)));
+ }
+
+ /**
+ * Create a file part using a file input stream and any additional params.
+ * It is the caller's responsibility to close the stream. It is safe to do so as soon as this
+ * call returns.
+ */
+ public FilePart(@NonNull String partKey, @NonNull PersistableBundle partParams,
+ @NonNull FileInputStream fileInputStream)
+ throws IOException {
+ this(partKey, partParams, ParcelFileDescriptor.dup(fileInputStream.getFD()));
+ }
+
+ /**
+ * Returns a FileInputStream for the associated File.
+ * Caller must close the associated stream when done reading from it.
+ *
+ * @return the FileInputStream associated with the FilePart.
+ */
+ @NonNull
+ public FileInputStream getFileInputStream() {
+ return new FileInputStream(mParcelFileDescriptor.getFileDescriptor());
+ }
+
+ /**
+ * Returns the unique key associated with the part. Each Part key added to a content object
+ * should be ensured to be unique.
+ */
+ @NonNull
+ public String getFilePartKey() {
+ return mPartKey;
+ }
+
+ /**
+ * Returns the params associated with Part.
+ */
+ @NonNull
+ public PersistableBundle getFilePartParams() {
+ return mPartParams;
+ }
+
+
+ @Override
+ public int describeContents() {
+ return CONTENTS_FILE_DESCRIPTOR;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString8(getFilePartKey());
+ dest.writePersistableBundle(getFilePartParams());
+ mParcelFileDescriptor.writeToParcel(dest, flags
+ | Parcelable.PARCELABLE_WRITE_RETURN_VALUE); // This flag ensures that the sender's
+ // copy of the Pfd is closed as soon as the Binder call succeeds.
+ }
+
+ @NonNull
+ public static final Creator<FilePart> CREATOR = new Creator<>() {
+ @Override
+ public FilePart createFromParcel(Parcel in) {
+ return new FilePart(in.readString(), in.readTypedObject(PersistableBundle.CREATOR),
+ in.readParcelable(
+ getClass().getClassLoader(), ParcelFileDescriptor.class));
+ }
+
+ @Override
+ public FilePart[] newArray(int size) {
+ return new FilePart[size];
+ }
+ };
+}
diff --git a/core/java/android/app/ondeviceintelligence/IDownloadCallback.aidl b/core/java/android/app/ondeviceintelligence/IDownloadCallback.aidl
new file mode 100644
index 000000000000..aba563f84e1b
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/IDownloadCallback.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.ondeviceintelligence;
+
+import android.app.ondeviceintelligence.IProcessingSignal;
+import android.os.PersistableBundle;
+
+/**
+ * Interface for Download callback to passed onto service implementation,
+ *
+ * @hide
+ */
+oneway interface IDownloadCallback {
+ void onDownloadStarted(long bytesToDownload) = 1;
+ void onDownloadProgress(long bytesDownloaded) = 2;
+ void onDownloadFailed(int failureStatus, String errorMessage, in PersistableBundle errorParams) = 3;
+ void onDownloadCompleted(in PersistableBundle downloadParams) = 4;
+} \ No newline at end of file
diff --git a/core/java/android/app/ondeviceintelligence/IFeatureCallback.aidl b/core/java/android/app/ondeviceintelligence/IFeatureCallback.aidl
new file mode 100644
index 000000000000..93a84ec96757
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/IFeatureCallback.aidl
@@ -0,0 +1,14 @@
+package android.app.ondeviceintelligence;
+
+import android.app.ondeviceintelligence.Feature;
+import android.os.PersistableBundle;
+
+/**
+ * Interface for receiving a feature for the given identifier.
+ *
+ * @hide
+ */
+interface IFeatureCallback {
+ void onSuccess(in Feature result) = 1;
+ void onFailure(int errorCode, in String errorMessage, in PersistableBundle errorParams) = 2;
+}
diff --git a/core/java/android/app/ondeviceintelligence/IFeatureDetailsCallback.aidl b/core/java/android/app/ondeviceintelligence/IFeatureDetailsCallback.aidl
new file mode 100644
index 000000000000..d95029059f4a
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/IFeatureDetailsCallback.aidl
@@ -0,0 +1,14 @@
+package android.app.ondeviceintelligence;
+
+import android.app.ondeviceintelligence.FeatureDetails;
+import android.os.PersistableBundle;
+
+/**
+ * Interface for receiving details about a given feature. .
+ *
+ * @hide
+ */
+interface IFeatureDetailsCallback {
+ void onSuccess(in FeatureDetails result) = 1;
+ void onFailure(int errorCode, in String errorMessage, in PersistableBundle errorParams) = 2;
+}
diff --git a/core/java/android/app/ondeviceintelligence/IListFeaturesCallback.aidl b/core/java/android/app/ondeviceintelligence/IListFeaturesCallback.aidl
new file mode 100644
index 000000000000..374cb71977d5
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/IListFeaturesCallback.aidl
@@ -0,0 +1,15 @@
+package android.app.ondeviceintelligence;
+
+import java.util.List;
+import android.app.ondeviceintelligence.Feature;
+import android.os.PersistableBundle;
+
+/**
+ * Interface for receiving list of supported features.
+ *
+ * @hide
+ */
+interface IListFeaturesCallback {
+ void onSuccess(in List<Feature> result) = 1;
+ void onFailure(int errorCode, in String errorMessage, in PersistableBundle errorParams) = 2;
+}
diff --git a/core/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl b/core/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl
new file mode 100644
index 000000000000..b925f4863de2
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ package android.app.ondeviceintelligence;
+
+ import com.android.internal.infra.AndroidFuture;
+ import android.os.ICancellationSignal;
+ import android.os.ParcelFileDescriptor;
+ import android.os.PersistableBundle;
+ import android.os.RemoteCallback;
+ import android.app.ondeviceintelligence.Content;
+ import android.app.ondeviceintelligence.Feature;
+ import android.app.ondeviceintelligence.FeatureDetails;
+ import android.app.ondeviceintelligence.IDownloadCallback;
+ import android.app.ondeviceintelligence.IListFeaturesCallback;
+ import android.app.ondeviceintelligence.IFeatureCallback;
+ import android.app.ondeviceintelligence.IFeatureDetailsCallback;
+ import android.app.ondeviceintelligence.IResponseCallback;
+ import android.app.ondeviceintelligence.IStreamingResponseCallback;
+ import android.app.ondeviceintelligence.IProcessingSignal;
+ import android.app.ondeviceintelligence.ITokenCountCallback;
+
+
+ /**
+ * Interface for a OnDeviceIntelligenceManager for managing OnDeviceIntelligenceService and OnDeviceSandboxedInferenceService.
+ *
+ * @hide
+ */
+ oneway interface IOnDeviceIntelligenceManager {
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)")
+ void getVersion(in RemoteCallback remoteCallback) = 1;
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)")
+ void getFeature(in int featureId, in IFeatureCallback remoteCallback) = 2;
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)")
+ void listFeatures(in IListFeaturesCallback listFeaturesCallback) = 3;
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)")
+ void getFeatureDetails(in Feature feature, in IFeatureDetailsCallback featureDetailsCallback) = 4;
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)")
+ void requestFeatureDownload(in Feature feature, ICancellationSignal signal, in IDownloadCallback callback) = 5;
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)")
+ void requestTokenCount(in Feature feature, in Content request, in ICancellationSignal signal,
+ in ITokenCountCallback tokenCountcallback) = 6;
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)")
+ void processRequest(in Feature feature, in Content request, int requestType, in ICancellationSignal cancellationSignal, in IProcessingSignal signal,
+ in IResponseCallback responseCallback) = 7;
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)")
+ void processRequestStreaming(in Feature feature,
+ in Content request, int requestType, in ICancellationSignal cancellationSignal, in IProcessingSignal signal,
+ in IStreamingResponseCallback streamingCallback) = 8;
+ }
diff --git a/core/java/android/app/ondeviceintelligence/IProcessingSignal.aidl b/core/java/android/app/ondeviceintelligence/IProcessingSignal.aidl
new file mode 100644
index 000000000000..03946eebd40b
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/IProcessingSignal.aidl
@@ -0,0 +1,14 @@
+package android.app.ondeviceintelligence;
+
+import android.os.PersistableBundle;
+
+/**
+* Signal to provide to the remote implementation in context of a given request or
+* feature specific event.
+*
+* @hide
+*/
+
+oneway interface IProcessingSignal {
+ void sendSignal(in PersistableBundle actionParams) = 2;
+}
diff --git a/core/java/android/app/ondeviceintelligence/IResponseCallback.aidl b/core/java/android/app/ondeviceintelligence/IResponseCallback.aidl
new file mode 100644
index 000000000000..9848e1ddda5a
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/IResponseCallback.aidl
@@ -0,0 +1,15 @@
+package android.app.ondeviceintelligence;
+
+import android.app.ondeviceintelligence.Content;
+import android.app.ondeviceintelligence.IProcessingSignal;
+import android.os.PersistableBundle;
+
+/**
+ * Interface for a IResponseCallback for receiving response from on-device intelligence service.
+ *
+ * @hide
+ */
+interface IResponseCallback {
+ void onSuccess(in Content result) = 1;
+ void onFailure(int errorCode, in String errorMessage, in PersistableBundle errorParams) = 2;
+}
diff --git a/core/java/android/app/ondeviceintelligence/IStreamingResponseCallback.aidl b/core/java/android/app/ondeviceintelligence/IStreamingResponseCallback.aidl
new file mode 100644
index 000000000000..a6805749fa04
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/IStreamingResponseCallback.aidl
@@ -0,0 +1,18 @@
+package android.app.ondeviceintelligence;
+
+import android.app.ondeviceintelligence.Content;
+import android.app.ondeviceintelligence.IResponseCallback;
+import android.app.ondeviceintelligence.IProcessingSignal;
+import android.os.PersistableBundle;
+
+
+/**
+ * This callback is a streaming variant of {@link IResponseCallback}.
+ *
+ * @hide
+ */
+interface IStreamingResponseCallback {
+ void onNewContent(in Content result) = 1;
+ void onSuccess(in Content result) = 2;
+ void onFailure(int errorCode, in String errorMessage, in PersistableBundle errorParams) = 3;
+}
diff --git a/core/java/android/app/ondeviceintelligence/ITokenCountCallback.aidl b/core/java/android/app/ondeviceintelligence/ITokenCountCallback.aidl
new file mode 100644
index 000000000000..b724e03fbca4
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/ITokenCountCallback.aidl
@@ -0,0 +1,13 @@
+package android.app.ondeviceintelligence;
+
+import android.os.PersistableBundle;
+
+/**
+ * Interface for receiving the token count of a request for a given features.
+ *
+ * @hide
+ */
+interface ITokenCountCallback {
+ void onSuccess(long tokenCount) = 1;
+ void onFailure(int errorCode, in String errorMessage, in PersistableBundle errorParams) = 2;
+}
diff --git a/core/java/android/app/ondeviceintelligence/OWNERS b/core/java/android/app/ondeviceintelligence/OWNERS
new file mode 100644
index 000000000000..85e9e653e6fb
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/OWNERS
@@ -0,0 +1,6 @@
+# Bug component: 1363385
+
+sandeepbandaru@google.com
+shivanker@google.com
+hackz@google.com
+volnov@google.com
diff --git a/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java b/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
new file mode 100644
index 000000000000..4d8e0d55e355
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
@@ -0,0 +1,624 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.Binder;
+import android.os.CancellationSignal;
+import android.os.ICancellationSignal;
+import android.os.OutcomeReceiver;
+import android.os.PersistableBundle;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+
+import androidx.annotation.IntDef;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.function.LongConsumer;
+
+/**
+ * Allows granted apps to manage on-device intelligence service configured on the device. Typical
+ * calling pattern will be to query and setup a required feature before proceeding to request
+ * processing.
+ *
+ * The contracts in this Manager class are designed to be open-ended in general, to allow
+ * interoperability. Therefore, it is recommended that implementations of this system-service
+ * expose this API to the clients via a separate sdk or library which has more defined contract.
+ *
+ * @hide
+ */
+@SystemApi
+@SystemService(Context.ON_DEVICE_INTELLIGENCE_SERVICE)
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public class OnDeviceIntelligenceManager {
+ public static final String API_VERSION_BUNDLE_KEY = "ApiVersionBundleKey";
+ private final Context mContext;
+ private final IOnDeviceIntelligenceManager mService;
+
+ /**
+ * @hide
+ */
+ public OnDeviceIntelligenceManager(Context context, IOnDeviceIntelligenceManager service) {
+ mContext = context;
+ mService = service;
+ }
+
+ /**
+ * Asynchronously get the version of the underlying remote implementation.
+ *
+ * @param versionConsumer consumer to populate the version of remote implementation.
+ * @param callbackExecutor executor to run the callback on.
+ */
+ @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+ public void getVersion(
+ @NonNull @CallbackExecutor Executor callbackExecutor,
+ @NonNull LongConsumer versionConsumer) {
+ // TODO explore modifying this method into getServicePackageDetails and return both
+ // version and package name of the remote service implementing this.
+ try {
+ RemoteCallback callback = new RemoteCallback(result -> {
+ if (result == null) {
+ Binder.withCleanCallingIdentity(
+ () -> callbackExecutor.execute(() -> versionConsumer.accept(0)));
+ }
+ long version = result.getLong(API_VERSION_BUNDLE_KEY);
+ Binder.withCleanCallingIdentity(
+ () -> callbackExecutor.execute(() -> versionConsumer.accept(version)));
+ });
+ mService.getVersion(callback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Asynchronously get feature for a given id.
+ *
+ * @param featureId the identifier pointing to the feature.
+ * @param featureReceiver callback to populate the feature object for given identifier.
+ * @param callbackExecutor executor to run the callback on.
+ */
+ @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+ public void getFeature(
+ int featureId,
+ @NonNull @CallbackExecutor Executor callbackExecutor,
+ @NonNull OutcomeReceiver<Feature, OnDeviceIntelligenceManagerException> featureReceiver) {
+ try {
+ IFeatureCallback callback =
+ new IFeatureCallback.Stub() {
+ @Override
+ public void onSuccess(Feature result) {
+ Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> featureReceiver.onResult(result)));
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage,
+ PersistableBundle errorParams) {
+ Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> featureReceiver.onError(
+ new OnDeviceIntelligenceManagerException(
+ errorCode, errorMessage, errorParams))));
+ }
+ };
+ mService.getFeature(featureId, callback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Asynchronously get a list of features that are supported for the caller.
+ *
+ * @param featureListReceiver callback to populate the list of features.
+ * @param callbackExecutor executor to run the callback on.
+ */
+ @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+ public void listFeatures(
+ @NonNull @CallbackExecutor Executor callbackExecutor,
+ @NonNull OutcomeReceiver<List<Feature>, OnDeviceIntelligenceManagerException> featureListReceiver) {
+ try {
+ IListFeaturesCallback callback =
+ new IListFeaturesCallback.Stub() {
+ @Override
+ public void onSuccess(List<Feature> result) {
+ Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> featureListReceiver.onResult(result)));
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage,
+ PersistableBundle errorParams) {
+ Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> featureListReceiver.onError(
+ new OnDeviceIntelligenceManagerException(
+ errorCode, errorMessage, errorParams))));
+ }
+ };
+ mService.listFeatures(callback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * This method should be used to fetch details about a feature which need some additional
+ * computation, that can be inefficient to return in all calls to {@link #getFeature}. Callers
+ * and implementation can utilize the {@link Feature#getFeatureParams()} to pass hint on what
+ * details are expected by the caller.
+ *
+ * @param feature the feature to check status for.
+ * @param featureDetailsReceiver callback to populate the feature details to.
+ * @param callbackExecutor executor to run the callback on.
+ */
+ @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+ public void getFeatureDetails(@NonNull Feature feature,
+ @NonNull @CallbackExecutor Executor callbackExecutor,
+ @NonNull OutcomeReceiver<FeatureDetails, OnDeviceIntelligenceManagerException> featureDetailsReceiver) {
+ try {
+ IFeatureDetailsCallback callback = new IFeatureDetailsCallback.Stub() {
+
+ @Override
+ public void onSuccess(FeatureDetails result) {
+ Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> featureDetailsReceiver.onResult(result)));
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage,
+ PersistableBundle errorParams) {
+ Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> featureDetailsReceiver.onError(
+ new OnDeviceIntelligenceManagerException(errorCode,
+ errorMessage, errorParams))));
+ }
+ };
+ mService.getFeatureDetails(feature, callback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * This method handles downloading all model and config files required to process requests
+ * sent against a given feature. The caller can listen to updates on the download status via
+ * the callback.
+ *
+ * Note: If a feature was already requested for downloaded previously, the onDownloadFailed
+ * callback would be invoked with {@link DownloadCallback#DOWNLOAD_FAILURE_STATUS_DOWNLOADING}.
+ * In such cases, clients should query the feature status via {@link #getFeatureStatus} to
+ * check
+ * on the feature's download status.
+ *
+ * @param feature feature to request download for.
+ * @param callback callback to populate updates about download status.
+ * @param cancellationSignal signal to invoke cancellation on the operation in the remote
+ * implementation.
+ * @param callbackExecutor executor to run the callback on.
+ */
+ @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+ public void requestFeatureDownload(@NonNull Feature feature,
+ @Nullable CancellationSignal cancellationSignal,
+ @NonNull @CallbackExecutor Executor callbackExecutor,
+ @NonNull DownloadCallback callback) {
+ try {
+ IDownloadCallback downloadCallback = new IDownloadCallback.Stub() {
+
+ @Override
+ public void onDownloadStarted(long bytesToDownload) {
+ Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> callback.onDownloadStarted(bytesToDownload)));
+ }
+
+ @Override
+ public void onDownloadProgress(long bytesDownloaded) {
+ Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> callback.onDownloadProgress(bytesDownloaded)));
+ }
+
+ @Override
+ public void onDownloadFailed(int failureStatus, String errorMessage,
+ PersistableBundle errorParams) {
+ Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> callback.onDownloadFailed(failureStatus, errorMessage,
+ errorParams)));
+ }
+
+ @Override
+ public void onDownloadCompleted(PersistableBundle downloadParams) {
+ Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> onDownloadCompleted(downloadParams)));
+ }
+ };
+
+ ICancellationSignal transport = null;
+ if (cancellationSignal != null) {
+ transport = CancellationSignal.createTransport();
+ cancellationSignal.setRemote(transport);
+ }
+
+ mService.requestFeatureDownload(feature, transport, downloadCallback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * The methods computes the token-count for a given request payload using the provided Feature
+ * details.
+ *
+ * @param feature feature associated with the request.
+ * @param request request that contains the content data and associated params.
+ * @param outcomeReceiver callback to populate the token count or exception in case of
+ * failure.
+ * @param cancellationSignal signal to invoke cancellation on the operation in the remote
+ * implementation.
+ * @param callbackExecutor executor to run the callback on.
+ */
+ @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+ public void requestTokenCount(@NonNull Feature feature, @NonNull Content request,
+ @Nullable CancellationSignal cancellationSignal,
+ @NonNull @CallbackExecutor Executor callbackExecutor,
+ @NonNull OutcomeReceiver<Long,
+ OnDeviceIntelligenceManagerException> outcomeReceiver) {
+ try {
+ ITokenCountCallback callback = new ITokenCountCallback.Stub() {
+ @Override
+ public void onSuccess(long tokenCount) {
+ Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> outcomeReceiver.onResult(tokenCount)));
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage,
+ PersistableBundle errorParams) {
+ Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> outcomeReceiver.onError(
+ new OnDeviceIntelligenceManagerProcessingException(
+ errorCode, errorMessage, errorParams))));
+ }
+ };
+
+ ICancellationSignal transport = null;
+ if (cancellationSignal != null) {
+ transport = CancellationSignal.createTransport();
+ cancellationSignal.setRemote(transport);
+ }
+
+ mService.requestTokenCount(feature, request, transport, callback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+
+ /**
+ * Asynchronously Process a request based on the associated params, to populate a
+ * response in
+ * {@link OutcomeReceiver#onResult} callback or failure callback status code if there
+ * was a
+ * failure.
+ *
+ * @param feature feature associated with the request.
+ * @param request request that contains the Content data and
+ * associated params.
+ * @param requestType type of request being sent for processing the content.
+ * @param responseOutcomeReceiver callback to populate the response content and
+ * associated
+ * params.
+ * @param processingSignal signal to invoke custom actions in the
+ * remote implementation.
+ * @param cancellationSignal signal to invoke cancellation or
+ * @param callbackExecutor executor to run the callback on.
+ */
+ @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+
+ public void processRequest(@NonNull Feature feature, @NonNull Content request,
+ @RequestType int requestType,
+ @Nullable CancellationSignal cancellationSignal,
+ @Nullable ProcessingSignal processingSignal,
+ @NonNull @CallbackExecutor Executor callbackExecutor,
+ @NonNull OutcomeReceiver<Content,
+ OnDeviceIntelligenceManagerProcessingException> responseOutcomeReceiver) {
+ try {
+ IResponseCallback callback = new IResponseCallback.Stub() {
+ @Override
+ public void onSuccess(Content result) {
+ Binder.withCleanCallingIdentity(() -> {
+ callbackExecutor.execute(() -> responseOutcomeReceiver.onResult(result));
+ });
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage,
+ PersistableBundle errorParams) {
+ Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> responseOutcomeReceiver.onError(
+ new OnDeviceIntelligenceManagerProcessingException(
+ errorCode, errorMessage, errorParams))));
+ }
+ };
+
+ IProcessingSignal transport = null;
+ if (processingSignal != null) {
+ transport = ProcessingSignal.createTransport();
+ processingSignal.setRemote(transport);
+ }
+
+ ICancellationSignal cancellationTransport = null;
+ if (cancellationSignal != null) {
+ cancellationTransport = CancellationSignal.createTransport();
+ cancellationSignal.setRemote(cancellationTransport);
+ }
+
+ mService.processRequest(feature, request, requestType, cancellationTransport, transport,
+ callback);
+
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Variation of {@link #processRequest} that asynchronously processes a request in a streaming
+ * fashion, where new content is pushed to caller in chunks via the
+ * {@link StreamingResponseReceiver#onNewContent}. After the streaming is complete,
+ * the service should call {@link StreamingResponseReceiver#onResult} and can optionally
+ * populate the complete {@link Response}'s Content as part of the callback when the final
+ * {@link Response} contains an enhanced aggregation of the Contents already streamed.
+ *
+ * @param feature feature associated with the request.
+ * @param request request that contains the Content data and associated
+ * params.
+ * @param requestType type of request being sent for processing the content.
+ * @param processingSignal signal to invoke other custom actions in the
+ * remote implementation.
+ * @param cancellationSignal signal to invoke cancellation
+ * @param streamingResponseReceiver streaming callback to populate the response content and
+ * associated params.
+ * @param callbackExecutor executor to run the callback on.
+ */
+ @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+ public void processRequestStreaming(@NonNull Feature feature, @NonNull Content request,
+ @RequestType int requestType,
+ @Nullable CancellationSignal cancellationSignal,
+ @Nullable ProcessingSignal processingSignal,
+ @NonNull @CallbackExecutor Executor callbackExecutor,
+ @NonNull StreamingResponseReceiver<Content, Content,
+ OnDeviceIntelligenceManagerProcessingException> streamingResponseReceiver) {
+ try {
+ IStreamingResponseCallback callback = new IStreamingResponseCallback.Stub() {
+ @Override
+ public void onNewContent(Content result) {
+ Binder.withCleanCallingIdentity(() -> {
+ callbackExecutor.execute(
+ () -> streamingResponseReceiver.onNewContent(result));
+ });
+ }
+
+ @Override
+ public void onSuccess(Content result) {
+ Binder.withCleanCallingIdentity(() -> {
+ callbackExecutor.execute(() -> streamingResponseReceiver.onResult(result));
+ });
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage,
+ PersistableBundle errorParams) {
+ Binder.withCleanCallingIdentity(() -> {
+ callbackExecutor.execute(
+ () -> streamingResponseReceiver.onError(
+ new OnDeviceIntelligenceManagerProcessingException(
+ errorCode, errorMessage, errorParams)));
+ });
+ }
+ };
+
+ IProcessingSignal transport = null;
+ if (processingSignal != null) {
+ transport = ProcessingSignal.createTransport();
+ processingSignal.setRemote(transport);
+ }
+
+ ICancellationSignal cancellationTransport = null;
+ if (cancellationSignal != null) {
+ cancellationTransport = CancellationSignal.createTransport();
+ cancellationSignal.setRemote(cancellationTransport);
+ }
+
+ mService.processRequestStreaming(
+ feature, request, requestType, cancellationTransport, transport, callback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+
+ /** Request inference with provided Content and Params. */
+ public static final int REQUEST_TYPE_INFERENCE = 0;
+
+ /**
+ * Prepares the remote implementation environment for e.g.loading inference runtime etc.which
+ * are time consuming beforehand to remove overhead and allow quick processing of requests
+ * thereof.
+ */
+ public static final int REQUEST_TYPE_PREPARE = 1;
+
+ /** Request Embeddings of the passed-in Content. */
+ public static final int REQUEST_TYPE_EMBEDDINGS = 2;
+
+ /**
+ * @hide
+ */
+ @IntDef(value = {
+ REQUEST_TYPE_INFERENCE,
+ REQUEST_TYPE_PREPARE,
+ REQUEST_TYPE_EMBEDDINGS
+ }, open = true)
+ @Target({ElementType.TYPE_USE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RequestType {
+ }
+
+
+ /**
+ * Exception type to be populated in callbacks to the methods under
+ * {@link OnDeviceIntelligenceManager}.
+ */
+ public static class OnDeviceIntelligenceManagerException extends Exception {
+ /**
+ * Error code returned when the OnDeviceIntelligenceManager service is unavailable.
+ */
+ public static final int ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE = 1000;
+
+ private final int mErrorCode;
+ private final PersistableBundle errorParams;
+
+ public OnDeviceIntelligenceManagerException(int errorCode, @NonNull String errorMessage,
+ @NonNull PersistableBundle errorParams) {
+ super(errorMessage);
+ this.mErrorCode = errorCode;
+ this.errorParams = errorParams;
+ }
+
+ public OnDeviceIntelligenceManagerException(int errorCode,
+ @NonNull PersistableBundle errorParams) {
+ this.mErrorCode = errorCode;
+ this.errorParams = errorParams;
+ }
+
+ public int getErrorCode() {
+ return mErrorCode;
+ }
+
+ @NonNull
+ public PersistableBundle getErrorParams() {
+ return errorParams;
+ }
+ }
+
+ /**
+ * Exception type to be populated in callbacks to the methods under
+ * {@link OnDeviceIntelligenceManager#processRequest} or
+ * {@link OnDeviceIntelligenceManager#processRequestStreaming} .
+ */
+ public static class OnDeviceIntelligenceManagerProcessingException extends
+ OnDeviceIntelligenceManagerException {
+
+ public static final int PROCESSING_ERROR_UNKNOWN = 1;
+
+ /** Request passed contains bad data for e.g. format. */
+ public static final int PROCESSING_ERROR_BAD_DATA = 2;
+
+ /** Bad request for inputs. */
+ public static final int PROCESSING_ERROR_BAD_REQUEST = 3;
+
+ /** Whole request was classified as not safe, and no response will be generated. */
+ public static final int PROCESSING_ERROR_REQUEST_NOT_SAFE = 4;
+
+ /** Underlying processing encountered an error and failed to compute results. */
+ public static final int PROCESSING_ERROR_COMPUTE_ERROR = 5;
+
+ /** Encountered an error while performing IPC */
+ public static final int PROCESSING_ERROR_IPC_ERROR = 6;
+
+ /** Request was cancelled either by user signal or by the underlying implementation. */
+ public static final int PROCESSING_ERROR_CANCELLED = 7;
+
+ /** Underlying processing in the remote implementation is not available. */
+ public static final int PROCESSING_ERROR_NOT_AVAILABLE = 8;
+
+ /** The service is currently busy. Callers should retry with exponential backoff. */
+ public static final int PROCESSING_ERROR_BUSY = 9;
+
+ /** Something went wrong with safety classification service. */
+ public static final int PROCESSING_ERROR_SAFETY_ERROR = 10;
+
+ /** Response generated was classified unsafe. */
+ public static final int PROCESSING_ERROR_RESPONSE_NOT_SAFE = 11;
+
+ /** Request is too large to be processed. */
+ public static final int PROCESSING_ERROR_REQUEST_TOO_LARGE = 12;
+
+ /** Inference suspended so that higher-priority inference can run. */
+ public static final int PROCESSING_ERROR_SUSPENDED = 13;
+
+ /** Underlying processing encountered an internal error, like a violated precondition. */
+ public static final int PROCESSING_ERROR_INTERNAL = 14;
+
+ /**
+ * The processing was not able to be passed on to the remote implementation, as the service
+ * was unavailable.
+ */
+ public static final int PROCESSING_ERROR_SERVICE_UNAVAILABLE = 15;
+
+ /**
+ * Error code of failed processing request.
+ *
+ * @hide
+ */
+ @IntDef(
+ value = {
+ PROCESSING_ERROR_UNKNOWN,
+ PROCESSING_ERROR_BAD_DATA,
+ PROCESSING_ERROR_BAD_REQUEST,
+ PROCESSING_ERROR_REQUEST_NOT_SAFE,
+ PROCESSING_ERROR_COMPUTE_ERROR,
+ PROCESSING_ERROR_IPC_ERROR,
+ PROCESSING_ERROR_CANCELLED,
+ PROCESSING_ERROR_NOT_AVAILABLE,
+ PROCESSING_ERROR_BUSY,
+ PROCESSING_ERROR_SAFETY_ERROR,
+ PROCESSING_ERROR_RESPONSE_NOT_SAFE,
+ PROCESSING_ERROR_REQUEST_TOO_LARGE,
+ PROCESSING_ERROR_SUSPENDED,
+ PROCESSING_ERROR_INTERNAL,
+ PROCESSING_ERROR_SERVICE_UNAVAILABLE
+ }, open = true)
+ @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
+ @interface ProcessingError {
+ }
+
+ public OnDeviceIntelligenceManagerProcessingException(
+ @ProcessingError int errorCode, @NonNull String errorMessage,
+ @NonNull PersistableBundle errorParams) {
+ super(errorCode, errorMessage, errorParams);
+ }
+
+ public OnDeviceIntelligenceManagerProcessingException(
+ @ProcessingError int errorCode,
+ @NonNull PersistableBundle errorParams) {
+ super(errorCode, errorParams);
+ }
+ }
+}
diff --git a/core/java/android/app/ondeviceintelligence/ProcessingSignal.java b/core/java/android/app/ondeviceintelligence/ProcessingSignal.java
new file mode 100644
index 000000000000..3e543d27143a
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/ProcessingSignal.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayDeque;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * A signal to perform orchestration actions on the inference and optionally receive a output about
+ * the result of the signal. This is an extension of {@link android.os.CancellationSignal}.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public final class ProcessingSignal {
+ private final Object mLock = new Object();
+
+ private static final int MAX_QUEUE_SIZE = 10;
+
+ @GuardedBy("mLock")
+ private final ArrayDeque<PersistableBundle> mActionParamsQueue;
+
+ @GuardedBy("mLock")
+ private IProcessingSignal mRemote;
+
+ private OnProcessingSignalCallback mOnProcessingSignalCallback;
+ private Executor mExecutor;
+
+ public ProcessingSignal() {
+ mActionParamsQueue = new ArrayDeque<>(MAX_QUEUE_SIZE);
+ }
+
+ /**
+ * Interface definition for a callback to be invoked when processing signals are received.
+ */
+ public interface OnProcessingSignalCallback {
+ /**
+ * Called when a custom signal was received.
+ * This method allows the receiver to provide logic to be executed based on the signal
+ * received.
+ *
+ * @param actionParams Parameters for the signal in the form of a {@link PersistableBundle}.
+ */
+
+ void onSignalReceived(@NonNull PersistableBundle actionParams);
+ }
+
+
+ /**
+ * Sends a custom signal with the provided parameters. It also signals the remote callback
+ * with the same params if already configured, if not the action is queued to be sent when a
+ * remote is configured. Similarly, on the receiver side, the callback will be invoked if
+ * already set, if not all actions are queued to be sent to callback when it is set.
+ *
+ * @param actionParams Parameters for the signal.
+ */
+ public void sendSignal(@NonNull PersistableBundle actionParams) {
+ final OnProcessingSignalCallback callback;
+ final IProcessingSignal remote;
+ synchronized (mLock) {
+ if (mActionParamsQueue.size() > MAX_QUEUE_SIZE) {
+ throw new RuntimeException(
+ "Maximum actions that can be queued are : " + MAX_QUEUE_SIZE);
+ }
+
+ mActionParamsQueue.add(actionParams);
+ callback = mOnProcessingSignalCallback;
+ remote = mRemote;
+
+ if (callback != null) {
+ while (!mActionParamsQueue.isEmpty()) {
+ PersistableBundle params = mActionParamsQueue.removeFirst();
+ mExecutor.execute(
+ () -> callback.onSignalReceived(params));
+ }
+ }
+ if (remote != null) {
+ while (!mActionParamsQueue.isEmpty()) {
+ try {
+ remote.sendSignal(mActionParamsQueue.removeFirst());
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Sets the processing signal callback to be called when signals are received.
+ *
+ * This method is intended to be used by the recipient of a processing signal
+ * such as the remote implementation for {@link OnDeviceIntelligenceManager} to handle
+ * cancellation requests while performing a long-running operation. This method is not
+ * intended
+ * to be used by applications themselves.
+ *
+ * If {@link ProcessingSignal#sendSignal} has already been called, then the provided callback
+ * is invoked immediately and all previously queued actions are passed to remote signal.
+ *
+ * This method is guaranteed that the callback will not be called after it
+ * has been removed.
+ *
+ * @param callback The processing signal callback, or null to remove the current callback.
+ * @param executor Executor to the run the callback methods on.
+ */
+ public void setOnProcessingSignalCallback(
+ @NonNull @CallbackExecutor Executor executor,
+ @Nullable OnProcessingSignalCallback callback) {
+ Objects.requireNonNull(executor);
+ synchronized (mLock) {
+ if (mOnProcessingSignalCallback == callback) {
+ return;
+ }
+
+ mOnProcessingSignalCallback = callback;
+ mExecutor = executor;
+ if (callback == null || mActionParamsQueue.isEmpty()) {
+ return;
+ }
+
+ while (!mActionParamsQueue.isEmpty()) {
+ PersistableBundle params = mActionParamsQueue.removeFirst();
+ mExecutor.execute(() -> callback.onSignalReceived(params));
+ }
+ }
+ }
+
+ /**
+ * Sets the remote transport.
+ *
+ * If there are actions queued from {@link ProcessingSignal#sendSignal}, they are also
+ * sequentially sent to the remote.
+ *
+ * This method is guaranteed that the remote transport will not be called after it
+ * has been removed.
+ *
+ * @param remote The remote transport, or null to remove.
+ * @hide
+ */
+ void setRemote(IProcessingSignal remote) {
+ synchronized (mLock) {
+ mRemote = remote;
+ if (mActionParamsQueue.isEmpty() || remote == null) {
+ return;
+ }
+
+ while (!mActionParamsQueue.isEmpty()) {
+ try {
+ remote.sendSignal(mActionParamsQueue.removeFirst());
+ } catch (RemoteException e) {
+ throw new RuntimeException("Failed to send action to remote signal", e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Creates a transport that can be returned back to the caller of
+ * a Binder function and subsequently used to dispatch a processing signal.
+ *
+ * @return The new processing signal transport.
+ * @hide
+ */
+ public static IProcessingSignal createTransport() {
+ return new Transport();
+ }
+
+ /**
+ * Given a locally created transport, returns its associated cancellation signal.
+ *
+ * @param transport The locally created transport, or null if none.
+ * @return The associated processing signal, or null if none.
+ * @hide
+ */
+ public static ProcessingSignal fromTransport(IProcessingSignal transport) {
+ if (transport instanceof Transport) {
+ return ((Transport) transport).mProcessingSignal;
+ }
+ return null;
+ }
+
+ private static final class Transport extends IProcessingSignal.Stub {
+ final ProcessingSignal mProcessingSignal = new ProcessingSignal();
+
+ @Override
+ public void sendSignal(PersistableBundle actionParams) {
+ mProcessingSignal.sendSignal(actionParams);
+ }
+ }
+
+} \ No newline at end of file
diff --git a/core/java/android/app/ondeviceintelligence/StreamingResponseReceiver.java b/core/java/android/app/ondeviceintelligence/StreamingResponseReceiver.java
new file mode 100644
index 000000000000..ebcf61c8c0c2
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/StreamingResponseReceiver.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.OutcomeReceiver;
+
+/**
+ * Streaming variant of outcome receiver to populate response while processing a given request,
+ * possibly in
+ * chunks to provide a async processing behaviour to the caller.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public interface StreamingResponseReceiver<R, T, E extends Throwable> extends
+ OutcomeReceiver<R, E> {
+ /**
+ * Callback to be invoked when a part of the response i.e. some {@link Content} is already
+ * processed and
+ * needs to be passed onto the caller.
+ */
+ void onNewContent(@NonNull T content);
+}
diff --git a/core/java/android/app/ondeviceintelligence/flags/ondevice_intelligence.aconfig b/core/java/android/app/ondeviceintelligence/flags/ondevice_intelligence.aconfig
new file mode 100644
index 000000000000..44f33298b1b2
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/flags/ondevice_intelligence.aconfig
@@ -0,0 +1,8 @@
+package: "android.app.ondeviceintelligence.flags"
+
+flag {
+ name: "enable_on_device_intelligence"
+ namespace: "ondeviceintelligence"
+ description: "Make methods on OnDeviceIntelligenceManager available for local inference."
+ bug: "304755128"
+}
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index 612d433adcd6..79696e047904 100644
--- a/core/java/android/app/servertransaction/ClientTransaction.java
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -29,6 +29,7 @@ import android.os.Parcelable;
import android.os.RemoteException;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.window.flags.Flags;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -85,12 +86,15 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {
* @param item A single message that can contain a client activity/window request/callback.
*/
public void addTransactionItem(@NonNull ClientTransactionItem item) {
- if (mTransactionItems == null) {
- mTransactionItems = new ArrayList<>();
+ if (Flags.bundleClientTransactionFlag()) {
+ if (mTransactionItems == null) {
+ mTransactionItems = new ArrayList<>();
+ }
+ mTransactionItems.add(item);
}
- mTransactionItems.add(item);
// TODO(b/324203798): cleanup after remove UnsupportedAppUsage
+ // Populate even if mTransactionItems is set to support the UnsupportedAppUsage.
if (item.isActivityLifecycleItem()) {
setLifecycleStateRequest((ActivityLifecycleItem) item);
} else {
@@ -114,7 +118,7 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {
*/
// TODO(b/324203798): cleanup after remove UnsupportedAppUsage
@Deprecated
- public void addCallback(@NonNull ClientTransactionItem activityCallback) {
+ private void addCallback(@NonNull ClientTransactionItem activityCallback) {
if (mActivityCallbacks == null) {
mActivityCallbacks = new ArrayList<>();
}
@@ -169,7 +173,7 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {
*/
// TODO(b/324203798): cleanup after remove UnsupportedAppUsage
@Deprecated
- public void setLifecycleStateRequest(@NonNull ActivityLifecycleItem stateRequest) {
+ private void setLifecycleStateRequest(@NonNull ActivityLifecycleItem stateRequest) {
if (mLifecycleStateRequest != null) {
return;
}
diff --git a/core/java/android/app/servertransaction/ClientTransactionListenerController.java b/core/java/android/app/servertransaction/ClientTransactionListenerController.java
index 9f97f6ff7c39..1a8136e06c28 100644
--- a/core/java/android/app/servertransaction/ClientTransactionListenerController.java
+++ b/core/java/android/app/servertransaction/ClientTransactionListenerController.java
@@ -23,7 +23,6 @@ import static java.util.Objects.requireNonNull;
import android.annotation.NonNull;
import android.app.ActivityThread;
import android.hardware.display.DisplayManagerGlobal;
-import android.os.Process;
import com.android.internal.annotations.VisibleForTesting;
@@ -67,7 +66,7 @@ public class ClientTransactionListenerController {
* window configuration.
*/
public void onDisplayChanged(int displayId) {
- if (!isBundleClientTransactionFlagEnabled()) {
+ if (!bundleClientTransactionFlag()) {
return;
}
if (ActivityThread.isSystem()) {
@@ -76,10 +75,4 @@ public class ClientTransactionListenerController {
}
mDisplayManager.handleDisplayChangeFromWindowManager(displayId);
}
-
- /** Whether {@link #bundleClientTransactionFlag} feature flag is enabled. */
- public boolean isBundleClientTransactionFlagEnabled() {
- // Can't read flag from isolated process.
- return !Process.isIsolated() && bundleClientTransactionFlag();
- }
}
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index 95f5ad0bd38f..f02cb212276b 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -42,6 +42,7 @@ import android.os.IBinder;
import android.os.Parcel;
import android.os.PersistableBundle;
import android.os.Trace;
+import android.window.ActivityWindowInfo;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IVoiceInteractor;
@@ -81,6 +82,8 @@ public class LaunchActivityItem extends ClientTransactionItem {
private boolean mLaunchedFromBubble;
private IBinder mTaskFragmentToken;
private IBinder mInitialCallerInfoAccessToken;
+ private ActivityWindowInfo mActivityWindowInfo;
+
/**
* It is only non-null if the process is the first time to launch activity. It is only an
* optimization for quick look up of the interface so the field is ignored for comparison.
@@ -107,7 +110,7 @@ public class LaunchActivityItem extends ClientTransactionItem {
mOverrideConfig, mReferrer, mVoiceInteractor, mState, mPersistentState,
mPendingResults, mPendingNewIntents, mSceneTransitionInfo, mIsForward,
mProfilerInfo, client, mAssistToken, mShareableActivityToken, mLaunchedFromBubble,
- mTaskFragmentToken, mInitialCallerInfoAccessToken);
+ mTaskFragmentToken, mInitialCallerInfoAccessToken, mActivityWindowInfo);
client.handleLaunchActivity(r, pendingActions, mDeviceId, null /* customIntent */);
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -141,7 +144,8 @@ public class LaunchActivityItem extends ClientTransactionItem {
boolean isForward, @Nullable ProfilerInfo profilerInfo, @NonNull IBinder assistToken,
@Nullable IActivityClientController activityClientController,
@NonNull IBinder shareableActivityToken, boolean launchedFromBubble,
- @Nullable IBinder taskFragmentToken, @NonNull IBinder initialCallerInfoAccessToken) {
+ @Nullable IBinder taskFragmentToken, @NonNull IBinder initialCallerInfoAccessToken,
+ @NonNull ActivityWindowInfo activityWindowInfo) {
LaunchActivityItem instance = ObjectPool.obtain(LaunchActivityItem.class);
if (instance == null) {
instance = new LaunchActivityItem();
@@ -156,7 +160,8 @@ public class LaunchActivityItem extends ClientTransactionItem {
sceneTransitionInfo, isForward,
profilerInfo != null ? new ProfilerInfo(profilerInfo) : null,
assistToken, activityClientController, shareableActivityToken,
- launchedFromBubble, taskFragmentToken, initialCallerInfoAccessToken);
+ launchedFromBubble, taskFragmentToken, initialCallerInfoAccessToken,
+ new ActivityWindowInfo(activityWindowInfo));
return instance;
}
@@ -171,7 +176,7 @@ public class LaunchActivityItem extends ClientTransactionItem {
@Override
public void recycle() {
setValues(this, null, null, 0, null, null, null, 0, null, null, 0, null, null, null, null,
- null, false, null, null, null, null, false, null, null);
+ null, false, null, null, null, null, false, null, null, null);
ObjectPool.recycle(this);
}
@@ -203,6 +208,7 @@ public class LaunchActivityItem extends ClientTransactionItem {
dest.writeBoolean(mLaunchedFromBubble);
dest.writeStrongBinder(mTaskFragmentToken);
dest.writeStrongBinder(mInitialCallerInfoAccessToken);
+ dest.writeTypedObject(mActivityWindowInfo, flags);
}
/** Read from Parcel. */
@@ -223,7 +229,8 @@ public class LaunchActivityItem extends ClientTransactionItem {
in.readStrongBinder(),
in.readBoolean(),
in.readStrongBinder(),
- in.readStrongBinder());
+ in.readStrongBinder(),
+ in.readTypedObject(ActivityWindowInfo.CREATOR));
}
public static final @NonNull Creator<LaunchActivityItem> CREATOR = new Creator<>() {
@@ -264,7 +271,8 @@ public class LaunchActivityItem extends ClientTransactionItem {
&& Objects.equals(mShareableActivityToken, other.mShareableActivityToken)
&& Objects.equals(mTaskFragmentToken, other.mTaskFragmentToken)
&& Objects.equals(mInitialCallerInfoAccessToken,
- other.mInitialCallerInfoAccessToken);
+ other.mInitialCallerInfoAccessToken)
+ && Objects.equals(mActivityWindowInfo, other.mActivityWindowInfo);
}
@Override
@@ -289,6 +297,7 @@ public class LaunchActivityItem extends ClientTransactionItem {
result = 31 * result + Objects.hashCode(mShareableActivityToken);
result = 31 * result + Objects.hashCode(mTaskFragmentToken);
result = 31 * result + Objects.hashCode(mInitialCallerInfoAccessToken);
+ result = 31 * result + Objects.hashCode(mActivityWindowInfo);
return result;
}
@@ -335,7 +344,9 @@ public class LaunchActivityItem extends ClientTransactionItem {
+ ",sceneTransitionInfo=" + mSceneTransitionInfo
+ ",profilerInfo=" + mProfilerInfo
+ ",assistToken=" + mAssistToken
- + ",shareableActivityToken=" + mShareableActivityToken + "}";
+ + ",shareableActivityToken=" + mShareableActivityToken
+ + ",activityWindowInfo=" + mActivityWindowInfo
+ + "}";
}
// Using the same method to set and clear values to make sure we don't forget anything
@@ -351,7 +362,8 @@ public class LaunchActivityItem extends ClientTransactionItem {
@Nullable ProfilerInfo profilerInfo, @Nullable IBinder assistToken,
@Nullable IActivityClientController activityClientController,
@Nullable IBinder shareableActivityToken, boolean launchedFromBubble,
- @Nullable IBinder taskFragmentToken, @Nullable IBinder initialCallerInfoAccessToken) {
+ @Nullable IBinder taskFragmentToken, @Nullable IBinder initialCallerInfoAccessToken,
+ @Nullable ActivityWindowInfo activityWindowInfo) {
instance.mActivityToken = activityToken;
instance.mIntent = intent;
instance.mIdent = ident;
@@ -375,5 +387,6 @@ public class LaunchActivityItem extends ClientTransactionItem {
instance.mLaunchedFromBubble = launchedFromBubble;
instance.mTaskFragmentToken = taskFragmentToken;
instance.mInitialCallerInfoAccessToken = initialCallerInfoAccessToken;
+ instance.mActivityWindowInfo = activityWindowInfo;
}
}
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index 406e00a84710..fa73c99be2b8 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -40,7 +40,6 @@ import android.app.ClientTransactionHandler;
import android.content.Context;
import android.content.res.Configuration;
import android.os.IBinder;
-import android.os.Process;
import android.os.Trace;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -218,8 +217,6 @@ public class TransactionExecutor {
final boolean shouldTrackConfigUpdatedContext =
// No configuration change for local transaction.
!mTransactionHandler.isExecutingLocalTransaction()
- // Can't read flag from isolated process.
- && !Process.isIsolated()
&& bundleClientTransactionFlag();
final Context configUpdatedContext = shouldTrackConfigUpdatedContext
? item.getContextToUpdate(mTransactionHandler)
diff --git a/core/java/android/app/ui_mode_manager.aconfig b/core/java/android/app/ui_mode_manager.aconfig
index 1ae5264a7e8e..27a38cc2bfd6 100644
--- a/core/java/android/app/ui_mode_manager.aconfig
+++ b/core/java/android/app/ui_mode_manager.aconfig
@@ -1,8 +1,11 @@
package: "android.app"
flag {
- namespace: "system_performance"
- name: "enable_night_mode_cache"
+ namespace: "systemui"
+ name: "enable_night_mode_binder_cache"
description: "Enables the use of binder caching for system night mode."
bug: "255999432"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
} \ No newline at end of file
diff --git a/core/java/android/app/wearable/IWearableSensingManager.aidl b/core/java/android/app/wearable/IWearableSensingManager.aidl
index 3cbc8a21d2d8..7d3b28511537 100644
--- a/core/java/android/app/wearable/IWearableSensingManager.aidl
+++ b/core/java/android/app/wearable/IWearableSensingManager.aidl
@@ -17,6 +17,7 @@
package android.app.wearable;
import android.app.PendingIntent;
+import android.content.ComponentName;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.RemoteCallback;
@@ -29,7 +30,7 @@ import android.os.SharedMemory;
*/
interface IWearableSensingManager {
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)")
- void provideWearableConnection(in ParcelFileDescriptor parcelFileDescriptor, in RemoteCallback callback);
+ void provideConnection(in ParcelFileDescriptor parcelFileDescriptor, in RemoteCallback callback);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)")
void provideDataStream(in ParcelFileDescriptor parcelFileDescriptor, in RemoteCallback callback);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)")
@@ -38,4 +39,8 @@ interface IWearableSensingManager {
void registerDataRequestObserver(int dataType, in PendingIntent dataRequestPendingIntent, in RemoteCallback statusCallback);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)")
void unregisterDataRequestObserver(int dataType, in PendingIntent dataRequestPendingIntent, in RemoteCallback statusCallback);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)")
+ void startHotwordRecognition(in ComponentName targetVisComponentName, in RemoteCallback statusCallback);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)")
+ void stopHotwordRecognition(in RemoteCallback statusCallback);
} \ No newline at end of file
diff --git a/core/java/android/app/wearable/WearableSensingManager.java b/core/java/android/app/wearable/WearableSensingManager.java
index 3b281e9c1ae2..fd72c491bf16 100644
--- a/core/java/android/app/wearable/WearableSensingManager.java
+++ b/core/java/android/app/wearable/WearableSensingManager.java
@@ -28,6 +28,7 @@ import android.annotation.SystemService;
import android.app.PendingIntent;
import android.app.ambientcontext.AmbientContextEvent;
import android.companion.CompanionDeviceManager;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
@@ -92,9 +93,13 @@ public class WearableSensingManager {
public static final int STATUS_SUCCESS = 1;
/**
- * The value of the status code that indicates one or more of the
- * requested events are not supported.
+ * The value of the status code that indicates one or more of the requested events are not
+ * supported.
*/
+ // TODO(b/324635656): Deprecate this status code. Update Javadoc:
+ // @deprecated WearableSensingManager does not deal with events. Use {@link
+ // STATUS_UNSUPPORTED_OPERATION} instead for operations not supported by the implementation of
+ // {@link WearableSensingService}.
public static final int STATUS_UNSUPPORTED = 2;
/**
@@ -122,7 +127,7 @@ public class WearableSensingManager {
/**
* The value of the status code that indicates an error occurred in the encrypted channel backed
- * by the provided connection. See {@link #provideWearableConnection(ParcelFileDescriptor,
+ * by the provided connection. See {@link #provideConnection(ParcelFileDescriptor,
* Executor, Consumer)}.
*/
@FlaggedApi(Flags.FLAG_ENABLE_PROVIDE_WEARABLE_CONNECTION_API)
@@ -218,13 +223,13 @@ public class WearableSensingManager {
*/
@RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)
@FlaggedApi(Flags.FLAG_ENABLE_PROVIDE_WEARABLE_CONNECTION_API)
- public void provideWearableConnection(
+ public void provideConnection(
@NonNull ParcelFileDescriptor wearableConnection,
@NonNull @CallbackExecutor Executor executor,
@NonNull @StatusCode Consumer<Integer> statusConsumer) {
try {
RemoteCallback callback = createStatusCallback(executor, statusConsumer);
- mService.provideWearableConnection(wearableConnection, callback);
+ mService.provideConnection(wearableConnection, callback);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -382,6 +387,83 @@ public class WearableSensingManager {
}
}
+ /**
+ * Requests the wearable to start hotword recognition.
+ *
+ * <p>When this method is called, the system will attempt to provide a {@link
+ * android.service.wearable.WearableHotwordAudioConsumer} to {@link WearableSensingService}.
+ * After first-stage hotword is detected on a wearable, {@link WearableSensingService} should
+ * send the hotword audio to the {@link android.service.wearable.WearableHotwordAudioConsumer},
+ * which will forward the data to the {@link android.service.voice.HotwordDetectionService} for
+ * second-stage hotword validation. If hotword is detected there, the audio data will be
+ * forwarded to the {@link android.service.voice.VoiceInteractionService}.
+ *
+ * <p>If the {@code targetVisComponentName} provided here is not null, when {@link
+ * WearableSensingService} sends hotword audio to the {@link
+ * android.service.wearable.WearableHotwordAudioConsumer}, the system will check whether the
+ * {@link android.service.voice.VoiceInteractionService} at that time is {@code
+ * targetVisComponentName}. If not, the system will call {@link
+ * WearableSensingService#onActiveHotwordAudioStopRequested()} and will not forward the audio
+ * data to the current {@link android.service.voice.HotwordDetectionService} nor {@link
+ * android.service.voice.VoiceInteractionService}. The system will not send a status code to
+ * {@code statusConsumer} regarding the {@code targetVisComponentName} check. The caller is
+ * responsible for determining whether the system's {@link
+ * android.service.voice.VoiceInteractionService} is the same as {@code targetVisComponentName}.
+ * The check here is just a protection against race conditions.
+ *
+ * <p>Calling this method again will send a new {@link
+ * android.service.wearable.WearableHotwordAudioConsumer} to {@link WearableSensingService}. For
+ * audio data sent to the new consumer, the system will perform the above check using the newly
+ * provided {@code targetVisComponentName}. The {@link WearableSensingService} should not
+ * continue to use the previous consumers after receiving a new one.
+ *
+ * <p>If the {@code statusConsumer} returns {@link STATUS_SUCCESS}, the caller should call
+ * {@link #stopListeningForHotword(Executor, Consumer)} when it wants the wearable to stop
+ * listening for hotword. If the {@code statusConsumer} returns any other status code, a failure
+ * has occurred and calling {@link #stopListeningForHotword(Executor, Consumer)} is not
+ * required. The system will not retry listening automatically. The caller should call this
+ * method again if they want to retry.
+ *
+ * <p>If a failure occurred after the {@link statusConsumer} returns {@link STATUS_SUCCESS},
+ * {@link statusConsumer} will be invoked again with a status code other than {@link
+ * STATUS_SUCCESS}.
+ *
+ * @param targetVisComponentName The ComponentName of the target VoiceInteractionService.
+ * @param executor Executor on which to run the consumer callback.
+ * @param statusConsumer A consumer that handles the status codes.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_HOTWORD_WEARABLE_SENSING_API)
+ @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)
+ public void startHotwordRecognition(
+ @Nullable ComponentName targetVisComponentName,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull @StatusCode Consumer<Integer> statusConsumer) {
+ try {
+ mService.startHotwordRecognition(
+ targetVisComponentName, createStatusCallback(executor, statusConsumer));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Requests the wearable to stop hotword recognition.
+ *
+ * @param executor Executor on which to run the consumer callback.
+ * @param statusConsumer A consumer that handles the status codes.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_HOTWORD_WEARABLE_SENSING_API)
+ @RequiresPermission(Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE)
+ public void stopHotwordRecognition(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull @StatusCode Consumer<Integer> statusConsumer) {
+ try {
+ mService.stopHotwordRecognition(createStatusCallback(executor, statusConsumer));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
private static RemoteCallback createStatusCallback(
Executor executor, Consumer<Integer> statusConsumer) {
return new RemoteCallback(
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index d74399274a60..91baa4ea1eeb 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -707,7 +707,9 @@ public final class CompanionDeviceManager {
* Only components from the same {@link ComponentName#getPackageName package} as the calling app
* are allowed.
*
- * Your app must have an association with a device before calling this API
+ * Your app must have an association with a device before calling this API.
+ *
+ * Side-loaded apps must allow restricted settings before requesting notification access.
*
* <p>Calling this API requires a uses-feature
* {@link PackageManager#FEATURE_COMPANION_DEVICE_SETUP} declaration in the manifest</p>
@@ -721,6 +723,9 @@ public final class CompanionDeviceManager {
IntentSender intentSender = mService
.requestNotificationAccess(component, mContext.getUserId())
.getIntentSender();
+ if (intentSender == null) {
+ return;
+ }
mContext.startIntentSender(intentSender, null, 0, 0, 0,
ActivityOptions.makeBasic().setPendingIntentBackgroundActivityStartMode(
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED).toBundle());
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index ab8db6e59ddb..24d6a5cfc42d 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -29,3 +29,18 @@ flag {
bug: "291737188"
is_fixed_read_only: true
}
+
+flag {
+ namespace: "virtual_devices"
+ name: "metrics_collection"
+ description: "Enable collection of VDM-related metrics"
+ bug: "324842215"
+ is_fixed_read_only: true
+}
+
+flag {
+ namespace: "virtual_devices"
+ name: "camera_device_awareness"
+ description: "Enable device awareness in camera service"
+ bug: "305170199"
+}
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index eb357fe09a31..728c350bfb51 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -26,8 +26,6 @@ import static com.android.window.flags.Flags.FLAG_DELEGATE_UNHANDLED_DRAGS;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SuppressLint;
-import android.app.PendingIntent;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo;
import android.content.res.AssetFileDescriptor;
@@ -213,7 +211,7 @@ public class ClipData implements Parcelable {
final CharSequence mText;
final String mHtmlText;
final Intent mIntent;
- final PendingIntent mPendingIntent;
+ final IntentSender mIntentSender;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
Uri mUri;
private TextLinks mTextLinks;
@@ -225,12 +223,11 @@ public class ClipData implements Parcelable {
* A builder for a ClipData Item.
*/
@FlaggedApi(FLAG_DELEGATE_UNHANDLED_DRAGS)
- @SuppressLint("PackageLayering")
public static final class Builder {
private CharSequence mText;
private String mHtmlText;
private Intent mIntent;
- private PendingIntent mPendingIntent;
+ private IntentSender mIntentSender;
private Uri mUri;
/**
@@ -264,18 +261,20 @@ public class ClipData implements Parcelable {
}
/**
- * Sets the PendingIntent for the item to be constructed. To prevent receiving apps from
- * improperly manipulating the intent to launch another activity as this caller, the
- * provided PendingIntent must be immutable (see {@link PendingIntent#FLAG_IMMUTABLE}).
- * The system will clean up the PendingIntent when it is no longer used.
+ * Sets the {@link IntentSender} for the item to be constructed. To prevent receiving
+ * apps from improperly manipulating the intent to launch another activity as this
+ * caller, the provided IntentSender must be immutable.
+ *
+ * If there is a fixed lifetime for this ClipData (ie. for drag and drop), the system
+ * will cancel the IntentSender when it is no longer used.
*/
@FlaggedApi(FLAG_DELEGATE_UNHANDLED_DRAGS)
@NonNull
- public Builder setPendingIntent(@Nullable PendingIntent pendingIntent) {
- if (pendingIntent != null && !pendingIntent.isImmutable()) {
- throw new IllegalArgumentException("Expected pending intent to be immutable");
+ public Builder setIntentSender(@Nullable IntentSender intentSender) {
+ if (intentSender != null && !intentSender.isImmutable()) {
+ throw new IllegalArgumentException("Expected intent sender to be immutable");
}
- mPendingIntent = pendingIntent;
+ mIntentSender = intentSender;
return this;
}
@@ -295,7 +294,7 @@ public class ClipData implements Parcelable {
@FlaggedApi(FLAG_DELEGATE_UNHANDLED_DRAGS)
@NonNull
public Item build() {
- return new Item(mText, mHtmlText, mIntent, mPendingIntent, mUri);
+ return new Item(mText, mHtmlText, mIntent, mIntentSender, mUri);
}
}
@@ -305,7 +304,7 @@ public class ClipData implements Parcelable {
mText = other.mText;
mHtmlText = other.mHtmlText;
mIntent = other.mIntent;
- mPendingIntent = other.mPendingIntent;
+ mIntentSender = other.mIntentSender;
mUri = other.mUri;
mActivityInfo = other.mActivityInfo;
mTextLinks = other.mTextLinks;
@@ -366,7 +365,7 @@ public class ClipData implements Parcelable {
/**
* Builder ctor.
*/
- private Item(CharSequence text, String htmlText, Intent intent, PendingIntent pendingIntent,
+ private Item(CharSequence text, String htmlText, Intent intent, IntentSender intentSender,
Uri uri) {
if (htmlText != null && text == null) {
throw new IllegalArgumentException(
@@ -375,7 +374,7 @@ public class ClipData implements Parcelable {
mText = text;
mHtmlText = htmlText;
mIntent = intent;
- mPendingIntent = pendingIntent;
+ mIntentSender = intentSender;
mUri = uri;
}
@@ -401,12 +400,12 @@ public class ClipData implements Parcelable {
}
/**
- * Returns the pending intent in this Item.
+ * Returns the {@link IntentSender} in this Item.
*/
@FlaggedApi(FLAG_DELEGATE_UNHANDLED_DRAGS)
@Nullable
- public PendingIntent getPendingIntent() {
- return mPendingIntent;
+ public IntentSender getIntentSender() {
+ return mIntentSender;
}
/**
@@ -1131,35 +1130,6 @@ public class ClipData implements Parcelable {
}
/**
- * Checks if this clip data has a pending intent that is an activity type.
- * @hide
- */
- public boolean hasActivityPendingIntents() {
- final int size = mItems.size();
- for (int i = 0; i < size; i++) {
- final Item item = mItems.get(i);
- if (item.mPendingIntent != null && item.mPendingIntent.isActivity()) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Cleans up all pending intents in the ClipData.
- * @hide
- */
- public void cleanUpPendingIntents() {
- final int size = mItems.size();
- for (int i = 0; i < size; i++) {
- final Item item = mItems.get(i);
- if (item.mPendingIntent != null) {
- item.mPendingIntent.cancel();
- }
- }
- }
-
- /**
* Prepare this {@link ClipData} to leave an app process.
*
* @hide
@@ -1361,7 +1331,7 @@ public class ClipData implements Parcelable {
TextUtils.writeToParcel(item.mText, dest, flags);
dest.writeString8(item.mHtmlText);
dest.writeTypedObject(item.mIntent, flags);
- dest.writeTypedObject(item.mPendingIntent, flags);
+ dest.writeTypedObject(item.mIntentSender, flags);
dest.writeTypedObject(item.mUri, flags);
dest.writeTypedObject(mParcelItemActivityInfos ? item.mActivityInfo : null, flags);
dest.writeTypedObject(item.mTextLinks, flags);
@@ -1381,11 +1351,11 @@ public class ClipData implements Parcelable {
CharSequence text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
String htmlText = in.readString8();
Intent intent = in.readTypedObject(Intent.CREATOR);
- PendingIntent pendingIntent = in.readTypedObject(PendingIntent.CREATOR);
+ IntentSender intentSender = in.readTypedObject(IntentSender.CREATOR);
Uri uri = in.readTypedObject(Uri.CREATOR);
ActivityInfo info = in.readTypedObject(ActivityInfo.CREATOR);
TextLinks textLinks = in.readTypedObject(TextLinks.CREATOR);
- Item item = new Item(text, htmlText, intent, pendingIntent, uri);
+ Item item = new Item(text, htmlText, intent, intentSender, uri);
item.setActivityInfo(info);
item.setTextLinks(textLinks);
mItems.add(item);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 9e192a06d404..f77ebc148070 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4815,7 +4815,9 @@ public abstract class Context {
* @see android.net.thread.ThreadNetworkManager
* @hide
*/
- @FlaggedApi("com.android.net.thread.flags.thread_enabled")
+ // TODO (b/325886480): update the flag to
+ // "com.android.net.thread.platform.flags.Flags.FLAG_THREAD_ENABLED_PLATFORM"
+ @FlaggedApi("com.android.net.thread.flags.thread_enabled_platform")
@SystemApi
public static final String THREAD_NETWORK_SERVICE = "thread_network";
@@ -6450,6 +6452,19 @@ public abstract class Context {
@SystemApi
public static final String WEARABLE_SENSING_SERVICE = "wearable_sensing";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.app.ondeviceintelligence.OnDeviceIntelligenceManager}.
+ *
+ * @see #getSystemService(String)
+ * @see OnDeviceIntelligenceManager
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+ public static final String ON_DEVICE_INTELLIGENCE_SERVICE = "on_device_intelligence";
+
/**
* Use with {@link #getSystemService(String)} to retrieve a
* {@link android.health.connect.HealthConnectManager}.
@@ -6543,6 +6558,13 @@ public abstract class Context {
public static final String ECM_ENHANCED_CONFIRMATION_SERVICE = "ecm_enhanced_confirmation";
/**
+ * Service to protect sensitive content during screen share.
+ * @hide
+ */
+ public static final String SENSITIVE_CONTENT_PROTECTION_SERVICE =
+ "sensitive_content_protection_service";
+
+ /**
* Use with {@link #getSystemService(String)} to retrieve a
* {@link android.provider.ContactKeysManager} to managing contact keys.
*
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index e8031a374310..fd2af99b6a7e 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -19,6 +19,7 @@ package android.content;
import static android.app.sdksandbox.SdkSandboxManager.ACTION_START_SANDBOXED_ACTIVITY;
import static android.content.ContentProvider.maybeAddUserId;
import static android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE;
+import static android.security.Flags.FLAG_FRP_ENFORCEMENT;
import static android.service.chooser.Flags.FLAG_ENABLE_SHARESHEET_METADATA_EXTRA;
import android.Manifest;
@@ -3902,6 +3903,26 @@ public class Intent implements Parcelable, Cloneable {
"android.intent.action.ACTION_IDLE_MAINTENANCE_END";
/**
+ * Broadcast Action: A broadcast sent to the main user when the main user changes their
+ * Lock Screen Knowledge Factor, either because they changed the current value, or because
+ * they added or removed it.
+ *
+ * <p class="note">At present, this intent is only broadcast to listeners with the
+ * CONFIGURE_FACTORY_RESET_PROTECTION signature|privileged permiession.</p>
+ *
+ * <p class="note">This is a protected intent that can only be sent by the system.</p>
+ *
+ * @hide
+ */
+ @FlaggedApi(FLAG_FRP_ENFORCEMENT)
+ @SystemApi
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @BroadcastBehavior(protectedBroadcast = true)
+ public static final String
+ ACTION_MAIN_USER_LOCKSCREEN_KNOWLEDGE_FACTOR_CHANGED =
+ "android.intent.action.MAIN_USER_LOCKSCREEN_KNOWLEDGE_FACTOR_CHANGED";
+
+ /**
* Broadcast Action: a remote intent is to be broadcasted.
*
* A remote intent is used for remote RPC between devices. The remote intent
@@ -7635,6 +7656,22 @@ public class Intent implements Parcelable, Cloneable {
/** @hide */
public static final int LOCAL_FLAG_FROM_SYSTEM = 1 << 5;
+ /** @hide */
+ @IntDef(flag = true, prefix = { "EXTENDED_FLAG_" }, value = {
+ EXTENDED_FLAG_FILTER_MISMATCH,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ExtendedFlags {}
+
+ /**
+ * This flag is not normally set by application code, but set for you by the system if
+ * an external intent does not match the receiving component's intent filter.
+ *
+ * @hide
+ */
+ @TestApi
+ public static final int EXTENDED_FLAG_FILTER_MISMATCH = 1 << 0;
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// toUri() and parseUri() options.
@@ -7754,6 +7791,7 @@ public class Intent implements Parcelable, Cloneable {
private int mFlags;
/** Set of in-process flags which are never parceled */
private int mLocalFlags;
+ private int mExtendedFlags;
private ArraySet<String> mCategories;
@UnsupportedAppUsage
private Bundle mExtras;
@@ -7813,6 +7851,7 @@ public class Intent implements Parcelable, Cloneable {
if (copyMode != COPY_MODE_FILTER) {
this.mFlags = o.mFlags;
+ this.mExtendedFlags = o.mExtendedFlags;
this.mContentUserHint = o.mContentUserHint;
this.mLaunchToken = o.mLaunchToken;
if (o.mSourceBounds != null) {
@@ -8140,12 +8179,17 @@ public class Intent implements Parcelable, Cloneable {
// launch flags
else if (uri.startsWith("launchFlags=", i)) {
- intent.mFlags = Integer.decode(value).intValue();
+ intent.mFlags = Integer.decode(value);
if ((flags& URI_ALLOW_UNSAFE) == 0) {
intent.mFlags &= ~IMMUTABLE_FLAGS;
}
}
+ // extended flags
+ else if (uri.startsWith("extendedLaunchFlags=", i)) {
+ intent.mExtendedFlags = Integer.decode(value);
+ }
+
// package
else if (uri.startsWith("package=", i)) {
intent.mPackage = value;
@@ -8353,7 +8397,7 @@ public class Intent implements Parcelable, Cloneable {
isIntentFragment = true;
i += 12;
int j = uri.indexOf(')', i);
- intent.mFlags = Integer.decode(uri.substring(i, j)).intValue();
+ intent.mFlags = Integer.decode(uri.substring(i, j));
if ((flags& URI_ALLOW_UNSAFE) == 0) {
intent.mFlags &= ~IMMUTABLE_FLAGS;
}
@@ -9847,6 +9891,22 @@ public class Intent implements Parcelable, Cloneable {
return mFlags;
}
+ /**
+ * Retrieve any extended flags associated with this intent. You will
+ * normally just set them with {@link #setExtendedFlags} and let the system
+ * take the appropriate action with them.
+ *
+ * @return The currently set extended flags.
+ * @see #addExtendedFlags
+ * @see #removeExtendedFlags
+ *
+ * @hide
+ */
+ @TestApi
+ public @ExtendedFlags int getExtendedFlags() {
+ return mExtendedFlags;
+ }
+
/** @hide */
@UnsupportedAppUsage
public boolean isExcludingStopped() {
@@ -11177,6 +11237,23 @@ public class Intent implements Parcelable, Cloneable {
}
/**
+ * Add additional extended flags to the intent (or with existing flags value).
+ *
+ * @param flags The new flags to set.
+ * @return Returns the same Intent object, for chaining multiple calls into
+ * a single statement.
+ * @see #getExtendedFlags
+ * @see #removeExtendedFlags
+ *
+ * @hide
+ */
+ @TestApi
+ public @NonNull Intent addExtendedFlags(@ExtendedFlags int flags) {
+ mExtendedFlags |= flags;
+ return this;
+ }
+
+ /**
* Remove these flags from the intent.
*
* @param flags The flags to remove.
@@ -11189,6 +11266,19 @@ public class Intent implements Parcelable, Cloneable {
}
/**
+ * Remove these extended flags from the intent.
+ *
+ * @param flags The flags to remove.
+ * @see #getExtendedFlags
+ * @see #addExtendedFlags
+ *
+ * @hide
+ */
+ public void removeExtendedFlags(@ExtendedFlags int flags) {
+ mExtendedFlags &= ~flags;
+ }
+
+ /**
* (Usually optional) Set an explicit application package name that limits
* the components this Intent will resolve to. If left to the default
* value of null, all components in all applications will considered.
@@ -11492,6 +11582,7 @@ public class Intent implements Parcelable, Cloneable {
changes |= FILL_IN_COMPONENT;
}
mFlags |= other.mFlags;
+ mExtendedFlags |= other.mExtendedFlags;
if (other.mSourceBounds != null
&& (mSourceBounds == null || (flags&FILL_IN_SOURCE_BOUNDS) != 0)) {
mSourceBounds = new Rect(other.mSourceBounds);
@@ -11727,6 +11818,13 @@ public class Intent implements Parcelable, Cloneable {
first = false;
b.append("flg=0x").append(Integer.toHexString(mFlags));
}
+ if (mExtendedFlags != 0) {
+ if (!first) {
+ b.append(' ');
+ }
+ first = false;
+ b.append("xflg=0x").append(Integer.toHexString(mExtendedFlags));
+ }
if (mPackage != null) {
if (!first) {
b.append(' ');
@@ -11825,6 +11923,9 @@ public class Intent implements Parcelable, Cloneable {
if (mFlags != 0) {
proto.write(IntentProto.FLAG, "0x" + Integer.toHexString(mFlags));
}
+ if (mExtendedFlags != 0) {
+ proto.write(IntentProto.EXTENDED_FLAG, "0x" + Integer.toHexString(mExtendedFlags));
+ }
if (mPackage != null) {
proto.write(IntentProto.PACKAGE, mPackage);
}
@@ -11998,6 +12099,10 @@ public class Intent implements Parcelable, Cloneable {
if (mFlags != 0) {
uri.append("launchFlags=0x").append(Integer.toHexString(mFlags)).append(';');
}
+ if (mExtendedFlags != 0) {
+ uri.append("extendedLaunchFlags=0x").append(Integer.toHexString(mExtendedFlags))
+ .append(';');
+ }
if (mPackage != null && !mPackage.equals(defPackage)) {
uri.append("package=").append(Uri.encode(mPackage)).append(';');
}
@@ -12047,6 +12152,7 @@ public class Intent implements Parcelable, Cloneable {
out.writeString8(mType);
out.writeString8(mIdentifier);
out.writeInt(mFlags);
+ out.writeInt(mExtendedFlags);
out.writeString8(mPackage);
ComponentName.writeToParcel(mComponent, out);
@@ -12115,6 +12221,7 @@ public class Intent implements Parcelable, Cloneable {
mType = in.readString8();
mIdentifier = in.readString8();
mFlags = in.readInt();
+ mExtendedFlags = in.readInt();
mPackage = in.readString8();
mComponent = ComponentName.readFromParcel(in);
@@ -12539,6 +12646,32 @@ public class Intent implements Parcelable, Cloneable {
}
/**
+ * Whether the intent mismatches all intent filters declared in the receiving component.
+ * <p>
+ * When a component receives an intent, normally obtainable through the following methods:
+ * <ul>
+ * <li> {@link BroadcastReceiver#onReceive(Context, Intent)}
+ * <li> {@link Activity#getIntent()}
+ * <li> {@link Activity#onNewIntent)}
+ * <li> {@link android.app.Service#onStartCommand(Intent, int, int)}
+ * <li> {@link android.app.Service#onBind(Intent)}
+ * </ul>
+ * The developer can call this method to check if this intent does not match any of its
+ * declared intent filters. A non-matching intent can be delivered when the intent sender
+ * explicitly set the component through {@link #setComponent} or {@link #setClassName}.
+ * <p>
+ * This method always returns {@code false} if the intent originated from within the same
+ * application or the system, because these cases are always exempted from security checks.
+ *
+ * @return Returns true if the intent does not match any intent filters declared in the
+ * receiving component.
+ */
+ @FlaggedApi(android.security.Flags.FLAG_ENFORCE_INTENT_FILTER_MATCH)
+ public boolean isMismatchingFilter() {
+ return (mExtendedFlags & EXTENDED_FLAG_FILTER_MISMATCH) != 0;
+ }
+
+ /**
* @hide
*/
@android.ravenwood.annotation.RavenwoodThrow
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index a8dba5182e90..cae4fab0099c 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1565,6 +1565,14 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
private Boolean requestRawExternalStorageAccess;
/**
+ * If {@code false}, this app does not allow its activities to be replaced by another app.
+ * Is set from application manifest application tag's allowCrossUidActivitySwitchFromBelow
+ * attribute.
+ * @hide
+ */
+ public boolean allowCrossUidActivitySwitchFromBelow = true;
+
+ /**
* Represents the default policy. The actual policy used will depend on other properties of
* the application, e.g. the target SDK version.
* @hide
@@ -1760,6 +1768,9 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
+ Integer.toHexString(localeConfigRes));
}
pw.println(prefix + "enableOnBackInvokedCallback=" + isOnBackInvokedCallbackEnabled());
+ pw.println(prefix + "allowCrossUidActivitySwitchFromBelow="
+ + allowCrossUidActivitySwitchFromBelow);
+
}
pw.println(prefix + "createTimestamp=" + createTimestamp);
if (mKnownActivityEmbeddingCerts != null) {
@@ -1877,6 +1888,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
proto.write(ApplicationInfoProto.Detail.NATIVE_HEAP_ZERO_INIT,
nativeHeapZeroInitialized);
}
+ proto.write(ApplicationInfoProto.Detail.ALLOW_CROSS_UID_ACTIVITY_SWITCH_FROM_BELOW,
+ allowCrossUidActivitySwitchFromBelow);
proto.end(detailToken);
}
if (!ArrayUtils.isEmpty(mKnownActivityEmbeddingCerts)) {
@@ -2002,6 +2015,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
nativeHeapZeroInitialized = orig.nativeHeapZeroInitialized;
requestRawExternalStorageAccess = orig.requestRawExternalStorageAccess;
localeConfigRes = orig.localeConfigRes;
+ allowCrossUidActivitySwitchFromBelow = orig.allowCrossUidActivitySwitchFromBelow;
createTimestamp = SystemClock.uptimeMillis();
}
@@ -2106,6 +2120,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
}
}
dest.writeInt(localeConfigRes);
+ dest.writeInt(allowCrossUidActivitySwitchFromBelow ? 1 : 0);
+
sForStringSet.parcel(mKnownActivityEmbeddingCerts, dest, flags);
}
@@ -2204,6 +2220,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
}
}
localeConfigRes = source.readInt();
+ allowCrossUidActivitySwitchFromBelow = source.readInt() != 0;
+
mKnownActivityEmbeddingCerts = sForStringSet.unparcel(source);
if (mKnownActivityEmbeddingCerts.isEmpty()) {
mKnownActivityEmbeddingCerts = null;
diff --git a/core/java/android/content/pm/ArchivedActivityInfo.java b/core/java/android/content/pm/ArchivedActivityInfo.java
index 166d26503625..9f65f5898c25 100644
--- a/core/java/android/content/pm/ArchivedActivityInfo.java
+++ b/core/java/android/content/pm/ArchivedActivityInfo.java
@@ -24,6 +24,7 @@ import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.util.Slog;
import com.android.internal.util.DataClass;
@@ -39,6 +40,7 @@ import java.util.Objects;
@DataClass(genBuilder = false, genConstructor = false, genSetters = true)
@FlaggedApi(Flags.FLAG_ARCHIVING)
public final class ArchivedActivityInfo {
+ private static final String TAG = "ArchivedActivityInfo";
/** The label for the activity. */
private @NonNull CharSequence mLabel;
/** The component name of this activity. */
@@ -138,7 +140,8 @@ public final class ArchivedActivityInfo {
bitmap.getByteCount())) {
bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
return baos.toByteArray();
- } catch (IOException ignored) {
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to compress bitmap", e);
return null;
}
}
@@ -240,10 +243,10 @@ public final class ArchivedActivityInfo {
}
@DataClass.Generated(
- time = 1705615445673L,
+ time = 1708042076897L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/content/pm/ArchivedActivityInfo.java",
- inputSignatures = "private @android.annotation.NonNull java.lang.CharSequence mLabel\nprivate @android.annotation.NonNull android.content.ComponentName mComponentName\nprivate @android.annotation.Nullable android.graphics.drawable.Drawable mIcon\nprivate @android.annotation.Nullable android.graphics.drawable.Drawable mMonochromeIcon\n @android.annotation.NonNull android.content.pm.ArchivedActivityParcel getParcel()\npublic static android.graphics.Bitmap drawableToBitmap(android.graphics.drawable.Drawable)\npublic static android.graphics.Bitmap drawableToBitmap(android.graphics.drawable.Drawable,int)\npublic static byte[] bytesFromBitmap(android.graphics.Bitmap)\nprivate static android.graphics.drawable.Drawable drawableFromCompressedBitmap(byte[])\nclass ArchivedActivityInfo extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=false, genConstructor=false, genSetters=true)")
+ inputSignatures = "private static final java.lang.String TAG\nprivate @android.annotation.NonNull java.lang.CharSequence mLabel\nprivate @android.annotation.NonNull android.content.ComponentName mComponentName\nprivate @android.annotation.Nullable android.graphics.drawable.Drawable mIcon\nprivate @android.annotation.Nullable android.graphics.drawable.Drawable mMonochromeIcon\n @android.annotation.NonNull android.content.pm.ArchivedActivityParcel getParcel()\npublic static android.graphics.Bitmap drawableToBitmap(android.graphics.drawable.Drawable)\npublic static android.graphics.Bitmap drawableToBitmap(android.graphics.drawable.Drawable,int)\npublic static byte[] bytesFromBitmap(android.graphics.Bitmap)\nprivate static android.graphics.drawable.Drawable drawableFromCompressedBitmap(byte[])\nclass ArchivedActivityInfo extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=false, genConstructor=false, genSetters=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index 529363f828bb..8220313a9197 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -19,7 +19,9 @@ import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.app.admin.DevicePolicyResources.Strings.Core.SWITCH_TO_PERSONAL_LABEL;
import static android.app.admin.DevicePolicyResources.Strings.Core.SWITCH_TO_WORK_LABEL;
import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
+import static android.app.admin.flags.Flags.FLAG_ALLOW_QUERYING_PROFILE_TYPE;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -314,6 +316,41 @@ public class CrossProfileApps {
}
}
+
+ /**
+ * Checks if the specified user is a profile, i.e. not the parent user.
+ *
+ * @param userHandle The UserHandle of the target profile, must be one of the users returned by
+ * {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
+ * be thrown.
+ * @return whether the specified user is a profile.
+ */
+ @FlaggedApi(FLAG_ALLOW_QUERYING_PROFILE_TYPE)
+ public boolean isProfile(@NonNull UserHandle userHandle) {
+ // Note that this is not a security check, but rather a check for correct use.
+ // The actual security check is performed by UserManager.
+ verifyCanAccessUser(userHandle);
+
+ return mUserManager.isProfile(userHandle.getIdentifier());
+ }
+
+ /**
+ * Checks if the specified user is a managed profile.
+ *
+ * @param userHandle The UserHandle of the target profile, must be one of the users returned by
+ * {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
+ * be thrown.
+ * @return whether the specified user is a managed profile.
+ */
+ @FlaggedApi(FLAG_ALLOW_QUERYING_PROFILE_TYPE)
+ public boolean isManagedProfile(@NonNull UserHandle userHandle) {
+ // Note that this is not a security check, but rather a check for correct use.
+ // The actual security check is performed by UserManager.
+ verifyCanAccessUser(userHandle);
+
+ return mUserManager.isManagedProfile(userHandle.getIdentifier());
+ }
+
/**
* Return a label that calling app can show to user for the semantic of profile switching --
* launching its own activity in specified user profile. For example, it may return
@@ -677,6 +714,11 @@ public class CrossProfileApps {
}
}
+ /**
+ * A validation method to check that the methods in this class are only being applied to user
+ * handles returned by {@link #getTargetUserProfiles()}. As this is run client-side for
+ * input validation purposes, this should never replace a real security check service-side.
+ */
private void verifyCanAccessUser(UserHandle userHandle) {
if (!getTargetUserProfiles().contains(userHandle)) {
throw new SecurityException("Not allowed to access " + userHandle);
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index 62db65f15df3..cec49c710d92 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -130,4 +130,6 @@ interface ILauncherApps {
void unRegisterDumpCallback(IDumpCallback cb);
void setArchiveCompatibilityOptions(boolean enableIconOverlay, boolean enableUnarchivalConfirmation);
+
+ List<UserHandle> getUserProfiles();
}
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 7c264f65d471..e437925693af 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -17,6 +17,8 @@
package android.content.pm;
import static android.Manifest.permission;
+import static android.Manifest.permission.ACCESS_HIDDEN_PROFILES;
+import static android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL;
import static android.Manifest.permission.READ_FRAME_BUFFER;
import android.annotation.CallbackExecutor;
@@ -693,12 +695,23 @@ public class LauncherApps {
* Otherwise it'll return the same list as {@link UserManager#getUserProfiles()} would.
*/
public List<UserHandle> getProfiles() {
- if (mUserManager.isManagedProfile()) {
- // If it's a managed profile, only return the current profile.
- final List result = new ArrayList(1);
+ if (mUserManager.isManagedProfile()
+ || (android.multiuser.Flags.enableLauncherAppsHiddenProfileChecks()
+ && android.os.Flags.allowPrivateProfile()
+ && mUserManager.isPrivateProfile())) {
+ // If it's a managed or private profile, only return the current profile.
+ final List result = new ArrayList(1);
result.add(android.os.Process.myUserHandle());
return result;
} else {
+ if (android.multiuser.Flags.enableLauncherAppsHiddenProfileChecks()) {
+ try {
+ return mService.getUserProfiles();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
return mUserManager.getUserProfiles();
}
}
@@ -779,15 +792,20 @@ public class LauncherApps {
/**
* Returns information related to a user which is useful for displaying UI elements
- * to distinguish it from other users (eg, badges). Only system launchers should
- * call this API.
+ * to distinguish it from other users (eg, badges).
*
- * @param userHandle user handle of the user for which LauncherUserInfo is requested
- * @return the LauncherUserInfo object related to the user specified.
- * @hide
+ * <p>If the user in question is a hidden profile like
+ * {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, caller should have
+ * {@link android.app.role.RoleManager.ROLE_HOME} and either of the permissions required.
+ *
+ * @param userHandle user handle of the user for which LauncherUserInfo is requested.
+ * @return the {@link LauncherUserInfo} object related to the user specified, null in case
+ * the user is inaccessible.
*/
@Nullable
@FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE)
+ @RequiresPermission(conditional = true,
+ anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
public final LauncherUserInfo getLauncherUserInfo(@NonNull UserHandle userHandle) {
if (DEBUG) {
Log.i(TAG, "getLauncherUserInfo " + userHandle);
@@ -823,17 +841,20 @@ public class LauncherApps {
* </ul>
* </p>
*
- *
+ * <p>If the user in question is a hidden profile
+ * {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, caller should have
+ * {@link android.app.role.RoleManager.ROLE_HOME} and either of the permissions required.
*
* @param packageName the package for which intent sender to launch App Market Activity is
* required.
* @param user the profile for which intent sender to launch App Market Activity is required.
* @return {@link IntentSender} object which launches the App Market Activity, null in case
* there is no such activity.
- * @hide
*/
@Nullable
@FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE)
+ @RequiresPermission(conditional = true,
+ anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
public IntentSender getAppMarketActivityIntent(@Nullable String packageName,
@NonNull UserHandle user) {
if (DEBUG) {
@@ -851,15 +872,21 @@ public class LauncherApps {
/**
* Returns the list of the system packages that are installed at user creation.
*
- * <p>An empty list denotes that all system packages are installed for that user at creation.
- * This behaviour is inherited from the underlining UserManager API.
+ * <p>An empty list denotes that all system packages should be treated as pre-installed for that
+ * user at creation.
+ *
+ * <p>If the user in question is a hidden profile like
+ * {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, caller should have
+ * {@link android.app.role.RoleManager.ROLE_HOME} and either of the permissions required.
*
* @param userHandle the user for which installed system packages are required.
* @return {@link List} of {@link String}, representing the package name of the installed
* package. Can be empty but not null.
- * @hide
*/
@FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE)
+ @NonNull
+ @RequiresPermission(conditional = true,
+ anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
public List<String> getPreInstalledSystemPackages(@NonNull UserHandle userHandle) {
if (DEBUG) {
Log.i(TAG, "getPreInstalledSystemPackages for user: " + userHandle);
diff --git a/core/java/android/content/pm/LauncherUserInfo.java b/core/java/android/content/pm/LauncherUserInfo.java
index 214c3e48db71..8426f54d4754 100644
--- a/core/java/android/content/pm/LauncherUserInfo.java
+++ b/core/java/android/content/pm/LauncherUserInfo.java
@@ -27,8 +27,6 @@ import android.os.UserManager;
/**
* The LauncherUserInfo object holds information about an Android user that is required to display
* the Launcher related UI elements specific to the user (like badges).
- *
- * @hide
*/
@FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE)
public final class LauncherUserInfo implements Parcelable {
@@ -41,11 +39,9 @@ public final class LauncherUserInfo implements Parcelable {
/**
* Returns type of the user as defined in {@link UserManager}. e.g.,
* {@link UserManager.USER_TYPE_PROFILE_MANAGED} or {@link UserManager.USER_TYPE_PROFILE_ClONE}
- * TODO(b/303812736): Make the return type public and update javadoc here once the linked bug
- * is resolved.
+ * or {@link UserManager.USER_TYPE_PROFILE_PRIVATE}
*
* @return the userType for the user whose LauncherUserInfo this is
- * @hide
*/
@FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE)
@NonNull
@@ -58,7 +54,6 @@ public final class LauncherUserInfo implements Parcelable {
* {@link UserManager#getSerialNumberForUser(UserHandle)}
*
* @return the serial number associated with the user
- * @hide
*/
@FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE)
public int getUserSerialNumber() {
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 49c8a7cad57a..7a015cd187ca 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2554,6 +2554,15 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_SHARED_LIBRARY_BAD_CERTIFICATE_DIGEST = -130;
/**
+ * Installation failed return code: if the system failed to install the package that
+ * {@link android.R.attr#multiArch} is true in its manifest because its packaged
+ * native code did not match all of the natively ABIs supported by the system.
+ *
+ * @hide
+ */
+ public static final int INSTALL_FAILED_MULTI_ARCH_NOT_MATCH_ALL_NATIVE_ABIS = -131;
+
+ /**
* App minimum aspect ratio set by the user which will override app-defined aspect ratio.
*
* @hide
@@ -3965,7 +3974,9 @@ public abstract class PackageManager {
* The device is capable of communicating with other devices via
* <a href="https://www.threadgroup.org">Thread</a> networking protocol.
*/
- @FlaggedApi("com.android.net.thread.flags.thread_enabled")
+ // TODO (b/325886480): update the flag to
+ // "com.android.net.thread.platform.flags.Flags.FLAG_THREAD_ENABLED_PLATFORM"
+ @FlaggedApi("com.android.net.thread.flags.thread_enabled_platform")
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_THREAD_NETWORK = "android.hardware.thread_network";
@@ -10452,6 +10463,8 @@ public abstract class PackageManager {
case INSTALL_FAILED_SESSION_INVALID: return "INSTALL_FAILED_SESSION_INVALID";
case INSTALL_FAILED_SHARED_LIBRARY_BAD_CERTIFICATE_DIGEST:
return "INSTALL_FAILED_SHARED_LIBRARY_BAD_CERTIFICATE_DIGEST";
+ case INSTALL_FAILED_MULTI_ARCH_NOT_MATCH_ALL_NATIVE_ABIS:
+ return "INSTALL_FAILED_MULTI_ARCH_NOT_MATCH_ALL_NATIVE_ABIS";
default: return Integer.toString(status);
}
}
diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java
index 087a7952acd7..55d0bc1deedc 100644
--- a/core/java/android/content/pm/ShortcutServiceInternal.java
+++ b/core/java/android/content/pm/ShortcutServiceInternal.java
@@ -115,6 +115,11 @@ public abstract class ShortcutServiceInternal {
public abstract boolean hasShortcutHostPermission(int launcherUserId,
@NonNull String callingPackage, int callingPid, int callingUid);
+ /**
+ * Returns whether or not Shortcuts are supported on Launcher Home Screen.
+ */
+ public abstract boolean areShortcutsSupportedOnHomeScreen(@UserIdInt int userId);
+
public abstract void setShortcutHostPackage(@NonNull String type, @Nullable String packageName,
int userId);
diff --git a/core/java/android/content/pm/SignedPackage.java b/core/java/android/content/pm/SignedPackage.java
index 4d1b136915f2..7bffa7722142 100644
--- a/core/java/android/content/pm/SignedPackage.java
+++ b/core/java/android/content/pm/SignedPackage.java
@@ -35,9 +35,9 @@ public class SignedPackage {
private final SignedPackageParcel mData;
/** @hide */
- public SignedPackage(@NonNull String pkgName, @NonNull byte[] certificateDigest) {
+ public SignedPackage(@NonNull String packageName, @NonNull byte[] certificateDigest) {
SignedPackageParcel data = new SignedPackageParcel();
- data.pkgName = pkgName;
+ data.packageName = packageName;
data.certificateDigest = certificateDigest;
mData = data;
}
@@ -52,8 +52,8 @@ public class SignedPackage {
return mData;
}
- public @NonNull String getPkgName() {
- return mData.pkgName;
+ public @NonNull String getPackageName() {
+ return mData.packageName;
}
public @NonNull byte[] getCertificateDigest() {
@@ -64,12 +64,12 @@ public class SignedPackage {
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof SignedPackage that)) return false;
- return mData.pkgName.equals(that.mData.pkgName) && Arrays.equals(mData.certificateDigest,
- that.mData.certificateDigest);
+ return mData.packageName.equals(that.mData.packageName) && Arrays.equals(
+ mData.certificateDigest, that.mData.certificateDigest);
}
@Override
public int hashCode() {
- return Objects.hash(mData.pkgName, Arrays.hashCode(mData.certificateDigest));
+ return Objects.hash(mData.packageName, Arrays.hashCode(mData.certificateDigest));
}
}
diff --git a/core/java/android/content/pm/SignedPackageParcel.aidl b/core/java/android/content/pm/SignedPackageParcel.aidl
index 7957f7f99d4a..bb4dc8654108 100644
--- a/core/java/android/content/pm/SignedPackageParcel.aidl
+++ b/core/java/android/content/pm/SignedPackageParcel.aidl
@@ -20,6 +20,6 @@ import android.content.ComponentName;
/** @hide */
parcelable SignedPackageParcel {
- String pkgName;
+ String packageName;
byte[] certificateDigest;
}
diff --git a/core/java/android/content/pm/UserProperties.java b/core/java/android/content/pm/UserProperties.java
index d347a0e8ae63..915992904a5c 100644
--- a/core/java/android/content/pm/UserProperties.java
+++ b/core/java/android/content/pm/UserProperties.java
@@ -537,7 +537,6 @@ public final class UserProperties implements Parcelable {
setDeleteAppWithParent(orig.getDeleteAppWithParent());
setAlwaysVisible(orig.getAlwaysVisible());
setAllowStoppingUserWithDelayedLocking(orig.getAllowStoppingUserWithDelayedLocking());
- setItemsRestrictedOnHomeScreen(orig.areItemsRestrictedOnHomeScreen());
}
if (hasManagePermission) {
// Add items that require MANAGE_USERS or stronger.
@@ -557,6 +556,7 @@ public final class UserProperties implements Parcelable {
setShowInSharingSurfaces(orig.getShowInSharingSurfaces());
setCrossProfileContentSharingStrategy(orig.getCrossProfileContentSharingStrategy());
setProfileApiVisibility(orig.getProfileApiVisibility());
+ setItemsRestrictedOnHomeScreen(orig.areItemsRestrictedOnHomeScreen());
}
/**
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index c7d93bfbb83d..22e233aebf36 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -139,6 +139,41 @@ flag {
flag {
name: "enable_launcher_apps_hidden_profile_checks"
namespace: "profile_experiences"
- description: "Enable extra check to limit access to hidden prfiles data in Launcher apps APIs."
+ description: "Enable extra check to limit access to hidden profiles data in Launcher apps APIs."
bug: "321988638"
}
+
+flag {
+ name: "show_set_screen_lock_dialog"
+ namespace: "profile_experiences"
+ description: "Display the dialog to set up screen lock when private space unlock operation is requested"
+ bug: "316129700"
+}
+
+flag {
+ name: "reorder_wallpaper_during_user_switch"
+ namespace: "multiuser"
+ description: "Reorder loading home and lock screen wallpapers during a user switch."
+ bug: "324911115"
+}
+
+flag {
+ name: "set_power_mode_during_user_switch"
+ namespace: "multiuser"
+ description: "Set power mode during a user switch."
+ bug: "325249845"
+}
+
+flag {
+ name: "disable_private_space_items_on_home"
+ namespace: "profile_experiences"
+ description: "Disables adding items belonging to Private Space on Home Screen manually as well as automatically"
+ bug: "287975131"
+}
+
+flag {
+ name: "enable_ps_sensitive_notifications_toggle"
+ namespace: "profile_experiences"
+ description: "Enable the sensitive notifications toggle to be visible in the Private space settings page"
+ bug: "317067050"
+}
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 23b9d0b7c9a7..d259e9755a41 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -137,6 +137,8 @@ public final class AssetManager implements AutoCloseable {
private ArrayList<ApkAssets> mUserApkAssets = new ArrayList<>();
private ArrayList<ResourcesLoader> mLoaders = new ArrayList<>();
+ private boolean mNoInit = false;
+
public Builder addApkAssets(ApkAssets apkAssets) {
mUserApkAssets.add(apkAssets);
return this;
@@ -147,6 +149,11 @@ public final class AssetManager implements AutoCloseable {
return this;
}
+ public Builder setNoInit() {
+ mNoInit = true;
+ return this;
+ }
+
public AssetManager build() {
// Retrieving the system ApkAssets forces their creation as well.
final ApkAssets[] systemApkAssets = getSystem().getApkAssets();
@@ -188,7 +195,7 @@ public final class AssetManager implements AutoCloseable {
final AssetManager assetManager = new AssetManager(false /*sentinel*/);
assetManager.mApkAssets = apkAssets;
AssetManager.nativeSetApkAssets(assetManager.mObject, apkAssets,
- false /*invalidateCaches*/);
+ false /*invalidateCaches*/, mNoInit /*preset*/);
assetManager.mLoaders = mLoaders.isEmpty() ? null
: mLoaders.toArray(new ResourcesLoader[0]);
@@ -329,7 +336,7 @@ public final class AssetManager implements AutoCloseable {
synchronized (this) {
ensureOpenLocked();
mApkAssets = newApkAssets;
- nativeSetApkAssets(mObject, mApkAssets, invalidateCaches);
+ nativeSetApkAssets(mObject, mApkAssets, invalidateCaches, false);
if (invalidateCaches) {
// Invalidate all caches.
invalidateCachesLocked(-1);
@@ -496,7 +503,7 @@ public final class AssetManager implements AutoCloseable {
mApkAssets = Arrays.copyOf(mApkAssets, count + 1);
mApkAssets[count] = assets;
- nativeSetApkAssets(mObject, mApkAssets, true);
+ nativeSetApkAssets(mObject, mApkAssets, true, false);
invalidateCachesLocked(-1);
return count + 1;
}
@@ -1503,12 +1510,29 @@ public final class AssetManager implements AutoCloseable {
int navigation, int screenWidth, int screenHeight, int smallestScreenWidthDp,
int screenWidthDp, int screenHeightDp, int screenLayout, int uiMode, int colorMode,
int grammaticalGender, int majorVersion) {
+ setConfigurationInternal(mcc, mnc, defaultLocale, locales, orientation,
+ touchscreen, density, keyboard, keyboardHidden, navigation, screenWidth,
+ screenHeight, smallestScreenWidthDp, screenWidthDp, screenHeightDp,
+ screenLayout, uiMode, colorMode, grammaticalGender, majorVersion, false);
+ }
+
+ /**
+ * Change the configuration used when retrieving resources, and potentially force a refresh of
+ * the state. Not for use by applications.
+ * @hide
+ */
+ void setConfigurationInternal(int mcc, int mnc, String defaultLocale, String[] locales,
+ int orientation, int touchscreen, int density, int keyboard, int keyboardHidden,
+ int navigation, int screenWidth, int screenHeight, int smallestScreenWidthDp,
+ int screenWidthDp, int screenHeightDp, int screenLayout, int uiMode, int colorMode,
+ int grammaticalGender, int majorVersion, boolean forceRefresh) {
synchronized (this) {
ensureValidLocked();
nativeSetConfiguration(mObject, mcc, mnc, defaultLocale, locales, orientation,
touchscreen, density, keyboard, keyboardHidden, navigation, screenWidth,
screenHeight, smallestScreenWidthDp, screenWidthDp, screenHeightDp,
- screenLayout, uiMode, colorMode, grammaticalGender, majorVersion);
+ screenLayout, uiMode, colorMode, grammaticalGender, majorVersion,
+ forceRefresh);
}
}
@@ -1593,13 +1617,13 @@ public final class AssetManager implements AutoCloseable {
private static native long nativeCreate();
private static native void nativeDestroy(long ptr);
private static native void nativeSetApkAssets(long ptr, @NonNull ApkAssets[] apkAssets,
- boolean invalidateCaches);
+ boolean invalidateCaches, boolean preset);
private static native void nativeSetConfiguration(long ptr, int mcc, int mnc,
@Nullable String defaultLocale, @NonNull String[] locales, int orientation,
int touchscreen, int density, int keyboard, int keyboardHidden, int navigation,
int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp,
int screenHeightDp, int screenLayout, int uiMode, int colorMode, int grammaticalGender,
- int majorVersion);
+ int majorVersion, boolean forceRefresh);
private static native @NonNull SparseArray<String> nativeGetAssignedPackageIdentifiers(
long ptr, boolean includeOverlays, boolean includeLoaders);
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 5e442b819774..079c2c1ab7c9 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -200,7 +200,7 @@ public class ResourcesImpl {
mMetrics.setToDefaults();
mDisplayAdjustments = displayAdjustments;
mConfiguration.setToDefaults();
- updateConfiguration(config, metrics, displayAdjustments.getCompatibilityInfo());
+ updateConfigurationImpl(config, metrics, displayAdjustments.getCompatibilityInfo(), true);
}
public DisplayAdjustments getDisplayAdjustments() {
@@ -402,7 +402,12 @@ public class ResourcesImpl {
}
public void updateConfiguration(Configuration config, DisplayMetrics metrics,
- CompatibilityInfo compat) {
+ CompatibilityInfo compat) {
+ updateConfigurationImpl(config, metrics, compat, false);
+ }
+
+ private void updateConfigurationImpl(Configuration config, DisplayMetrics metrics,
+ CompatibilityInfo compat, boolean forceAssetsRefresh) {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ResourcesImpl#updateConfiguration");
try {
synchronized (mAccessLock) {
@@ -528,7 +533,7 @@ public class ResourcesImpl {
keyboardHidden = mConfiguration.keyboardHidden;
}
- mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
+ mAssets.setConfigurationInternal(mConfiguration.mcc, mConfiguration.mnc,
defaultLocale,
selectedLocales,
mConfiguration.orientation,
@@ -539,7 +544,7 @@ public class ResourcesImpl {
mConfiguration.screenWidthDp, mConfiguration.screenHeightDp,
mConfiguration.screenLayout, mConfiguration.uiMode,
mConfiguration.colorMode, mConfiguration.getGrammaticalGender(),
- Build.VERSION.RESOURCES_SDK_INT);
+ Build.VERSION.RESOURCES_SDK_INT, forceAssetsRefresh);
if (DEBUG_CONFIG) {
Slog.i(TAG, "**** Updating config of " + this + ": final config is "
diff --git a/core/java/android/credentials/GetCredentialResponse.java b/core/java/android/credentials/GetCredentialResponse.java
index ea699b9a74e5..a1477ee1a43a 100644
--- a/core/java/android/credentials/GetCredentialResponse.java
+++ b/core/java/android/credentials/GetCredentialResponse.java
@@ -63,9 +63,6 @@ public final class GetCredentialResponse implements Parcelable {
}
/**
- *
- * @return
- *
* @hide
*/
public AutofillId getAutofillId() {
diff --git a/core/java/android/credentials/flags.aconfig b/core/java/android/credentials/flags.aconfig
index ef7b2594fd5d..09e59d315428 100644
--- a/core/java/android/credentials/flags.aconfig
+++ b/core/java/android/credentials/flags.aconfig
@@ -68,4 +68,18 @@ flag {
name: "new_framework_metrics"
description: "Enables new metrics fror 24Q3 / VIC"
bug: "324291187"
-} \ No newline at end of file
+}
+
+flag {
+ namespace: "credential_manager"
+ name: "clear_credentials_api_fix_enabled"
+ description: "Fixes bug in clearCredential API that causes indefinite suspension"
+ bug: "314926460"
+}
+
+flag {
+ namespace: "credential_manager"
+ name: "hybrid_filter_fix_enabled"
+ description: "Removes capability check from hybrid implementation"
+ bug: "323923403"
+}
diff --git a/core/java/android/credentials/selection/Constants.java b/core/java/android/credentials/selection/Constants.java
index f7fec237dd08..2229f258a3a4 100644
--- a/core/java/android/credentials/selection/Constants.java
+++ b/core/java/android/credentials/selection/Constants.java
@@ -30,13 +30,6 @@ public class Constants {
"android.credentials.selection.extra.RESULT_RECEIVER";
/**
- * The intent extra key for indicating whether the bottom sheet should be started directly
- * on the 'All Options' screen.
- */
- public static final String EXTRA_REQ_FOR_ALL_OPTIONS =
- "android.credentials.selection.extra.REQ_FOR_ALL_OPTIONS";
-
- /**
* The intent extra key for the final result receiver object
*/
public static final String EXTRA_FINAL_RESPONSE_RECEIVER =
diff --git a/core/java/android/credentials/selection/IntentFactory.java b/core/java/android/credentials/selection/IntentFactory.java
index ac2bae495219..4b0fa6d32af7 100644
--- a/core/java/android/credentials/selection/IntentFactory.java
+++ b/core/java/android/credentials/selection/IntentFactory.java
@@ -48,41 +48,28 @@ import java.util.ArrayList;
public class IntentFactory {
/**
- * Generate a new launch intent to the Credential Selector UI for auto-filling.
+ * Generate a new launch intent to the Credential Selector UI for auto-filling. This intent is
+ * invoked from the Autofill flow, when the user requests to bring up the 'All Options' page of
+ * the credential bottom-sheet. When the user clicks on the pinned entry, the intent will bring
+ * up the 'All Options' page of the bottom-sheet. The provider data list is processed by the
+ * credential autofill service for each autofill id and passed in as an auth extra.
*
* @hide
*/
@NonNull
- // TODO(b/323552850) - clean up method overloads
- public static Intent createCredentialSelectorIntent(
+ public static Intent createCredentialSelectorIntentForAutofill(
@NonNull Context context,
@NonNull RequestInfo requestInfo,
@SuppressLint("ConcreteCollection") // Concrete collection needed for marshalling.
- @Nullable
- ArrayList<ProviderData> enabledProviderDataList,
- @SuppressLint("ConcreteCollection") // Concrete collection needed for marshalling.
@NonNull
ArrayList<DisabledProviderData> disabledProviderDataList,
- @NonNull ResultReceiver resultReceiver,
- boolean isRequestForAllOptions) {
-
- Intent intent;
- if (enabledProviderDataList != null) {
- intent = createCredentialSelectorIntent(context, requestInfo, enabledProviderDataList,
- disabledProviderDataList, resultReceiver);
- } else {
- intent = createCredentialSelectorIntent(context, requestInfo,
- disabledProviderDataList, resultReceiver);
- }
- intent.putExtra(Constants.EXTRA_REQ_FOR_ALL_OPTIONS, isRequestForAllOptions);
-
- return intent;
+ @NonNull ResultReceiver resultReceiver) {
+ return createCredentialSelectorIntent(context, requestInfo,
+ disabledProviderDataList, resultReceiver);
}
/**
* Generate a new launch intent to the Credential Selector UI.
- *
- * @hide
*/
@NonNull
private static Intent createCredentialSelectorIntent(
diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java
index 870546a6fd2b..ba356bb55194 100644
--- a/core/java/android/database/CursorWindow.java
+++ b/core/java/android/database/CursorWindow.java
@@ -40,7 +40,7 @@ import dalvik.system.CloseGuard;
*/
@android.ravenwood.annotation.RavenwoodKeepWholeClass
@android.ravenwood.annotation.RavenwoodNativeSubstitutionClass(
- "com.android.hoststubgen.nativesubstitution.CursorWindow_host")
+ "com.android.platform.test.ravenwood.nativesubstitution.CursorWindow_host")
public class CursorWindow extends SQLiteClosable implements Parcelable {
private static final String STATS_TAG = "CursorWindowStats";
diff --git a/core/java/android/hardware/CameraPrivacyAllowlistEntry.aidl b/core/java/android/hardware/CameraPrivacyAllowlistEntry.aidl
new file mode 100644
index 000000000000..838e41ee1c08
--- /dev/null
+++ b/core/java/android/hardware/CameraPrivacyAllowlistEntry.aidl
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2024, 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.hardware;
+
+/** @hide */
+parcelable CameraPrivacyAllowlistEntry {
+ String packageName;
+ boolean isMandatory;
+}
+
diff --git a/core/java/android/hardware/ISensorPrivacyListener.aidl b/core/java/android/hardware/ISensorPrivacyListener.aidl
index 2ac21d2615aa..19ae302355be 100644
--- a/core/java/android/hardware/ISensorPrivacyListener.aidl
+++ b/core/java/android/hardware/ISensorPrivacyListener.aidl
@@ -25,5 +25,6 @@ oneway interface ISensorPrivacyListener {
// frameworks/native/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl
// =============== Beginning of transactions used on native side as well ======================
void onSensorPrivacyChanged(int toggleType, int sensor, boolean enabled);
+ void onSensorPrivacyStateChanged(int toggleType, int sensor, int state);
// =============== End of transactions used on native side as well ============================
}
diff --git a/core/java/android/hardware/ISensorPrivacyManager.aidl b/core/java/android/hardware/ISensorPrivacyManager.aidl
index 9cf329ca3d3d..851ce2add94f 100644
--- a/core/java/android/hardware/ISensorPrivacyManager.aidl
+++ b/core/java/android/hardware/ISensorPrivacyManager.aidl
@@ -16,6 +16,7 @@
package android.hardware;
+import android.hardware.CameraPrivacyAllowlistEntry;
import android.hardware.ISensorPrivacyListener;
/** @hide */
@@ -45,6 +46,22 @@ interface ISensorPrivacyManager {
void setToggleSensorPrivacy(int userId, int source, int sensor, boolean enable);
void setToggleSensorPrivacyForProfileGroup(int userId, int source, int sensor, boolean enable);
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)")
+ List<CameraPrivacyAllowlistEntry> getCameraPrivacyAllowlist();
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)")
+ int getToggleSensorPrivacyState(int toggleType, int sensor);
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)")
+ void setToggleSensorPrivacyState(int userId, int source, int sensor, int state);
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY)")
+ void setToggleSensorPrivacyStateForProfileGroup(int userId, int source, int sensor, int state);
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY)")
+ boolean isCameraPrivacyEnabled(String packageName);
+
// =============== End of transactions used on native side as well ============================
void suppressToggleSensorPrivacyReminders(int userId, int sensor, IBinder token,
@@ -53,4 +70,4 @@ interface ISensorPrivacyManager {
boolean requiresAuthentication();
void showSensorUseDialog(int sensor);
-} \ No newline at end of file
+}
diff --git a/core/java/android/hardware/OverlayProperties.java b/core/java/android/hardware/OverlayProperties.java
index 72586b2b22cb..88089eed50f4 100644
--- a/core/java/android/hardware/OverlayProperties.java
+++ b/core/java/android/hardware/OverlayProperties.java
@@ -71,17 +71,36 @@ public final class OverlayProperties implements Parcelable {
/**
* @return True if the device can support fp16, false otherwise.
+ * TODO: Move this to isCombinationSupported once the flag flips
* @hide
*/
public boolean isFp16SupportedForHdr() {
if (mNativeObject == 0) {
return false;
}
- return nSupportFp16ForHdr(mNativeObject);
+ return nIsCombinationSupported(
+ mNativeObject, DataSpace.DATASPACE_SCRGB, HardwareBuffer.RGBA_FP16);
}
/**
- * Indicates that hw composition of two or more overlays
+ * Indicates that hardware composition of a buffer encoded with the provided {@link DataSpace}
+ * and {@link HardwareBuffer.Format} is supported on the device.
+ *
+ * @return True if the device can support efficiently compositing the content described by the
+ * dataspace and format. False if GPOU composition fallback is otherwise required.
+ */
+ @FlaggedApi(Flags.FLAG_OVERLAYPROPERTIES_CLASS_API)
+ public boolean isCombinationSupported(@DataSpace.ColorDataSpace int dataspace,
+ @HardwareBuffer.Format int format) {
+ if (mNativeObject == 0) {
+ return false;
+ }
+
+ return nIsCombinationSupported(mNativeObject, dataspace, format);
+ }
+
+ /**
+ * Indicates that hardware composition of two or more overlays
* with different colorspaces is supported on the device.
*
* @return True if the device can support mixed colorspaces efficiently,
@@ -131,6 +150,8 @@ public final class OverlayProperties implements Parcelable {
private static native long nCreateDefault();
private static native boolean nSupportFp16ForHdr(long nativeObject);
private static native boolean nSupportMixedColorSpaces(long nativeObject);
+ private static native boolean nIsCombinationSupported(
+ long nativeObject, int dataspace, int format);
private static native void nWriteOverlayPropertiesToParcel(long nativeObject, Parcel dest);
private static native long nReadOverlayPropertiesFromParcel(Parcel in);
}
diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java
index 18c95bfbb297..6294a8d617de 100644
--- a/core/java/android/hardware/SensorPrivacyManager.java
+++ b/core/java/android/hardware/SensorPrivacyManager.java
@@ -17,6 +17,7 @@
package android.hardware;
import android.Manifest;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
@@ -38,9 +39,11 @@ import android.util.Log;
import android.util.Pair;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.camera.flags.Flags;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -215,13 +218,41 @@ public final class SensorPrivacyManager {
public static final int DISABLED = SensorPrivacyIndividualEnabledSensorProto.DISABLED;
/**
+ * Constant indicating privacy is enabled except for the automotive driver assistance apps
+ * which are helpful for driving.
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
+ public static final int AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS =
+ SensorPrivacyIndividualEnabledSensorProto.AUTO_DRIVER_ASSISTANCE_HELPFUL_APPS;
+
+ /**
+ * Constant indicating privacy is enabled except for the automotive driver assistance apps
+ * which are required by car manufacturer for driving.
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
+ public static final int AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS =
+ SensorPrivacyIndividualEnabledSensorProto.AUTO_DRIVER_ASSISTANCE_REQUIRED_APPS;
+
+ /**
+ * Constant indicating privacy is enabled except for the automotive driver assistance apps
+ * which are both helpful for driving and also apps required by car manufacturer for
+ * driving.
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
+ public static final int AUTOMOTIVE_DRIVER_ASSISTANCE_APPS =
+ SensorPrivacyIndividualEnabledSensorProto.AUTO_DRIVER_ASSISTANCE_APPS;
+
+ /**
* Types of state which can exist for a sensor privacy toggle
*
* @hide
*/
@IntDef(value = {
ENABLED,
- DISABLED
+ DISABLED,
+ AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS,
+ AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS,
+ AUTOMOTIVE_DRIVER_ASSISTANCE_APPS
})
@Retention(RetentionPolicy.SOURCE)
public @interface StateType {}
@@ -266,6 +297,19 @@ public final class SensorPrivacyManager {
private int mToggleType;
private int mSensor;
private boolean mEnabled;
+ private int mState;
+
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
+ private SensorPrivacyChangedParams(int toggleType, int sensor, int state) {
+ mToggleType = toggleType;
+ mSensor = sensor;
+ mState = state;
+ if (state == StateTypes.ENABLED) {
+ mEnabled = true;
+ } else {
+ mEnabled = false;
+ }
+ }
private SensorPrivacyChangedParams(int toggleType, int sensor, boolean enabled) {
mToggleType = toggleType;
@@ -284,6 +328,12 @@ public final class SensorPrivacyManager {
public boolean isEnabled() {
return mEnabled;
}
+
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
+ public @StateTypes.StateType int getState() {
+ return mState;
+ }
+
}
}
@@ -319,6 +369,9 @@ public final class SensorPrivacyManager {
private final ArrayMap<Pair<Integer, OnSensorPrivacyChangedListener>,
OnSensorPrivacyChangedListener> mLegacyToggleListeners = new ArrayMap<>();
+ @GuardedBy("mLock")
+ private ArrayMap<String, Boolean> mCameraPrivacyAllowlist = null;
+
/** The singleton ISensorPrivacyListener for IPC which will be used to dispatch to local
* listeners */
@NonNull
@@ -328,12 +381,33 @@ public final class SensorPrivacyManager {
synchronized (mLock) {
for (int i = 0; i < mToggleListeners.size(); i++) {
OnSensorPrivacyChangedListener listener = mToggleListeners.keyAt(i);
+ if (Flags.cameraPrivacyAllowlist()) {
+ int state = enabled ? StateTypes.ENABLED : StateTypes.DISABLED;
+ mToggleListeners.valueAt(i).execute(() -> listener
+ .onSensorPrivacyChanged(new OnSensorPrivacyChangedListener
+ .SensorPrivacyChangedParams(toggleType, sensor, state)));
+ } else {
+ mToggleListeners.valueAt(i).execute(() -> listener
+ .onSensorPrivacyChanged(new OnSensorPrivacyChangedListener
+ .SensorPrivacyChangedParams(toggleType, sensor, enabled)));
+ }
+ }
+ }
+ }
+
+ @Override
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
+ public void onSensorPrivacyStateChanged(int toggleType, int sensor, int state) {
+ synchronized (mLock) {
+ for (int i = 0; i < mToggleListeners.size(); i++) {
+ OnSensorPrivacyChangedListener listener = mToggleListeners.keyAt(i);
mToggleListeners.valueAt(i).execute(() -> listener
.onSensorPrivacyChanged(new OnSensorPrivacyChangedListener
- .SensorPrivacyChangedParams(toggleType, sensor, enabled)));
+ .SensorPrivacyChangedParams(toggleType, sensor, state)));
}
}
}
+
};
/** Whether the singleton ISensorPrivacyListener has been registered */
@@ -649,6 +723,73 @@ public final class SensorPrivacyManager {
}
/**
+ * Returns sensor privacy state for a specific sensor.
+ *
+ * @return int sensor privacy state.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
+ public @StateTypes.StateType int getSensorPrivacyState(@ToggleType int toggleType,
+ @Sensors.Sensor int sensor) {
+ try {
+ return mService.getToggleSensorPrivacyState(toggleType, sensor);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns if camera privacy is enabled for a specific package.
+ *
+ * @return boolean sensor privacy state.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
+ public boolean isCameraPrivacyEnabled(@NonNull String packageName) {
+ try {
+ return mService.isCameraPrivacyEnabled(packageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns camera privacy allowlist.
+ *
+ * @return List of automotive driver assistance packages for
+ * privacy allowlisting. The returned map includes the package
+ * name as key and the value is a Boolean which tells if that package
+ * is required by the car manufacturer as mandatory package for driving.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
+ public @NonNull Map<String, Boolean> getCameraPrivacyAllowlist() {
+ synchronized (mLock) {
+ if (mCameraPrivacyAllowlist == null) {
+ mCameraPrivacyAllowlist = new ArrayMap<>();
+ try {
+ for (CameraPrivacyAllowlistEntry entry :
+ mService.getCameraPrivacyAllowlist()) {
+ mCameraPrivacyAllowlist.put(entry.packageName, entry.isMandatory);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return mCameraPrivacyAllowlist;
+ }
+ }
+
+ /**
* Sets sensor privacy to the specified state for an individual sensor.
*
* @param sensor the sensor which to change the state for
@@ -677,6 +818,22 @@ public final class SensorPrivacyManager {
* Sets sensor privacy to the specified state for an individual sensor.
*
* @param sensor the sensor which to change the state for
+ * @param state the state to which sensor privacy should be set.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
+ public void setSensorPrivacyState(@Sensors.Sensor int sensor,
+ @StateTypes.StateType int state) {
+ setSensorPrivacyState(resolveSourceFromCurrentContext(), sensor, state);
+ }
+
+ /**
+ * Sets sensor privacy to the specified state for an individual sensor.
+ *
+ * @param sensor the sensor which to change the state for
* @param enable the state to which sensor privacy should be set.
*
* @hide
@@ -708,6 +865,27 @@ public final class SensorPrivacyManager {
}
/**
+ * Sets sensor privacy to the specified state for an individual sensor.
+ *
+ * @param sensor the sensor which to change the state for
+ * @param state the state to which sensor privacy should be set.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
+ public void setSensorPrivacyState(@Sources.Source int source, @Sensors.Sensor int sensor,
+ @StateTypes.StateType int state) {
+ try {
+ mService.setToggleSensorPrivacyState(mContext.getUserId(), source, sensor, state);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ }
+
+ /**
* Sets sensor privacy to the specified state for an individual sensor for the profile group of
* context's user.
*
@@ -745,6 +923,28 @@ public final class SensorPrivacyManager {
}
/**
+ * Sets sensor privacy to the specified state for an individual sensor for the profile group of
+ * context's user.
+ *
+ * @param source the source using which the sensor is toggled.
+ * @param sensor the sensor which to change the state for
+ * @param state the state to which sensor privacy should be set.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
+ public void setSensorPrivacyStateForProfileGroup(@Sources.Source int source,
+ @Sensors.Sensor int sensor, @StateTypes.StateType int state) {
+ try {
+ mService.setToggleSensorPrivacyStateForProfileGroup(mContext.getUserId(), source,
+ sensor, state);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Don't show dialogs to turn off sensor privacy for this package.
*
* @param suppress Whether to suppress or re-enable.
@@ -865,6 +1065,12 @@ public final class SensorPrivacyManager {
boolean enabled) {
listener.onAllSensorPrivacyChanged(enabled);
}
+
+ @Override
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
+ public void onSensorPrivacyStateChanged(int toggleType, int sensor,
+ int state) {
+ }
};
mListeners.put(listener, iListener);
}
diff --git a/core/java/android/hardware/SerialManager.java b/core/java/android/hardware/SerialManager.java
index 26e5129b1fb3..e2ce7230a796 100644
--- a/core/java/android/hardware/SerialManager.java
+++ b/core/java/android/hardware/SerialManager.java
@@ -29,6 +29,7 @@ import java.io.IOException;
* @hide
*/
@SystemService(Context.SERIAL_SERVICE)
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class SerialManager {
private static final String TAG = "SerialManager";
@@ -69,6 +70,8 @@ public class SerialManager {
* @return the serial port
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = ParcelFileDescriptor.class, reason =
+ "Needs socketpair() to offer accurate emulation")
public SerialPort openSerialPort(String name, int speed) throws IOException {
try {
ParcelFileDescriptor pfd = mService.openSerialPort(name);
diff --git a/core/java/android/hardware/SerialManagerInternal.java b/core/java/android/hardware/SerialManagerInternal.java
new file mode 100644
index 000000000000..9132da06aeac
--- /dev/null
+++ b/core/java/android/hardware/SerialManagerInternal.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 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.hardware;
+
+import android.annotation.NonNull;
+import android.os.ParcelFileDescriptor;
+
+import java.util.function.Supplier;
+
+/**
+ * Internal interactions with {@link SerialManager}.
+ *
+ * @hide
+ */
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
+public abstract class SerialManagerInternal {
+ public abstract void addVirtualSerialPortForTest(@NonNull String name,
+ @NonNull Supplier<ParcelFileDescriptor> supplier);
+
+ public abstract void removeVirtualSerialPortForTest(@NonNull String name);
+}
diff --git a/core/java/android/hardware/biometrics/BiometricFaceConstants.java b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
index 2b62b98529a9..2ba1d897fe34 100644
--- a/core/java/android/hardware/biometrics/BiometricFaceConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
@@ -19,6 +19,8 @@ package android.hardware.biometrics;
import android.annotation.IntDef;
import android.app.KeyguardManager;
import android.hardware.biometrics.BiometricManager.Authenticators;
+import android.hardware.face.FaceEnrollOptions;
+import android.hardware.face.FaceEnrollOptions.EnrollReason;
import android.hardware.face.FaceManager;
import java.lang.annotation.Retention;
@@ -432,4 +434,22 @@ public interface BiometricFaceConstants {
* vendor code.
*/
int FACE_ACQUIRED_VENDOR_BASE = 1000;
+
+
+ /**
+ * Converts FaceEnrollOptions.reason into BiometricsProtoEnums.enrollReason
+ */
+ public static int reasonToMetric(@EnrollReason int reason) {
+ switch (reason) {
+ case FaceEnrollOptions.ENROLL_REASON_RE_ENROLL_NOTIFICATION:
+ return BiometricsProtoEnums.ENROLLMENT_SOURCE_FRR_NOTIFICATION;
+ case FaceEnrollOptions.ENROLL_REASON_SETTINGS:
+ return BiometricsProtoEnums.ENROLLMENT_SOURCE_SETTINGS;
+ case FaceEnrollOptions.ENROLL_REASON_SUW:
+ return BiometricsProtoEnums.ENROLLMENT_SOURCE_SUW;
+ default:
+ return BiometricsProtoEnums.ENROLLMENT_SOURCE_UNKNOWN;
+ }
+
+ }
}
diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
index 5b24fb6860a2..770448bd594b 100644
--- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
@@ -20,6 +20,8 @@ import android.annotation.IntDef;
import android.app.KeyguardManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.hardware.biometrics.BiometricManager.Authenticators;
+import android.hardware.fingerprint.FingerprintEnrollOptions;
+import android.hardware.fingerprint.FingerprintEnrollOptions.EnrollReason;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Build;
@@ -350,4 +352,22 @@ public interface BiometricFingerprintConstants {
return false;
}
}
+
+
+ /**
+ * Converts FaceEnrollOptions.reason into BiometricsProtoEnums.enrollReason
+ */
+ static int reasonToMetric(@EnrollReason int reason) {
+ switch(reason) {
+ case FingerprintEnrollOptions.ENROLL_REASON_RE_ENROLL_NOTIFICATION:
+ return BiometricsProtoEnums.ENROLLMENT_SOURCE_FRR_NOTIFICATION;
+ case FingerprintEnrollOptions.ENROLL_REASON_SETTINGS:
+ return BiometricsProtoEnums.ENROLLMENT_SOURCE_SETTINGS;
+ case FingerprintEnrollOptions.ENROLL_REASON_SUW:
+ return BiometricsProtoEnums.ENROLLMENT_SOURCE_SUW;
+ default:
+ return BiometricsProtoEnums.ENROLLMENT_SOURCE_UNKNOWN;
+ }
+
+ }
}
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index d7d1d1a7c677..fdd8b04921f5 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -100,6 +100,13 @@ public class BiometricManager {
Authenticators.DEVICE_CREDENTIAL | Authenticators.BIOMETRIC_STRONG;
/**
+ * Enroll reason extra that can be used by settings to understand where this request came
+ * from.
+ * @hide
+ */
+ public static final String EXTRA_ENROLL_REASON = "enroll_reason";
+
+ /**
* @hide
*/
@IntDef({BIOMETRIC_SUCCESS,
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index d4c58b239c84..0208fed6040f 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -22,8 +22,9 @@ import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.hardware.biometrics.BiometricManager.Authenticators;
import static android.hardware.biometrics.Flags.FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT;
-import static android.hardware.biometrics.Flags.FLAG_GET_OP_ID_CRYPTO_OBJECT;
import static android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT;
+import static android.hardware.biometrics.Flags.FLAG_GET_OP_ID_CRYPTO_OBJECT;
+import static android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE;
import android.annotation.CallbackExecutor;
import android.annotation.DrawableRes;
@@ -501,6 +502,28 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
}
/**
+ * Remove {@link Builder#setAllowBackgroundAuthentication(boolean)} once
+ * FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE is enabled.
+ *
+ * @param allow If true, allows authentication when the calling package is not in the
+ * foreground. This is set to false by default.
+ * @param useParentProfileForDeviceCredential If true, uses parent profile for device
+ * credential IME request
+ * @return This builder
+ * @hide
+ */
+ @FlaggedApi(FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE)
+ @TestApi
+ @NonNull
+ @RequiresPermission(anyOf = {TEST_BIOMETRIC, USE_BIOMETRIC_INTERNAL})
+ public Builder setAllowBackgroundAuthentication(boolean allow,
+ boolean useParentProfileForDeviceCredential) {
+ mPromptInfo.setAllowBackgroundAuthentication(allow);
+ mPromptInfo.setUseParentProfileForDeviceCredential(useParentProfileForDeviceCredential);
+ return this;
+ }
+
+ /**
* If set check the Device Policy Manager for disabled biometrics.
*
* @param checkDevicePolicyManager
diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java
index 2236660ee388..8bb958553673 100644
--- a/core/java/android/hardware/biometrics/PromptInfo.java
+++ b/core/java/android/hardware/biometrics/PromptInfo.java
@@ -55,6 +55,7 @@ public class PromptInfo implements Parcelable {
private boolean mIgnoreEnrollmentState;
private boolean mIsForLegacyFingerprintManager = false;
private boolean mShowEmergencyCallButton = false;
+ private boolean mUseParentProfileForDeviceCredential = false;
public PromptInfo() {
@@ -85,6 +86,7 @@ public class PromptInfo implements Parcelable {
mIgnoreEnrollmentState = in.readBoolean();
mIsForLegacyFingerprintManager = in.readBoolean();
mShowEmergencyCallButton = in.readBoolean();
+ mUseParentProfileForDeviceCredential = in.readBoolean();
}
public static final Creator<PromptInfo> CREATOR = new Creator<PromptInfo>() {
@@ -129,6 +131,7 @@ public class PromptInfo implements Parcelable {
dest.writeBoolean(mIgnoreEnrollmentState);
dest.writeBoolean(mIsForLegacyFingerprintManager);
dest.writeBoolean(mShowEmergencyCallButton);
+ dest.writeBoolean(mUseParentProfileForDeviceCredential);
}
// LINT.IfChange
@@ -181,6 +184,13 @@ public class PromptInfo implements Parcelable {
}
return false;
}
+
+ /**
+ * Returns if parent profile needs to be used for device credential.
+ */
+ public boolean shouldUseParentProfileForDeviceCredential() {
+ return mUseParentProfileForDeviceCredential;
+ }
// LINT.ThenChange(frameworks/base/core/java/android/hardware/biometrics/BiometricPrompt.java)
// Setters
@@ -281,6 +291,11 @@ public class PromptInfo implements Parcelable {
mShowEmergencyCallButton = showEmergencyCallButton;
}
+ public void setUseParentProfileForDeviceCredential(
+ boolean useParentProfileForDeviceCredential) {
+ mUseParentProfileForDeviceCredential = useParentProfileForDeviceCredential;
+ }
+
// Getters
@DrawableRes
public int getLogoRes() {
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 451d6fba0105..57b437f4bacf 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -21,6 +21,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.impl.ExtensionKey;
import android.hardware.camera2.impl.PublicKey;
import android.hardware.camera2.impl.SyntheticKey;
import android.hardware.camera2.params.DeviceStateSensorOrientationMap;
@@ -557,8 +558,11 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* on a particular SessionConfiguration.</p>
*
* @return List of CameraCharacteristic keys containing characterisitics specific to a session
- * configuration. For Android 15, this list only contains CONTROL_ZOOM_RATIO_RANGE and
- * SCALER_AVAILABLE_MAX_DIGITAL_ZOOM.
+ * configuration. If {@link #INFO_SESSION_CONFIGURATION_QUERY_VERSION} is
+ * {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, then this list will only contain
+ * CONTROL_ZOOM_RATIO_RANGE and SCALER_AVAILABLE_MAX_DIGITAL_ZOOM
+ *
+ * @see INFO_SESSION_CONFIGURATION_QUERY_VERSION
*/
@NonNull
@FlaggedApi(Flags.FLAG_FEATURE_COMBINATION_QUERY)
@@ -6076,6 +6080,28 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> JPEGR_AVAILABLE_JPEG_R_STALL_DURATIONS_MAXIMUM_RESOLUTION =
new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.jpegr.availableJpegRStallDurationsMaximumResolution", android.hardware.camera2.params.StreamConfigurationDuration[].class);
+ /**
+ * <p>Minimum and maximum padding zoom factors supported by this camera device for
+ * {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } used for the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension.</p>
+ * <p>The minimum and maximum padding zoom factors supported by the device for
+ * {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } used as part of the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension feature. This extension specific camera characteristic can be queried using
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#get }.</p>
+ * <p><b>Units</b>: A pair of padding zoom factors in floating-points:
+ * (minPaddingZoomFactor, maxPaddingZoomFactor)</p>
+ * <p><b>Range of valid values:</b><br></p>
+ * <p>1.0 &lt; minPaddingZoomFactor &lt;= maxPaddingZoomFactor</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * @hide
+ */
+ @ExtensionKey
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final Key<android.util.Range<Float>> EFV_PADDING_ZOOM_FACTOR_RANGE =
+ new Key<android.util.Range<Float>>("android.efv.paddingZoomFactorRange", new TypeReference<android.util.Range<Float>>() {{ }});
+
/*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
* End generated code
*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 1a0074f01087..991bade09a25 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -1718,7 +1718,7 @@ public abstract class CameraDevice implements AutoCloseable {
* <p>Other than that, the characteristics returned here can be used in the same way as
* those returned from {@link CameraManager#getCameraCharacteristics}.</p>
*
- * @param sessionConfig : The session configuration for which characteristics are fetched.
+ * @param sessionConfig The session configuration for which characteristics are fetched.
* @return CameraCharacteristics specific to a given session configuration.
*
* @throws IllegalArgumentException if the session configuration is invalid
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index 3b10e0dd516a..749f218b0e6a 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -26,6 +26,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.ImageFormat;
+import android.hardware.camera2.CameraCharacteristics.Key;
import android.hardware.camera2.extension.IAdvancedExtenderImpl;
import android.hardware.camera2.extension.ICameraExtensionsProxyService;
import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
@@ -35,6 +36,8 @@ import android.hardware.camera2.extension.LatencyRange;
import android.hardware.camera2.extension.SizeList;
import android.hardware.camera2.impl.CameraExtensionUtils;
import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.impl.ExtensionKey;
+import android.hardware.camera2.impl.PublicKey;
import android.hardware.camera2.params.ExtensionSessionConfiguration;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.os.Binder;
@@ -805,13 +808,11 @@ public final class CameraExtensionCharacteristics {
extender.init(mCameraId, mCharacteristicsMapNative);
CameraMetadataNative metadata =
extender.getAvailableCharacteristicsKeyValues(mCameraId);
- CameraCharacteristics fallbackCharacteristics = mCharacteristicsMap.get(mCameraId);
if (metadata == null) {
- return fallbackCharacteristics.get(key);
+ return null;
}
CameraCharacteristics characteristics = new CameraCharacteristics(metadata);
- T value = characteristics.get(key);
- return value == null ? fallbackCharacteristics.get(key) : value;
+ return characteristics.get(key);
}
} catch (RemoteException e) {
Log.e(TAG, "Failed to query the extension for the specified key! Extension "
@@ -873,8 +874,11 @@ public final class CameraExtensionCharacteristics {
Class<CameraCharacteristics.Key<?>> keyTyped =
(Class<CameraCharacteristics.Key<?>>) key;
+ // Do not include synthetic keys. Including synthetic keys leads to undefined
+ // behavior. This causes inclusion of capabilities that may not be supported in
+ // camera extensions.
ret.addAll(chars.getAvailableKeyList(CameraCharacteristics.class, keyTyped, keys,
- /*includeSynthetic*/ true));
+ /*includeSynthetic*/ false));
}
} catch (RemoteException e) {
Log.e(TAG, "Failed to query the extension for all available keys! Extension "
@@ -1497,4 +1501,28 @@ public final class CameraExtensionCharacteristics {
return Collections.unmodifiableSet(ret);
}
+
+
+ /**
+ * <p>Minimum and maximum padding zoom factors supported by this camera device for
+ * {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } used for
+ * the {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension.</p>
+ * <p>The minimum and maximum padding zoom factors supported by the device for
+ * {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } used as part of the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension feature. This extension specific camera characteristic can be queried using
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#get}.</p>
+ * <p><b>Units</b>: A pair of padding zoom factors in floating-points:
+ * (minPaddingZoomFactor, maxPaddingZoomFactor)</p>
+ * <p><b>Range of valid values:</b><br></p>
+ * <p>1.0 &lt; minPaddingZoomFactor &lt;= maxPaddingZoomFactor</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ */
+ @PublicKey
+ @NonNull
+ @ExtensionKey
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final Key<android.util.Range<Float>> EFV_PADDING_ZOOM_FACTOR_RANGE =
+ CameraCharacteristics.EFV_PADDING_ZOOM_FACTOR_RANGE;
}
diff --git a/core/java/android/hardware/camera2/CameraExtensionSession.java b/core/java/android/hardware/camera2/CameraExtensionSession.java
index 21fead980cdd..2d9433e31ab2 100644
--- a/core/java/android/hardware/camera2/CameraExtensionSession.java
+++ b/core/java/android/hardware/camera2/CameraExtensionSession.java
@@ -16,11 +16,14 @@
package android.hardware.camera2;
+import android.annotation.FlaggedApi;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.camera2.utils.HashCodeHelpers;
+import com.android.internal.camera.flags.Flags;
+
import java.util.concurrent.Executor;
/**
@@ -132,6 +135,34 @@ public abstract class CameraExtensionSession implements AutoCloseable {
}
/**
+ * This method is called instead of
+ * {@link #onCaptureProcessStarted} when the camera device failed
+ * to produce the required input for the device-specific extension. The
+ * cause could be a failed camera capture request, a failed
+ * capture result or dropped camera frame. More information about
+ * the reason is included in the 'failure' argument.
+ *
+ * <p>Other requests are unaffected, and some or all image buffers
+ * from the capture may have been pushed to their respective output
+ * streams.</p>
+ *
+ * <p>The default implementation of this method does nothing.</p>
+ *
+ * @param session the session received during
+ * {@link StateCallback#onConfigured(CameraExtensionSession)}
+ * @param request The request that was given to the CameraDevice
+ * @param failure The capture failure reason
+ *
+ * @see #capture
+ * @see #setRepeatingRequest
+ */
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public void onCaptureFailed(@NonNull CameraExtensionSession session,
+ @NonNull CaptureRequest request, @CaptureFailure.FailureReason int failure) {
+ // default empty implementation
+ }
+
+ /**
* This method is called independently of the others in
* ExtensionCaptureCallback, when a capture sequence finishes.
*
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 7cf10d89004f..e24c98e98c5d 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -21,6 +21,7 @@ import android.annotation.NonNull;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.impl.ExtensionKey;
import android.hardware.camera2.impl.PublicKey;
import android.hardware.camera2.impl.SyntheticKey;
import android.util.Log;
@@ -276,8 +277,11 @@ public abstract class CameraMetadata<TKey> {
throw new IllegalArgumentException("key type must be that of a metadata key");
}
- if (field.getAnnotation(PublicKey.class) == null) {
- // Never expose @hide keys up to the API user
+ if (field.getAnnotation(PublicKey.class) == null
+ && field.getAnnotation(ExtensionKey.class) == null) {
+ // Never expose @hide keys to the API user unless they are
+ // marked as @ExtensionKey, as these keys are publicly accessible via
+ // the extension key classes.
return false;
}
@@ -3893,6 +3897,36 @@ public abstract class CameraMetadata<TKey> {
public static final int DISTORTION_CORRECTION_MODE_HIGH_QUALITY = 2;
//
+ // Enumeration values for CaptureRequest#EFV_STABILIZATION_MODE
+ //
+
+ /**
+ * <p>No stabilization.</p>
+ * @see CaptureRequest#EFV_STABILIZATION_MODE
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final int EFV_STABILIZATION_MODE_OFF = 0;
+
+ /**
+ * <p>Gimbal stabilization mode.</p>
+ * @see CaptureRequest#EFV_STABILIZATION_MODE
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final int EFV_STABILIZATION_MODE_GIMBAL = 1;
+
+ /**
+ * <p>Locked stabilization mode which uses the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * stabilization to directionally steady the target region.</p>
+ * @see CaptureRequest#EFV_STABILIZATION_MODE
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final int EFV_STABILIZATION_MODE_LOCKED = 2;
+
+ //
// Enumeration values for CaptureResult#CONTROL_AE_STATE
//
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index ded96a23e11e..66efccd14097 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -21,6 +21,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.impl.ExtensionKey;
import android.hardware.camera2.impl.PublicKey;
import android.hardware.camera2.impl.SyntheticKey;
import android.hardware.camera2.params.OutputConfiguration;
@@ -4292,6 +4293,146 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
public static final Key<Integer> EXTENSION_STRENGTH =
new Key<Integer>("android.extension.strength", int.class);
+ /**
+ * <p>Used to apply an additional digital zoom factor for the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension in {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_LOCKED } mode.</p>
+ * <p>For the {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * feature, an additional zoom factor is applied on top of the existing {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}.
+ * This additional zoom factor serves as a buffer to provide more flexibility for the
+ * {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_LOCKED }
+ * mode. If android.efv.paddingZoomFactor is not set, the default will be used.
+ * The effectiveness of the stabilization may be influenced by the amount of padding zoom
+ * applied. A higher padding zoom factor can stabilize the target region more effectively
+ * with greater flexibility but may potentially impact image quality. Conversely, a lower
+ * padding zoom factor may be used to prioritize preserving image quality, albeit with less
+ * leeway in stabilizing the target region. It is recommended to set the
+ * android.efv.paddingZoomFactor to at least 1.5.</p>
+ * <p>If android.efv.autoZoom is enabled, the requested android.efv.paddingZoomFactor will be overridden.
+ * android.efv.maxPaddingZoomFactor can be checked for more details on controlling the
+ * padding zoom factor during android.efv.autoZoom.</p>
+ * <p><b>Range of valid values:</b><br>
+ * android.efv.paddingZoomFactorRange</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#CONTROL_ZOOM_RATIO
+ * @hide
+ */
+ @ExtensionKey
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final Key<Float> EFV_PADDING_ZOOM_FACTOR =
+ new Key<Float>("android.efv.paddingZoomFactor", float.class);
+
+ /**
+ * <p>Used to enable or disable auto zoom for the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension in {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_LOCKED } mode.</p>
+ * <p>Turn on auto zoom to let the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * feature decide at any given point a combination of
+ * {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} and android.efv.paddingZoomFactor
+ * to keep the target region in view and stabilized. The combination chosen by the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * will equal the requested {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} multiplied with the requested
+ * android.efv.paddingZoomFactor. A limit can be set on the padding zoom if wanting
+ * to control image quality further using android.efv.maxPaddingZoomFactor.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#CONTROL_ZOOM_RATIO
+ * @hide
+ */
+ @ExtensionKey
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final Key<Boolean> EFV_AUTO_ZOOM =
+ new Key<Boolean>("android.efv.autoZoom", boolean.class);
+
+ /**
+ * <p>Used to limit the android.efv.paddingZoomFactor if
+ * android.efv.autoZoom is enabled for the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension in {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_LOCKED } mode.</p>
+ * <p>If android.efv.autoZoom is enabled, this key can be used to set a limit
+ * on the android.efv.paddingZoomFactor chosen by the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension in {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_LOCKED } mode
+ * to control image quality.</p>
+ * <p><b>Range of valid values:</b><br>
+ * The range of android.efv.paddingZoomFactorRange. Use a value greater than or equal to
+ * the android.efv.paddingZoomFactor to effectively utilize this key.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * @hide
+ */
+ @ExtensionKey
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final Key<Float> EFV_MAX_PADDING_ZOOM_FACTOR =
+ new Key<Float>("android.efv.maxPaddingZoomFactor", float.class);
+
+ /**
+ * <p>Set the stabilization mode for the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension</p>
+ * <p>The desired stabilization mode. Gimbal stabilization mode provides simple, non-locked
+ * video stabilization. Locked mode uses the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * stabilization feature to fixate on the current region, utilizing it as the target area for
+ * stabilization.</p>
+ * <p><b>Possible values:</b></p>
+ * <ul>
+ * <li>{@link #EFV_STABILIZATION_MODE_OFF OFF}</li>
+ * <li>{@link #EFV_STABILIZATION_MODE_GIMBAL GIMBAL}</li>
+ * <li>{@link #EFV_STABILIZATION_MODE_LOCKED LOCKED}</li>
+ * </ul>
+ *
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * @see #EFV_STABILIZATION_MODE_OFF
+ * @see #EFV_STABILIZATION_MODE_GIMBAL
+ * @see #EFV_STABILIZATION_MODE_LOCKED
+ * @hide
+ */
+ @ExtensionKey
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final Key<Integer> EFV_STABILIZATION_MODE =
+ new Key<Integer>("android.efv.stabilizationMode", int.class);
+
+ /**
+ * <p>Used to update the target region for the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension in {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_LOCKED } mode.</p>
+ * <p>A android.util.Pair<Integer,Integer> that represents the desired
+ * <Horizontal,Vertical> shift of the current locked view (or target region) in
+ * pixels. Negative values indicate left and upward shifts, while positive values indicate
+ * right and downward shifts in the active array coordinate system.</p>
+ * <p><b>Range of valid values:</b><br>
+ * android.util.Pair<Integer,Integer> represents the
+ * <Horizontal,Vertical> shift. The range for the horizontal shift is
+ * [-max(android.efv.paddingRegion-left), max(android.efv.paddingRegion-right)].
+ * The range for the vertical shift is
+ * [-max(android.efv.paddingRegion-top), max(android.efv.paddingRegion-bottom)]</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * @hide
+ */
+ @ExtensionKey
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final Key<android.util.Pair<Integer,Integer>> EFV_TRANSLATE_VIEWPORT =
+ new Key<android.util.Pair<Integer,Integer>>("android.efv.translateViewport", new TypeReference<android.util.Pair<Integer,Integer>>() {{ }});
+
+ /**
+ * <p>Representing the desired clockwise rotation
+ * of the target region in degrees for the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension in {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_LOCKED } mode.</p>
+ * <p>Value representing the desired clockwise rotation of the target
+ * region in degrees.</p>
+ * <p><b>Range of valid values:</b><br>
+ * 0 to 360</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * @hide
+ */
+ @ExtensionKey
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final Key<Float> EFV_ROTATE_VIEWPORT =
+ new Key<Float>("android.efv.rotateViewport", float.class);
+
/*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
* End generated code
*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 7cf5a7f2e445..a01c23d984f4 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -22,6 +22,7 @@ import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.impl.CaptureResultExtras;
+import android.hardware.camera2.impl.ExtensionKey;
import android.hardware.camera2.impl.PublicKey;
import android.hardware.camera2.impl.SyntheticKey;
import android.hardware.camera2.utils.TypeReference;
@@ -5919,6 +5920,214 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
public static final Key<Integer> EXTENSION_STRENGTH =
new Key<Integer>("android.extension.strength", int.class);
+ /**
+ * <p>The padding region for the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension in {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_LOCKED } mode.</p>
+ * <p>An array [left, top, right, bottom] of the padding in pixels remaining on all four sides
+ * before the target region starts to go out of bounds.</p>
+ * <p>The padding region denotes the area surrounding the stabilized target region within which
+ * the camera can be moved while maintaining the target region in view. As the camera moves,
+ * the padding region adjusts to represent the proximity of the target region to the
+ * boundary, which is the point at which the target region will start to go out of bounds.</p>
+ * <p><b>Range of valid values:</b><br>
+ * The padding is the number of remaining pixels of padding in each direction.
+ * The pixels reference the active array coordinate system. Negative values indicate the target
+ * region is out of bounds. The value for this key may be null for when the stabilization mode is
+ * in {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_OFF }
+ * or {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_GIMBAL } mode.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * @hide
+ */
+ @ExtensionKey
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final Key<int[]> EFV_PADDING_REGION =
+ new Key<int[]>("android.efv.paddingRegion", int[].class);
+
+ /**
+ * <p>The padding region when android.efv.autoZoom is enabled for the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension in {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_LOCKED } mode.</p>
+ * <p>An array [left, top, right, bottom] of the padding in pixels remaining on all four sides
+ * before the target region starts to go out of bounds.</p>
+ * <p>This may differ from android.efv.paddingRegion as the field of view can change
+ * during android.efv.autoZoom, altering the boundary region and thus updating the padding between the
+ * target region and the boundary.</p>
+ * <p><b>Range of valid values:</b><br>
+ * The padding is the number of remaining pixels of padding in each direction
+ * when android.efv.autoZoom is enabled. Negative values indicate the target region is out of bounds.
+ * The value for this key may be null for when the android.efv.autoZoom is not enabled.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * @hide
+ */
+ @ExtensionKey
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final Key<int[]> EFV_AUTO_ZOOM_PADDING_REGION =
+ new Key<int[]>("android.efv.autoZoomPaddingRegion", int[].class);
+
+ /**
+ * <p>List of coordinates representing the target region relative to the
+ * {@link android.hardware.camera2.CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE }
+ * for the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension in
+ * {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_LOCKED } mode.</p>
+ * <p>A list of android.graphics.PointF that define the coordinates of the target region
+ * relative to the
+ * {@link android.hardware.camera2.CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE }.
+ * The array represents the target region coordinates as: top-left, top-right, bottom-left,
+ * bottom-right.</p>
+ * <p><b>Range of valid values:</b><br>
+ * The list of target coordinates will define a region within the bounds of the
+ * {@link android.hardware.camera2.CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE }</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * @hide
+ */
+ @ExtensionKey
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final Key<android.graphics.PointF[]> EFV_TARGET_COORDINATES =
+ new Key<android.graphics.PointF[]>("android.efv.targetCoordinates", android.graphics.PointF[].class);
+
+ /**
+ * <p>Used to apply an additional digital zoom factor for the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension in {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_LOCKED } mode.</p>
+ * <p>For the {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * feature, an additional zoom factor is applied on top of the existing {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}.
+ * This additional zoom factor serves as a buffer to provide more flexibility for the
+ * {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_LOCKED }
+ * mode. If android.efv.paddingZoomFactor is not set, the default will be used.
+ * The effectiveness of the stabilization may be influenced by the amount of padding zoom
+ * applied. A higher padding zoom factor can stabilize the target region more effectively
+ * with greater flexibility but may potentially impact image quality. Conversely, a lower
+ * padding zoom factor may be used to prioritize preserving image quality, albeit with less
+ * leeway in stabilizing the target region. It is recommended to set the
+ * android.efv.paddingZoomFactor to at least 1.5.</p>
+ * <p>If android.efv.autoZoom is enabled, the requested android.efv.paddingZoomFactor will be overridden.
+ * android.efv.maxPaddingZoomFactor can be checked for more details on controlling the
+ * padding zoom factor during android.efv.autoZoom.</p>
+ * <p><b>Range of valid values:</b><br>
+ * android.efv.paddingZoomFactorRange</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#CONTROL_ZOOM_RATIO
+ * @hide
+ */
+ @ExtensionKey
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final Key<Float> EFV_PADDING_ZOOM_FACTOR =
+ new Key<Float>("android.efv.paddingZoomFactor", float.class);
+
+ /**
+ * <p>Set the stabilization mode for the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension</p>
+ * <p>The desired stabilization mode. Gimbal stabilization mode provides simple, non-locked
+ * video stabilization. Locked mode uses the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * stabilization feature to fixate on the current region, utilizing it as the target area for
+ * stabilization.</p>
+ * <p><b>Possible values:</b></p>
+ * <ul>
+ * <li>{@link #EFV_STABILIZATION_MODE_OFF OFF}</li>
+ * <li>{@link #EFV_STABILIZATION_MODE_GIMBAL GIMBAL}</li>
+ * <li>{@link #EFV_STABILIZATION_MODE_LOCKED LOCKED}</li>
+ * </ul>
+ *
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * @see #EFV_STABILIZATION_MODE_OFF
+ * @see #EFV_STABILIZATION_MODE_GIMBAL
+ * @see #EFV_STABILIZATION_MODE_LOCKED
+ * @hide
+ */
+ @ExtensionKey
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final Key<Integer> EFV_STABILIZATION_MODE =
+ new Key<Integer>("android.efv.stabilizationMode", int.class);
+
+ /**
+ * <p>Used to enable or disable auto zoom for the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension in {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_LOCKED } mode.</p>
+ * <p>Turn on auto zoom to let the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * feature decide at any given point a combination of
+ * {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} and android.efv.paddingZoomFactor
+ * to keep the target region in view and stabilized. The combination chosen by the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * will equal the requested {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} multiplied with the requested
+ * android.efv.paddingZoomFactor. A limit can be set on the padding zoom if wanting
+ * to control image quality further using android.efv.maxPaddingZoomFactor.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#CONTROL_ZOOM_RATIO
+ * @hide
+ */
+ @ExtensionKey
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final Key<Boolean> EFV_AUTO_ZOOM =
+ new Key<Boolean>("android.efv.autoZoom", boolean.class);
+
+ /**
+ * <p>Representing the desired clockwise rotation
+ * of the target region in degrees for the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension in {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_LOCKED } mode.</p>
+ * <p>Value representing the desired clockwise rotation of the target
+ * region in degrees.</p>
+ * <p><b>Range of valid values:</b><br>
+ * 0 to 360</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * @hide
+ */
+ @ExtensionKey
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final Key<Float> EFV_ROTATE_VIEWPORT =
+ new Key<Float>("android.efv.rotateViewport", float.class);
+
+ /**
+ * <p>Used to update the target region for the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension in {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_LOCKED } mode.</p>
+ * <p>A android.util.Pair<Integer,Integer> that represents the desired
+ * <Horizontal,Vertical> shift of the current locked view (or target region) in
+ * pixels. Negative values indicate left and upward shifts, while positive values indicate
+ * right and downward shifts in the active array coordinate system.</p>
+ * <p><b>Range of valid values:</b><br>
+ * android.util.Pair<Integer,Integer> represents the
+ * <Horizontal,Vertical> shift. The range for the horizontal shift is
+ * [-max(android.efv.paddingRegion-left), max(android.efv.paddingRegion-right)].
+ * The range for the vertical shift is
+ * [-max(android.efv.paddingRegion-top), max(android.efv.paddingRegion-bottom)]</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * @hide
+ */
+ @ExtensionKey
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final Key<android.util.Pair<Integer,Integer>> EFV_TRANSLATE_VIEWPORT =
+ new Key<android.util.Pair<Integer,Integer>>("android.efv.translateViewport", new TypeReference<android.util.Pair<Integer,Integer>>() {{ }});
+
+ /**
+ * <p>Used to limit the android.efv.paddingZoomFactor if
+ * android.efv.autoZoom is enabled for the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension in {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_LOCKED } mode.</p>
+ * <p>If android.efv.autoZoom is enabled, this key can be used to set a limit
+ * on the android.efv.paddingZoomFactor chosen by the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension in {@link android.hardware.camera2.CameraMetadata#EFV_STABILIZATION_MODE_LOCKED } mode
+ * to control image quality.</p>
+ * <p><b>Range of valid values:</b><br>
+ * The range of android.efv.paddingZoomFactorRange. Use a value greater than or equal to
+ * the android.efv.paddingZoomFactor to effectively utilize this key.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * @hide
+ */
+ @ExtensionKey
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final Key<Float> EFV_MAX_PADDING_ZOOM_FACTOR =
+ new Key<Float>("android.efv.maxPaddingZoomFactor", float.class);
+
/*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
* End generated code
*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
diff --git a/core/java/android/hardware/camera2/ExtensionCaptureRequest.java b/core/java/android/hardware/camera2/ExtensionCaptureRequest.java
new file mode 100644
index 000000000000..32039c6ec0ba
--- /dev/null
+++ b/core/java/android/hardware/camera2/ExtensionCaptureRequest.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2024 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.hardware.camera2;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureRequest.Key;
+import android.hardware.camera2.impl.ExtensionKey;
+import android.hardware.camera2.impl.PublicKey;
+
+import com.android.internal.camera.flags.Flags;
+
+/**
+ * ExtensionCaptureRequest contains definitions for extension-specific CaptureRequest keys that
+ * can be used to configure a {@link android.hardware.camera2.CaptureRequest} during a
+ * {@link android.hardware.camera2.CameraExtensionSession}.
+ *
+ * Note that ExtensionCaptureRequest is not intended to be used as a replacement
+ * for CaptureRequest in the extensions. It serves as a supplementary class providing
+ * extension-specific CaptureRequest keys. Developers should use these keys in conjunction
+ * with regular CaptureRequest objects during a
+ * {@link android.hardware.camera2.CameraExtensionSession}.
+ *
+ * @see CaptureRequest
+ * @see CameraExtensionSession
+ */
+@FlaggedApi(Flags.FLAG_CONCERT_MODE)
+public final class ExtensionCaptureRequest {
+
+ /**
+ * <p>Used to apply an additional digital zoom factor for the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension in {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED } mode.</p>
+ * <p>For the {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * feature, an additional zoom factor is applied on top of the existing {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}.
+ * This additional zoom factor serves as a buffer to provide more flexibility for the
+ * {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED }
+ * mode. If {@link ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } is not set, the default will be used.
+ * The effectiveness of the stabilization may be influenced by the amount of padding zoom
+ * applied. A higher padding zoom factor can stabilize the target region more effectively
+ * with greater flexibility but may potentially impact image quality. Conversely, a lower
+ * padding zoom factor may be used to prioritize preserving image quality, albeit with less
+ * leeway in stabilizing the target region. It is recommended to set the
+ * {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } to at least 1.5.</p>
+ * <p>If {@link ExtensionCaptureRequest#EFV_AUTO_ZOOM } is enabled, the requested {@link ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } will be overridden.
+ * {@link ExtensionCaptureRequest#EFV_MAX_PADDING_ZOOM_FACTOR } can be checked for more details on controlling the
+ * padding zoom factor during {@link ExtensionCaptureRequest#EFV_AUTO_ZOOM }.</p>
+ * <p><b>Range of valid values:</b><br>
+ * {@link CameraExtensionCharacteristics#EFV_PADDING_ZOOM_FACTOR_RANGE }</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#CONTROL_ZOOM_RATIO
+ * @see ExtensionCaptureRequest#EFV_AUTO_ZOOM
+ * @see ExtensionCaptureRequest#EFV_MAX_PADDING_ZOOM_FACTOR
+ * @see ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR
+ * @see CameraExtensionCharacteristics#EFV_PADDING_ZOOM_FACTOR_RANGE
+ */
+ @PublicKey
+ @NonNull
+ @ExtensionKey
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final Key<Float> EFV_PADDING_ZOOM_FACTOR = CaptureRequest.EFV_PADDING_ZOOM_FACTOR;
+
+ /**
+ * <p>Used to enable or disable auto zoom for the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension in {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED } mode.</p>
+ * <p>Turn on auto zoom to let the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * feature decide at any given point a combination of
+ * {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} and {@link ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR }
+ * to keep the target region in view and stabilized. The combination chosen by the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * will equal the requested {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} multiplied with the requested
+ * {@link ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR }. A limit can be set on the padding zoom if wanting
+ * to control image quality further using {@link ExtensionCaptureRequest#EFV_MAX_PADDING_ZOOM_FACTOR }.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#CONTROL_ZOOM_RATIO
+ * @see ExtensionCaptureRequest#EFV_MAX_PADDING_ZOOM_FACTOR
+ * @see ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR
+ */
+ @PublicKey
+ @NonNull
+ @ExtensionKey
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final Key<Boolean> EFV_AUTO_ZOOM = CaptureRequest.EFV_AUTO_ZOOM;
+
+ /**
+ * <p>Used to limit the {@link ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } if
+ * {@link ExtensionCaptureRequest#EFV_AUTO_ZOOM } is enabled for the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension in {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED } mode.</p>
+ * <p>If {@link ExtensionCaptureRequest#EFV_AUTO_ZOOM } is enabled, this key can be used to set a limit
+ * on the {@link ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } chosen by the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension in {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED } mode
+ * to control image quality.</p>
+ * <p><b>Range of valid values:</b><br>
+ * The range of {@link CameraExtensionCharacteristics#EFV_PADDING_ZOOM_FACTOR_RANGE Range}. Use a value greater than or equal to
+ * the {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } to
+ * effectively utilize this key.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see ExtensionCaptureRequest#EFV_AUTO_ZOOM
+ * @see ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR
+ * @see CameraExtensionCharacteristics#EFV_PADDING_ZOOM_FACTOR_RANGE
+ */
+ @PublicKey
+ @NonNull
+ @ExtensionKey
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final Key<Float> EFV_MAX_PADDING_ZOOM_FACTOR = CaptureRequest.EFV_MAX_PADDING_ZOOM_FACTOR;
+
+ /**
+ * <p>Set the stabilization mode for the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension</p>
+ * <p>The desired stabilization mode. Gimbal stabilization mode provides simple, non-locked
+ * video stabilization. Locked mode uses the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * stabilization feature to fixate on the current region, utilizing it as the target area for
+ * stabilization.</p>
+ * <p><b>Possible values:</b></p>
+ * <ul>
+ * <li>{@link #EFV_STABILIZATION_MODE_OFF OFF}</li>
+ * <li>{@link #EFV_STABILIZATION_MODE_GIMBAL GIMBAL}</li>
+ * <li>{@link #EFV_STABILIZATION_MODE_LOCKED LOCKED}</li>
+ * </ul>
+ *
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * @see #EFV_STABILIZATION_MODE_OFF
+ * @see #EFV_STABILIZATION_MODE_GIMBAL
+ * @see #EFV_STABILIZATION_MODE_LOCKED
+ */
+ @PublicKey
+ @NonNull
+ @ExtensionKey
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final Key<Integer> EFV_STABILIZATION_MODE = CaptureRequest.EFV_STABILIZATION_MODE;
+
+ /**
+ * <p>Used to update the target region for the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension in {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED } mode.</p>
+ * <p>A android.util.Pair<Integer,Integer> that represents the desired
+ * <Horizontal,Vertical> shift of the current locked view (or target region) in
+ * pixels. Negative values indicate left and upward shifts, while positive values indicate
+ * right and downward shifts in the active array coordinate system.</p>
+ * <p><b>Range of valid values:</b><br>
+ * android.util.Pair<Integer,Integer> represents the
+ * <Horizontal,Vertical> shift. The range for the horizontal shift is
+ * [-max({@link ExtensionCaptureResult#EFV_PADDING_REGION }-left), max({@link ExtensionCaptureResult#EFV_PADDING_REGION }-right)].
+ * The range for the vertical shift is
+ * [-max({@link ExtensionCaptureResult#EFV_PADDING_REGION }-top), max({@link ExtensionCaptureResult#EFV_PADDING_REGION }-bottom)]</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see ExtensionCaptureResult#EFV_PADDING_REGION
+ */
+ @PublicKey
+ @NonNull
+ @ExtensionKey
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final Key<android.util.Pair<Integer,Integer>> EFV_TRANSLATE_VIEWPORT = CaptureRequest.EFV_TRANSLATE_VIEWPORT;
+
+ /**
+ * <p>Representing the desired clockwise rotation
+ * of the target region in degrees for the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension in {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED } mode.</p>
+ * <p>Value representing the desired clockwise rotation of the target
+ * region in degrees.</p>
+ * <p><b>Range of valid values:</b><br>
+ * 0 to 360</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ */
+ @PublicKey
+ @NonNull
+ @ExtensionKey
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final Key<Float> EFV_ROTATE_VIEWPORT = CaptureRequest.EFV_ROTATE_VIEWPORT;
+
+
+ //
+ // Enumeration values for CaptureRequest#EFV_STABILIZATION_MODE
+ //
+
+ /**
+ * <p>No stabilization.</p>
+ * @see ExtensionCaptureRequest#EFV_STABILIZATION_MODE
+ */
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final int EFV_STABILIZATION_MODE_OFF = CaptureRequest.EFV_STABILIZATION_MODE_OFF;
+
+ /**
+ * <p>Gimbal stabilization mode.</p>
+ * @see ExtensionCaptureRequest#EFV_STABILIZATION_MODE
+ */
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final int EFV_STABILIZATION_MODE_GIMBAL = CaptureRequest.EFV_STABILIZATION_MODE_GIMBAL;
+
+ /**
+ * <p>Locked stabilization mode which uses the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * stabilization to directionally steady the target region.</p>
+ * @see ExtensionCaptureRequest#EFV_STABILIZATION_MODE
+ */
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final int EFV_STABILIZATION_MODE_LOCKED = CaptureRequest.EFV_STABILIZATION_MODE_LOCKED;
+
+} \ No newline at end of file
diff --git a/core/java/android/hardware/camera2/ExtensionCaptureResult.java b/core/java/android/hardware/camera2/ExtensionCaptureResult.java
new file mode 100644
index 000000000000..5c9990975a9b
--- /dev/null
+++ b/core/java/android/hardware/camera2/ExtensionCaptureResult.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2024 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.hardware.camera2;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraExtensionCharacteristics;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.CaptureResult.Key;
+import android.hardware.camera2.impl.ExtensionKey;
+import android.hardware.camera2.impl.PublicKey;
+
+import com.android.internal.camera.flags.Flags;
+
+/**
+ * ExtensionCaptureResult contains definitions for extension-specific CaptureResult keys that
+ * are available during a {@link android.hardware.camera2.CameraExtensionSession} after a
+ * {@link android.hardware.camera2.CaptureRequest} is processed.
+ *
+ * Note that ExtensionCaptureResult is not intended to be used as a replacement
+ * for CaptureResult in the extensions. It serves as a supplementary class providing
+ * extension-specific CaptureResult keys. Developers should use these keys in conjunction
+ * with regular CaptureResult objects during a
+ * {@link android.hardware.camera2.CameraExtensionSession}.
+ *
+ * @see CaptureResult
+ * @see CaptureRequest
+ * @see CameraExtensionSession
+ */
+@FlaggedApi(Flags.FLAG_CONCERT_MODE)
+public final class ExtensionCaptureResult {
+
+ /**
+ * <p>The padding region for the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension in {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED } mode.</p>
+ * <p>An array [left, top, right, bottom] of the padding in pixels remaining on all four sides
+ * before the target region starts to go out of bounds.</p>
+ * <p>The padding region denotes the area surrounding the stabilized target region within which
+ * the camera can be moved while maintaining the target region in view. As the camera moves,
+ * the padding region adjusts to represent the proximity of the target region to the
+ * boundary, which is the point at which the target region will start to go out of bounds.</p>
+ * <p><b>Range of valid values:</b><br>
+ * The padding is the number of remaining pixels of padding in each direction.
+ * The pixels reference the active array coordinate system. Negative values indicate the target region
+ * is out of bounds. The value for this key may be null for when the stabilization mode is
+ * in {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_OFF }
+ * or {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_GIMBAL } mode.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ */
+ @PublicKey
+ @NonNull
+ @ExtensionKey
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final Key<int[]> EFV_PADDING_REGION = CaptureResult.EFV_PADDING_REGION;
+
+ /**
+ * <p>The padding region when {@link ExtensionCaptureRequest#EFV_AUTO_ZOOM } is enabled for the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension in {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED } mode.</p>
+ * <p>An array [left, top, right, bottom] of the padding in pixels remaining on all four sides
+ * before the target region starts to go out of bounds.</p>
+ * <p>This may differ from {@link ExtensionCaptureResult#EFV_PADDING_REGION } as the field of view can change
+ * during {@link ExtensionCaptureRequest#EFV_AUTO_ZOOM }, altering the boundary region and thus updating the padding between the
+ * target region and the boundary.</p>
+ * <p><b>Range of valid values:</b><br>
+ * The padding is the number of remaining pixels of padding in each direction
+ * when {@link ExtensionCaptureRequest#EFV_AUTO_ZOOM } is enabled. Negative values indicate the target region is out of bounds.
+ * The value for this key may be null for when the {@link ExtensionCaptureRequest#EFV_AUTO_ZOOM } is not enabled.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see ExtensionCaptureRequest#EFV_AUTO_ZOOM
+ * @see ExtensionCaptureResult#EFV_PADDING_REGION
+ */
+ @PublicKey
+ @NonNull
+ @ExtensionKey
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final Key<int[]> EFV_AUTO_ZOOM_PADDING_REGION = CaptureResult.EFV_AUTO_ZOOM_PADDING_REGION;
+
+ /**
+ * <p>List of coordinates representing the target region relative to the
+ * {@link android.hardware.camera2.CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE }
+ * for the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension in
+ * {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED } mode.</p>
+ * <p>A list of android.graphics.PointF that define the coordinates of the target region
+ * relative to the
+ * {@link android.hardware.camera2.CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE }.
+ * The array represents the target region coordinates as: top-left, top-right, bottom-left,
+ * bottom-right.</p>
+ * <p><b>Range of valid values:</b><br>
+ * The list of target coordinates will define a region within the bounds of the
+ * {@link android.hardware.camera2.CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE }</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ */
+ @PublicKey
+ @NonNull
+ @ExtensionKey
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final Key<android.graphics.PointF[]> EFV_TARGET_COORDINATES = CaptureResult.EFV_TARGET_COORDINATES;
+
+ /**
+ * <p>Used to apply an additional digital zoom factor for the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension in {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED } mode.</p>
+ * <p>For the {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * feature, an additional zoom factor is applied on top of the existing {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}.
+ * This additional zoom factor serves as a buffer to provide more flexibility for the
+ * {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED }
+ * mode. If {@link ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } is not set, the default will be used.
+ * The effectiveness of the stabilization may be influenced by the amount of padding zoom
+ * applied. A higher padding zoom factor can stabilize the target region more effectively
+ * with greater flexibility but may potentially impact image quality. Conversely, a lower
+ * padding zoom factor may be used to prioritize preserving image quality, albeit with less
+ * leeway in stabilizing the target region. It is recommended to set the
+ * {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } to at least 1.5.</p>
+ * <p>If {@link ExtensionCaptureRequest#EFV_AUTO_ZOOM } is enabled, the requested {@link ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } will be overridden.
+ * {@link ExtensionCaptureRequest#EFV_MAX_PADDING_ZOOM_FACTOR } can be checked for more details on controlling the
+ * padding zoom factor during {@link ExtensionCaptureRequest#EFV_AUTO_ZOOM }.</p>
+ * <p><b>Range of valid values:</b><br>
+ * {@link CameraExtensionCharacteristics#EFV_PADDING_ZOOM_FACTOR_RANGE }</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#CONTROL_ZOOM_RATIO
+ * @see ExtensionCaptureRequest#EFV_AUTO_ZOOM
+ * @see ExtensionCaptureRequest#EFV_MAX_PADDING_ZOOM_FACTOR
+ * @see ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR
+ * @see CameraExtensionCharacteristics#EFV_PADDING_ZOOM_FACTOR_RANGE
+ */
+ @PublicKey
+ @NonNull
+ @ExtensionKey
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final Key<Float> EFV_PADDING_ZOOM_FACTOR = CaptureResult.EFV_PADDING_ZOOM_FACTOR;
+
+ /**
+ * <p>Set the stabilization mode for the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension</p>
+ * <p>The desired stabilization mode. Gimbal stabilization mode provides simple, non-locked
+ * video stabilization. Locked mode uses the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * stabilization feature to fixate on the current region, utilizing it as the target area for
+ * stabilization.</p>
+ * <p><b>Possible values:</b></p>
+ * <ul>
+ * <li>{@link #EFV_STABILIZATION_MODE_OFF OFF}</li>
+ * <li>{@link #EFV_STABILIZATION_MODE_GIMBAL GIMBAL}</li>
+ * <li>{@link #EFV_STABILIZATION_MODE_LOCKED LOCKED}</li>
+ * </ul>
+ *
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * @see #EFV_STABILIZATION_MODE_OFF
+ * @see #EFV_STABILIZATION_MODE_GIMBAL
+ * @see #EFV_STABILIZATION_MODE_LOCKED
+ */
+ @PublicKey
+ @NonNull
+ @ExtensionKey
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final Key<Integer> EFV_STABILIZATION_MODE = CaptureResult.EFV_STABILIZATION_MODE;
+
+ /**
+ * <p>Used to enable or disable auto zoom for the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension in {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED } mode.</p>
+ * <p>Turn on auto zoom to let the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * feature decide at any given point a combination of
+ * {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} and {@link ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR }
+ * to keep the target region in view and stabilized. The combination chosen by the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * will equal the requested {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} multiplied with the requested
+ * {@link ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR }. A limit can be set on the padding zoom if wanting
+ * to control image quality further using {@link ExtensionCaptureRequest#EFV_MAX_PADDING_ZOOM_FACTOR }.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#CONTROL_ZOOM_RATIO
+ * @see ExtensionCaptureRequest#EFV_MAX_PADDING_ZOOM_FACTOR
+ * @see ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR
+ */
+ @PublicKey
+ @NonNull
+ @ExtensionKey
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final Key<Boolean> EFV_AUTO_ZOOM = CaptureResult.EFV_AUTO_ZOOM;
+
+ /**
+ * <p>Representing the desired clockwise rotation
+ * of the target region in degrees for the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension in {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED } mode.</p>
+ * <p>Value representing the desired clockwise rotation of the target
+ * region in degrees.</p>
+ * <p><b>Range of valid values:</b><br>
+ * 0 to 360</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ */
+ @PublicKey
+ @NonNull
+ @ExtensionKey
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final Key<Float> EFV_ROTATE_VIEWPORT = CaptureResult.EFV_ROTATE_VIEWPORT;
+
+ /**
+ * <p>Used to update the target region for the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension in {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED } mode.</p>
+ * <p>A android.util.Pair<Integer,Integer> that represents the desired
+ * <Horizontal,Vertical> shift of the current locked view (or target region) in
+ * pixels. Negative values indicate left and upward shifts, while positive values indicate
+ * right and downward shifts in the active array coordinate system.</p>
+ * <p><b>Range of valid values:</b><br>
+ * android.util.Pair<Integer,Integer> represents the
+ * <Horizontal,Vertical> shift. The range for the horizontal shift is
+ * [-max({@link ExtensionCaptureResult#EFV_PADDING_REGION }-left), max({@link ExtensionCaptureResult#EFV_PADDING_REGION }-right)].
+ * The range for the vertical shift is
+ * [-max({@link ExtensionCaptureResult#EFV_PADDING_REGION }-top), max({@link ExtensionCaptureResult#EFV_PADDING_REGION }-bottom)]</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see ExtensionCaptureResult#EFV_PADDING_REGION
+ */
+ @PublicKey
+ @NonNull
+ @ExtensionKey
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final Key<android.util.Pair<Integer,Integer>> EFV_TRANSLATE_VIEWPORT = CaptureResult.EFV_TRANSLATE_VIEWPORT;
+
+ /**
+ * <p>Used to limit the {@link ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } if
+ * {@link ExtensionCaptureRequest#EFV_AUTO_ZOOM } is enabled for the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension in {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED } mode.</p>
+ * <p>If {@link ExtensionCaptureRequest#EFV_AUTO_ZOOM } is enabled, this key can be used to set a limit
+ * on the {@link ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } chosen by the
+ * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
+ * extension in {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED } mode
+ * to control image quality.</p>
+ * <p><b>Range of valid values:</b><br>
+ * The range of {@link CameraExtensionCharacteristics#EFV_PADDING_ZOOM_FACTOR_RANGE }. Use a value greater than or equal to
+ * the {@link android.hardware.camera2.ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR } to
+ * effectively utilize this key.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see ExtensionCaptureRequest#EFV_AUTO_ZOOM
+ * @see ExtensionCaptureRequest#EFV_PADDING_ZOOM_FACTOR
+ * @see CameraExtensionCharacteristics#EFV_PADDING_ZOOM_FACTOR_RANGE
+ */
+ @PublicKey
+ @NonNull
+ @ExtensionKey
+ @FlaggedApi(Flags.FLAG_CONCERT_MODE)
+ public static final Key<Float> EFV_MAX_PADDING_ZOOM_FACTOR = CaptureResult.EFV_MAX_PADDING_ZOOM_FACTOR;
+
+} \ No newline at end of file
diff --git a/core/java/android/hardware/camera2/extension/CameraOutputSurface.java b/core/java/android/hardware/camera2/extension/CameraOutputSurface.java
index b4fe7fe1f0d1..53f56bc9f896 100644
--- a/core/java/android/hardware/camera2/extension/CameraOutputSurface.java
+++ b/core/java/android/hardware/camera2/extension/CameraOutputSurface.java
@@ -18,8 +18,11 @@ package android.hardware.camera2.extension;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.graphics.ImageFormat;
+import android.hardware.camera2.params.ColorSpaceProfiles;
+import android.hardware.camera2.params.DynamicRangeProfiles;
import android.hardware.camera2.utils.SurfaceUtils;
import android.util.Size;
import android.view.Surface;
@@ -65,6 +68,8 @@ public final class CameraOutputSurface {
mOutputSurface.size = new android.hardware.camera2.extension.Size();
mOutputSurface.size.width = size.getWidth();
mOutputSurface.size.height = size.getHeight();
+ mOutputSurface.dynamicRangeProfile = DynamicRangeProfiles.STANDARD;
+ mOutputSurface.colorSpace = ColorSpaceProfiles.UNSPECIFIED;
}
/**
@@ -95,4 +100,48 @@ public final class CameraOutputSurface {
public @ImageFormat.Format int getImageFormat() {
return mOutputSurface.imageFormat;
}
+
+ /**
+ * Return the dynamic range profile. The default
+ * dynamicRangeProfile is
+ * {@link android.hardware.camera2.params.DynamicRangeProfiles.STANDARD}
+ * unless specified by CameraOutputSurface.setDynamicRangeProfile.
+ */
+ @FlaggedApi(Flags.FLAG_EXTENSION_10_BIT)
+ public @DynamicRangeProfiles.Profile long getDynamicRangeProfile() {
+ return mOutputSurface.dynamicRangeProfile;
+ }
+
+ /**
+ * Return the color space. The default colorSpace is
+ * {@link android.hardware.camera2.params.ColorSpaceProfiles.UNSPECIFIED}
+ * unless specified by CameraOutputSurface.setColorSpace.
+ */
+ @SuppressLint("MethodNameUnits")
+ @FlaggedApi(Flags.FLAG_EXTENSION_10_BIT)
+ public int getColorSpace() {
+ return mOutputSurface.colorSpace;
+ }
+
+ /**
+ * Set the dynamic range profile. The default dynamicRangeProfile
+ * will be {@link android.hardware.camera2.params.DynamicRangeProfiles.STANDARD}
+ * unless explicitly set using this method.
+ */
+ @FlaggedApi(Flags.FLAG_EXTENSION_10_BIT)
+ public void setDynamicRangeProfile(
+ @DynamicRangeProfiles.Profile long dynamicRangeProfile) {
+ mOutputSurface.dynamicRangeProfile = dynamicRangeProfile;
+ }
+
+ /**
+ * Set the color space. The default colorSpace
+ * will be
+ * {@link android.hardware.camera2.params.ColorSpaceProfiles.UNSPECIFIED}
+ * unless explicitly set using this method.
+ */
+ @FlaggedApi(Flags.FLAG_EXTENSION_10_BIT)
+ public void setColorSpace(int colorSpace) {
+ mOutputSurface.colorSpace = colorSpace;
+ }
}
diff --git a/core/java/android/hardware/camera2/extension/ICaptureCallback.aidl b/core/java/android/hardware/camera2/extension/ICaptureCallback.aidl
index 02a4690c3b77..b1f10ee103c1 100644
--- a/core/java/android/hardware/camera2/extension/ICaptureCallback.aidl
+++ b/core/java/android/hardware/camera2/extension/ICaptureCallback.aidl
@@ -15,6 +15,7 @@
*/
package android.hardware.camera2.extension;
+import android.hardware.camera2.extension.CaptureFailure;
import android.hardware.camera2.extension.Request;
import android.hardware.camera2.impl.CameraMetadataNative;
@@ -28,4 +29,5 @@ interface ICaptureCallback
void onCaptureSequenceAborted(int captureSequenceId);
void onCaptureCompleted(long shutterTimestamp, int requestId, in CameraMetadataNative results);
void onCaptureProcessProgressed(int progress);
+ void onCaptureProcessFailed(int captureSequenceId, int captureFailureReason);
}
diff --git a/core/java/android/hardware/camera2/extension/OutputSurface.aidl b/core/java/android/hardware/camera2/extension/OutputSurface.aidl
index 841537918f65..02e160cdd8dd 100644
--- a/core/java/android/hardware/camera2/extension/OutputSurface.aidl
+++ b/core/java/android/hardware/camera2/extension/OutputSurface.aidl
@@ -24,4 +24,6 @@ parcelable OutputSurface
Surface surface;
Size size;
int imageFormat;
+ long dynamicRangeProfile;
+ int colorSpace;
}
diff --git a/core/java/android/hardware/camera2/extension/SessionProcessor.java b/core/java/android/hardware/camera2/extension/SessionProcessor.java
index e7cc5303fc18..2e428e5aa5f9 100644
--- a/core/java/android/hardware/camera2/extension/SessionProcessor.java
+++ b/core/java/android/hardware/camera2/extension/SessionProcessor.java
@@ -20,6 +20,7 @@ import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.impl.CameraExtensionUtils.HandlerExecutor;
@@ -132,13 +133,14 @@ public abstract class SessionProcessor {
* This method is called instead of
* {@link #onCaptureProcessStarted} when the camera device failed
* to produce the required input for the device-specific
- * extension. The cause could be a failed camera capture request,
- * a failed capture result or dropped camera frame.
+ * extension. The callback allows clients to be notified
+ * about failure reason.
*
* @param captureSequenceId id of the current capture sequence
+ * @param failure The capture failure reason
*/
@FlaggedApi(Flags.FLAG_CONCERT_MODE)
- void onCaptureFailed(int captureSequenceId);
+ void onCaptureFailed(int captureSequenceId, @CaptureFailure.FailureReason int failure);
/**
* This method is called independently of the others in the
@@ -181,8 +183,8 @@ public abstract class SessionProcessor {
* capture results. This is the return value of
* either {@link #startRepeating} or {@link
* #startMultiFrameCapture}.
- * @param results The supported capture results. Do note
- * that if results 'android.jpeg.quality' and
+ * @param results Key value map of the supported capture results.
+ * Do note that if results 'android.jpeg.quality' and
* android.jpeg.orientation' are present in the
* process capture input results, then the values
* must also be passed as part of this callback.
@@ -192,7 +194,7 @@ public abstract class SessionProcessor {
*/
@FlaggedApi(Flags.FLAG_CONCERT_MODE)
void onCaptureCompleted(long shutterTimestamp, int requestId,
- @NonNull CaptureResult results);
+ @NonNull Map<CaptureResult.Key, Object> results);
}
/**
@@ -412,7 +414,7 @@ public abstract class SessionProcessor {
public int startRepeating(ICaptureCallback callback) throws RemoteException {
return SessionProcessor.this.startRepeating(
new HandlerExecutor(new Handler(Looper.getMainLooper())),
- new CaptureCallbackImpl(callback));
+ new CaptureCallbackImpl(callback, mVendorId));
}
@Override
@@ -425,7 +427,7 @@ public abstract class SessionProcessor {
throws RemoteException {
return SessionProcessor.this.startMultiFrameCapture(
new HandlerExecutor(new Handler(Looper.getMainLooper())),
- new CaptureCallbackImpl(callback));
+ new CaptureCallbackImpl(callback, mVendorId));
}
@Override
@@ -438,7 +440,7 @@ public abstract class SessionProcessor {
throws RemoteException {
return SessionProcessor.this.startTrigger(captureRequest,
new HandlerExecutor(new Handler(Looper.getMainLooper())),
- new CaptureCallbackImpl(callback));
+ new CaptureCallbackImpl(callback, mVendorId));
}
@Override
@@ -450,9 +452,11 @@ public abstract class SessionProcessor {
private static final class CaptureCallbackImpl implements CaptureCallback {
private final ICaptureCallback mCaptureCallback;
+ private long mVendorId = -1;
- CaptureCallbackImpl(@NonNull ICaptureCallback cb) {
+ CaptureCallbackImpl(@NonNull ICaptureCallback cb, long vendorId) {
mCaptureCallback = cb;
+ mVendorId = vendorId;
}
@Override
@@ -474,9 +478,9 @@ public abstract class SessionProcessor {
}
@Override
- public void onCaptureFailed(int captureSequenceId) {
+ public void onCaptureFailed(int captureSequenceId, int failure) {
try {
- mCaptureCallback.onCaptureFailed(captureSequenceId);
+ mCaptureCallback.onCaptureProcessFailed(captureSequenceId, failure);
} catch (RemoteException e) {
Log.e(TAG, "Failed to notify capture failure start due to remote exception!");
}
@@ -502,10 +506,14 @@ public abstract class SessionProcessor {
@Override
public void onCaptureCompleted(long shutterTimestamp, int requestId,
- @androidx.annotation.NonNull CaptureResult results) {
+ Map<CaptureResult.Key, Object> results) {
+ CameraMetadataNative captureResults = new CameraMetadataNative();
+ captureResults.setVendorId(mVendorId);
+ for (Map.Entry<CaptureResult.Key, Object> entry : results.entrySet()) {
+ captureResults.set(entry.getKey(), entry.getValue());
+ }
try {
- mCaptureCallback.onCaptureCompleted(shutterTimestamp, requestId,
- results.getNativeCopy());
+ mCaptureCallback.onCaptureCompleted(shutterTimestamp, requestId, captureResults);
} catch (RemoteException e) {
Log.e(TAG, "Failed to notify capture complete due to remote exception!");
}
diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
index b2032fa3db81..a7d6caf9d9df 100644
--- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
@@ -66,6 +66,8 @@ import android.util.Log;
import android.util.Size;
import android.view.Surface;
+import com.android.internal.camera.flags.Flags;
+
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
@@ -779,6 +781,22 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes
}
@Override
+ public void onCaptureProcessFailed(int captureSequenceId, int captureFailureReason) {
+ if (Flags.concertMode()) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mClientExecutor.execute(
+ () -> mClientCallbacks.onCaptureFailed(
+ CameraAdvancedExtensionSessionImpl.this, mClientRequest,
+ captureFailureReason
+ ));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ @Override
public void onCaptureSequenceCompleted(int captureSequenceId) {
final long ident = Binder.clearCallingIdentity();
try {
diff --git a/core/java/android/hardware/camera2/impl/ExtensionKey.java b/core/java/android/hardware/camera2/impl/ExtensionKey.java
new file mode 100644
index 000000000000..15e8982c12b4
--- /dev/null
+++ b/core/java/android/hardware/camera2/impl/ExtensionKey.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 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.hardware.camera2.impl;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Denote a static field {@code Key} as being an extension key (i.e. @hide as a CaptureRequest/
+ * CaptureResult key but exposed as a @PublicKey through
+ * ExtensionCaptureRequest/ExtensionCaptureResult).
+ *
+ * <p>Keys with this annotation are assumed to always have a hidden key counter-part in
+ * CaptureRequest/CaptureResult.</p>
+ *
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface ExtensionKey {
+
+}
diff --git a/core/java/android/hardware/camera2/params/ExtensionSessionConfiguration.java b/core/java/android/hardware/camera2/params/ExtensionSessionConfiguration.java
index 0e6c1b39271c..69a6e9b0ab32 100644
--- a/core/java/android/hardware/camera2/params/ExtensionSessionConfiguration.java
+++ b/core/java/android/hardware/camera2/params/ExtensionSessionConfiguration.java
@@ -15,15 +15,20 @@
*/
package android.hardware.camera2.params;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
-
+import android.annotation.SuppressLint;
+import android.graphics.ColorSpace;
+import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraExtensionCharacteristics.Extension;
import android.hardware.camera2.CameraExtensionSession;
import java.util.List;
import java.util.concurrent.Executor;
+import com.android.internal.camera.flags.Flags;
+
/**
* A class that aggregates all supported arguments for
* {@link CameraExtensionSession} initialization.
@@ -36,6 +41,7 @@ public final class ExtensionSessionConfiguration {
private OutputConfiguration mPostviewOutput = null;
private Executor mExecutor = null;
private CameraExtensionSession.StateCallback mCallback = null;
+ private int mColorSpace;
/**
* Create a new ExtensionSessionConfiguration
@@ -118,4 +124,55 @@ public final class ExtensionSessionConfiguration {
Executor getExecutor() {
return mExecutor;
}
+
+ /**
+ * Set a specific device-supported color space.
+ *
+ * <p>Clients can choose from any profile advertised as supported in
+ * {@link CameraCharacteristics#REQUEST_AVAILABLE_COLOR_SPACE_PROFILES}
+ * queried using {@link ColorSpaceProfiles#getSupportedColorSpaces}.
+ * When set, the colorSpace will override the default color spaces of the output targets,
+ * or the color space implied by the dataSpace passed into an {@link ImageReader}'s
+ * constructor.</p>
+ */
+ @FlaggedApi(Flags.FLAG_EXTENSION_10_BIT)
+ public void setColorSpace(@NonNull ColorSpace.Named colorSpace) {
+ mColorSpace = colorSpace.ordinal();
+ for (OutputConfiguration outputConfiguration : mOutputs) {
+ outputConfiguration.setColorSpace(colorSpace);
+ }
+ if (mPostviewOutput != null) {
+ mPostviewOutput.setColorSpace(colorSpace);
+ }
+ }
+
+ /**
+ * Clear the color space, such that the default color space will be used.
+ */
+ @FlaggedApi(Flags.FLAG_EXTENSION_10_BIT)
+ public void clearColorSpace() {
+ mColorSpace = ColorSpaceProfiles.UNSPECIFIED;
+ for (OutputConfiguration outputConfiguration : mOutputs) {
+ outputConfiguration.clearColorSpace();
+ }
+ if (mPostviewOutput != null) {
+ mPostviewOutput.clearColorSpace();
+ }
+ }
+
+ /**
+ * Return the current color space.
+ *
+ * @return the currently set color space, or null
+ * if not set
+ */
+ @FlaggedApi(Flags.FLAG_EXTENSION_10_BIT)
+ @SuppressLint("MethodNameUnits")
+ public @Nullable ColorSpace getColorSpace() {
+ if (mColorSpace != ColorSpaceProfiles.UNSPECIFIED) {
+ return ColorSpace.get(ColorSpace.Named.values()[mColorSpace]);
+ } else {
+ return null;
+ }
+ }
}
diff --git a/core/java/android/hardware/devicestate/DeviceState.java b/core/java/android/hardware/devicestate/DeviceState.java
index 5a349050a28f..e35e80126388 100644
--- a/core/java/android/hardware/devicestate/DeviceState.java
+++ b/core/java/android/hardware/devicestate/DeviceState.java
@@ -16,18 +16,25 @@
package android.hardware.devicestate;
-import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
-import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
+import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE_IDENTIFIER;
+import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE_IDENTIFIER;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
import com.android.internal.util.Preconditions;
+import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.Collections;
import java.util.Objects;
+import java.util.Set;
/**
* A state of the device defined by the {@link DeviceStateProvider} and managed by the
@@ -37,21 +44,29 @@ import java.util.Objects;
* state of the system. This is useful for variable-state devices, like foldable or rollable
* devices, that can be configured by users into differing hardware states, which each may have a
* different expected use case.
- * @hide
*
+ * @hide
* @see DeviceStateManager
*/
+@SystemApi
+@FlaggedApi(android.hardware.devicestate.feature.flags.Flags.FLAG_DEVICE_STATE_PROPERTY_API)
public final class DeviceState {
/**
* Flag that indicates override requests should be cancelled when this device state becomes the
* base device state.
+ * @hide
+ * @deprecated use {@link #PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS}
*/
+ @Deprecated
public static final int FLAG_CANCEL_OVERRIDE_REQUESTS = 1 << 0;
/**
* Flag that indicates this device state is inaccessible for applications to be placed in. This
- * could be a device-state where the {@link DEFAULT_DISPLAY} is not enabled.
+ * could be a device-state where the {@link Display#DEFAULT_DISPLAY} is not enabled.
+ * @hide
+ * @deprecated use {@link #PROPERTY_APP_INACCESSIBLE}
*/
+ @Deprecated
public static final int FLAG_APP_INACCESSIBLE = 1 << 1;
/**
@@ -60,7 +75,10 @@ public final class DeviceState {
* through emulation and have no physical configuration to match.
*
* This flag indicates that the corresponding state can only be entered through emulation.
+ * @hide
+ * @deprecated use {@link #PROPERTY_EMULATED_ONLY}
*/
+ @Deprecated
public static final int FLAG_EMULATED_ONLY = 1 << 2;
/**
@@ -68,19 +86,28 @@ public final class DeviceState {
* requesting app is no longer on top. The app is considered not on top when (1) the top
* activity in the system is from a different app, (2) the device is in sleep mode, or
* (3) the keyguard shows up.
+ * @hide
+ * @deprecated use {@link #PROPERTY_POLICY_CANCEL_WHEN_REQUESTER_NOT_ON_TOP}
*/
+ @Deprecated
public static final int FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP = 1 << 3;
/**
* This flag indicates that the corresponding state should be disabled when the device is
* overheating and reaching the critical status.
+ * @hide
+ * @deprecated use {@link #PROPERTY_POLICY_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL}
*/
+ @Deprecated
public static final int FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL = 1 << 4;
/**
* This flag indicates that the corresponding state should be disabled when power save mode
* is enabled.
+ * @hide
+ * @deprecated use {@link #PROPERTY_POLICY_UNSUPPORTED_WHEN_POWER_SAVE_MODE}
*/
+ @Deprecated
public static final int FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE = 1 << 5;
/** @hide */
@@ -92,11 +119,157 @@ public final class DeviceState {
FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL,
FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE
})
+ @Deprecated
@Retention(RetentionPolicy.SOURCE)
public @interface DeviceStateFlags {}
+ /**
+ * Property that indicates that a fold-in style foldable device is currently in a fully closed
+ * configuration.
+ */
+ public static final int PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED = 1;
+
+ /**
+ * Property that indicates that a fold-in style foldable device is currently in a half-opened
+ * configuration. This signifies that the device's hinge is positioned somewhere around 90
+ * degrees. Checking for display configuration properties as well can provide information
+ * on which display is currently active.
+ */
+ public static final int PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN = 2;
+
+ /**
+ * Property that indicates that a fold-in style foldable device is currently in a fully open
+ * configuration.
+ */
+ public static final int PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN = 3;
+
+ /**
+ * Property that indicates override requests should be cancelled when the device is physically
+ * put into this state.
+ * @hide
+ */
+ public static final int PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS = 4;
+
+ /**
+ * This property indicates that the corresponding state should be automatically canceled when
+ * the requesting app is no longer on top. The app is considered not on top when (1) the top
+ * activity in the system is from a different app, (2) the device is in sleep mode, or
+ * (3) the keyguard shows up.
+ * @hide
+ */
+ public static final int PROPERTY_POLICY_CANCEL_WHEN_REQUESTER_NOT_ON_TOP = 5;
+
+ /**
+ * This property indicates that the corresponding state should be disabled when the device is
+ * overheating and reaching the critical status.
+ * @hide
+ */
+ public static final int PROPERTY_POLICY_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL = 6;
+
+ /**
+ * This property indicates that the corresponding state should be disabled when power save mode
+ * is enabled.
+ * @hide
+ */
+ public static final int PROPERTY_POLICY_UNSUPPORTED_WHEN_POWER_SAVE_MODE = 7;
+
+ /**
+ * This property denotes that this state is available for applications to request and the system
+ * server should deny any request that comes from a process that does not hold the
+ * CONTROL_DEVICE_STATE permission if it is requesting a state that does not have this property
+ * on it.
+ * @hide
+ */
+ @TestApi
+ public static final int PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST = 8;
+
+ /**
+ * Property that indicates this device state is inaccessible for applications to be made
+ * visible to the user. This could be a device-state where the {@link Display#DEFAULT_DISPLAY}
+ * is not enabled.
+ * @hide
+ */
+ public static final int PROPERTY_APP_INACCESSIBLE = 9;
+
+ /**
+ * This property indidcates that this state can only be entered through emulation and has no
+ * physical configuration to match.
+ */
+ public static final int PROPERTY_EMULATED_ONLY = 10;
+
+ /**
+ * Property that indicates that the outer display area of a foldable device is currently the
+ * primary display area.
+ *
+ * Note: This does not necessarily mean that the outer display area is the
+ * {@link Display#DEFAULT_DISPLAY}.
+ */
+ public static final int PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY = 11;
+
+ /**
+ * Property that indicates that the inner display area of a foldable device is currently the
+ * primary display area.
+ *
+ * Note: This does not necessarily mean that the inner display area is the
+ * {@link Display#DEFAULT_DISPLAY}.
+ */
+ public static final int PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY = 12;
+
+ /**
+ * Property that indicates that this device state will attempt to trigger the device to go to
+ * sleep.
+ */
+ public static final int PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP = 13;
+
+ /**
+ * Property that indicates that this device state will attempt to trigger the device to wake up.
+ */
+ public static final int PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE = 14;
+
+ /**
+ * Property that indicates that an external display has been connected to the device. Specifics
+ * around display mode or properties around the display should be gathered through
+ * {@link android.hardware.display.DisplayManager}
+ */
+ public static final int PROPERTY_EXTENDED_DEVICE_STATE_EXTERNAL_DISPLAY = 15;
+ /**
+ * Property that indicates that this state corresponds to the device state for rear display
+ * mode. This means that the active display is facing the same direction as the rear camera.
+ */
+ public static final int PROPERTY_FEATURE_REAR_DISPLAY = 16;
+
+ /**
+ * Property that indicates that this state corresponds to the device state where both displays
+ * on a foldable are active, with the internal display being the default display.
+ */
+ public static final int PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT = 17;
+
+ /** @hide */
+ @IntDef(prefix = {"PROPERTY_"}, flag = true, value = {
+ PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED,
+ PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN,
+ PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN,
+ PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS,
+ PROPERTY_POLICY_CANCEL_WHEN_REQUESTER_NOT_ON_TOP,
+ PROPERTY_POLICY_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL,
+ PROPERTY_POLICY_UNSUPPORTED_WHEN_POWER_SAVE_MODE,
+ PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST,
+ PROPERTY_APP_INACCESSIBLE,
+ PROPERTY_EMULATED_ONLY,
+ PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY,
+ PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY,
+ PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP,
+ PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE,
+ PROPERTY_EXTENDED_DEVICE_STATE_EXTERNAL_DISPLAY,
+ PROPERTY_FEATURE_REAR_DISPLAY,
+ PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
+ public @interface DeviceStateProperties {}
+
/** Unique identifier for the device state. */
- @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE)
+ @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to = MAXIMUM_DEVICE_STATE_IDENTIFIER)
private final int mIdentifier;
/** String description of the device state. */
@@ -106,20 +279,49 @@ public final class DeviceState {
@DeviceStateFlags
private final int mFlags;
+ private final Set<@DeviceStateProperties Integer> mProperties;
+
+ /**
+ * @deprecated Deprecated in favor of {@link #DeviceState(int, String, Set)}
+ * @hide
+ */
+ // TODO(b/325124054): Make non-default and remove deprecated callback methods.
+ @TestApi
+ @Deprecated
public DeviceState(
- @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier,
+ @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to =
+ MAXIMUM_DEVICE_STATE_IDENTIFIER) int identifier,
@NonNull String name,
@DeviceStateFlags int flags) {
- Preconditions.checkArgumentInRange(identifier, MINIMUM_DEVICE_STATE, MAXIMUM_DEVICE_STATE,
+ Preconditions.checkArgumentInRange(identifier, MINIMUM_DEVICE_STATE_IDENTIFIER,
+ MAXIMUM_DEVICE_STATE_IDENTIFIER,
"identifier");
mIdentifier = identifier;
mName = name;
mFlags = flags;
+ mProperties = Collections.emptySet();
+ }
+
+ /** @hide */
+ @TestApi
+ public DeviceState(
+ @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to =
+ MAXIMUM_DEVICE_STATE_IDENTIFIER) int identifier,
+ @NonNull String name,
+ @NonNull Set<@DeviceStateProperties Integer> properties) {
+ Preconditions.checkArgumentInRange(identifier, MINIMUM_DEVICE_STATE_IDENTIFIER,
+ MAXIMUM_DEVICE_STATE_IDENTIFIER,
+ "identifier");
+
+ mIdentifier = identifier;
+ mName = name;
+ mProperties = Set.copyOf(properties);
+ mFlags = 0;
}
/** Returns the unique identifier for the device state. */
- @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE)
+ @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER)
public int getIdentifier() {
return mIdentifier;
}
@@ -130,6 +332,12 @@ public final class DeviceState {
return mName;
}
+ /**
+ * @hide
+ * @deprecated in favor of {@link #hasProperty(int)} method
+ */
+ // TODO(b/325124054): Make non-default and remove deprecated callback methods.
+ @Deprecated
@DeviceStateFlags
public int getFlags() {
return mFlags;
@@ -138,9 +346,9 @@ public final class DeviceState {
@Override
public String toString() {
return "DeviceState{" + "identifier=" + mIdentifier + ", name='" + mName + '\''
- + ", app_accessible=" + !hasFlag(FLAG_APP_INACCESSIBLE)
+ + ", app_accessible=" + !hasProperty(PROPERTY_APP_INACCESSIBLE)
+ ", cancel_when_requester_not_on_top="
- + hasFlag(FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP)
+ + hasProperty(PROPERTY_POLICY_CANCEL_WHEN_REQUESTER_NOT_ON_TOP)
+ "}";
}
@@ -151,17 +359,40 @@ public final class DeviceState {
DeviceState that = (DeviceState) o;
return mIdentifier == that.mIdentifier
&& Objects.equals(mName, that.mName)
- && mFlags == that.mFlags;
+ && Objects.equals(mProperties, that.mProperties);
}
@Override
public int hashCode() {
- return Objects.hash(mIdentifier, mName, mFlags);
+ return Objects.hash(mIdentifier, mName, mProperties);
}
/** Checks if a specific flag is set
+ * @hide
+ * @deprecated in favor of {@link #hasProperty(int)}
*/
+ // TODO(b/325124054): Make non-default and remove deprecated callback methods.
+ @Deprecated
public boolean hasFlag(int flagToCheckFor) {
return (mFlags & flagToCheckFor) == flagToCheckFor;
}
+
+ /**
+ * Checks if a specific property is set on this state
+ */
+ public boolean hasProperty(@DeviceStateProperties int propertyToCheckFor) {
+ return mProperties.contains(propertyToCheckFor);
+ }
+
+ /**
+ * Checks if a list of properties are all set on this state
+ */
+ public boolean hasProperties(@NonNull @DeviceStateProperties int... properties) {
+ for (int i = 0; i < properties.length; i++) {
+ if (mProperties.contains(properties[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
}
diff --git a/core/java/android/hardware/devicestate/DeviceStateManager.java b/core/java/android/hardware/devicestate/DeviceStateManager.java
index 6a667fe39974..8b4d43ed651f 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManager.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManager.java
@@ -18,15 +18,19 @@ package android.hardware.devicestate;
import android.Manifest;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.content.Context;
import com.android.internal.util.ArrayUtils;
+import java.util.List;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -36,7 +40,8 @@ import java.util.function.Consumer;
*
* @hide
*/
-@TestApi
+@SystemApi
+@FlaggedApi(android.hardware.devicestate.feature.flags.Flags.FLAG_DEVICE_STATE_PROPERTY_API)
@SystemService(Context.DEVICE_STATE_SERVICE)
public final class DeviceStateManager {
/**
@@ -46,11 +51,19 @@ public final class DeviceStateManager {
*/
public static final int INVALID_DEVICE_STATE = -1;
- /** The minimum allowed device state identifier. */
- public static final int MINIMUM_DEVICE_STATE = 0;
+ /**
+ * The minimum allowed device state identifier.
+ * @hide
+ */
+ @TestApi
+ public static final int MINIMUM_DEVICE_STATE_IDENTIFIER = 0;
- /** The maximum allowed device state identifier. */
- public static final int MAXIMUM_DEVICE_STATE = 255;
+ /**
+ * The maximum allowed device state identifier.
+ * @hide
+ */
+ @TestApi
+ public static final int MAXIMUM_DEVICE_STATE_IDENTIFIER = 10000;
/**
* Intent needed to launch the rear display overlay activity from SysUI
@@ -83,13 +96,27 @@ public final class DeviceStateManager {
/**
* Returns the list of device states that are supported and can be requested with
* {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
+ * @deprecated use {@link #getSupportedDeviceStates()}
+ * @hide
*/
+ // TODO(b/325124054): Make non-default and remove deprecated callback methods.
+ @TestApi
+ @Deprecated
@NonNull
public int[] getSupportedStates() {
return mGlobal.getSupportedStates();
}
/**
+ * Returns the list of device states that are supported and can be requested with
+ * {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
+ */
+ @NonNull
+ public List<DeviceState> getSupportedDeviceStates() {
+ return mGlobal.getSupportedDeviceStates();
+ }
+
+ /**
* Submits a {@link DeviceStateRequest request} to modify the device state.
* <p>
* By default, the request is kept active until one of the following occurs:
@@ -107,7 +134,10 @@ public final class DeviceStateManager {
* the {@link android.Manifest.permission#CONTROL_DEVICE_STATE} permission is held.
*
* @see DeviceStateRequest
+ * @hide
*/
+ @SuppressLint("RequiresPermission") // Lint doesn't handle conditional permission checks today
+ @TestApi
@RequiresPermission(value = android.Manifest.permission.CONTROL_DEVICE_STATE,
conditional = true)
public void requestState(@NonNull DeviceStateRequest request,
@@ -124,7 +154,10 @@ public final class DeviceStateManager {
*
* @throws SecurityException if the caller is neither the current top-focused activity nor if
* the {@link android.Manifest.permission#CONTROL_DEVICE_STATE} permission is held.
+ * @hide
*/
+ @SuppressLint("RequiresPermission") // Lint doesn't handle conditional permission checks today
+ @TestApi
@RequiresPermission(value = android.Manifest.permission.CONTROL_DEVICE_STATE,
conditional = true)
public void cancelStateRequest() {
@@ -151,11 +184,11 @@ public final class DeviceStateManager {
* emulated override requests take priority.
*
* @throws IllegalArgumentException if the requested state is unsupported.
- * @throws SecurityException if the caller does not hold the
- * {@link android.Manifest.permission#CONTROL_DEVICE_STATE} permission.
*
* @see DeviceStateRequest
+ * @hide
*/
+ @TestApi
@RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)
public void requestBaseStateOverride(@NonNull DeviceStateRequest request,
@Nullable @CallbackExecutor Executor executor,
@@ -169,9 +202,9 @@ public final class DeviceStateManager {
* <p>
* This method is noop if there is no base state request currently active.
*
- * @throws SecurityException if the caller does not hold the
- * {@link android.Manifest.permission#CONTROL_DEVICE_STATE} permission.
+ * @hide
*/
+ @TestApi
@RequiresPermission(Manifest.permission.CONTROL_DEVICE_STATE)
public void cancelBaseStateOverride() {
mGlobal.cancelBaseStateOverride();
@@ -209,10 +242,31 @@ public final class DeviceStateManager {
* @param supportedStates the new supported states.
*
* @see DeviceStateManager#getSupportedStates()
+ * @deprecated use {@link #onSupportedStatesChanged(List)}
+ * @hide
*/
+ // TODO(b/325124054): Make non-default and remove deprecated callback methods.
+ @TestApi
+ @Deprecated
default void onSupportedStatesChanged(@NonNull int[] supportedStates) {}
/**
+ * Called in response to a change in the states supported by the device.
+ * <p>
+ * Guaranteed to be called once on registration of the callback with the initial value and
+ * then on every subsequent change in the supported states.
+ *
+ * The supported device states may change due to certain states becoming unavailable
+ * due to device configuration or device conditions such as if the device is too hot or
+ * external monitors have been connected.
+ *
+ * @param supportedStates the new supported states.
+ *
+ * @see DeviceStateManager#getSupportedDeviceStates()
+ */
+ default void onSupportedStatesChanged(@NonNull List<DeviceState> supportedStates) {}
+
+ /**
* Called in response to a change in the base device state.
* <p>
* The base state is the state of the device without considering any requests made through
@@ -224,7 +278,13 @@ public final class DeviceStateManager {
* then on every subsequent change in the non-override state.
*
* @param state the new base device state.
+ * @deprecated use {@link #onDeviceStateChanged(DeviceState)} and query for physical
+ * properties that are relevant to your needs.
+ * @hide
*/
+ // TODO(b/325124054): Make non-default and remove deprecated callback methods.
+ @TestApi
+ @Deprecated
default void onBaseStateChanged(int state) {}
/**
@@ -234,8 +294,24 @@ public final class DeviceStateManager {
* then on every subsequent change in device state.
*
* @param state the new device state.
+ * @deprecated use {@link #onDeviceStateChanged(DeviceState)}
+ * @hide
*/
+ // TODO(b/325124054): Make non-default and remove deprecated callback methods.
+ @TestApi
+ @Deprecated
void onStateChanged(int state);
+
+ /**
+ * Called in response to device state changes.
+ * <p>
+ * Guaranteed to be called once on registration of the callback with the initial value and
+ * then on every subsequent change in device state.
+ *
+ * @param state the new device state.
+ */
+ // TODO(b/325124054): Make non-default and remove deprecated callback methods.
+ default void onDeviceStateChanged(@NonNull DeviceState state) {}
}
/**
@@ -247,6 +323,7 @@ public final class DeviceStateManager {
public static class FoldStateListener implements DeviceStateCallback {
private final int[] mFoldedDeviceStates;
private final Consumer<Boolean> mDelegate;
+ private final android.hardware.devicestate.feature.flags.FeatureFlags mFeatureFlags;
@Nullable
private Boolean lastResult;
@@ -259,11 +336,23 @@ public final class DeviceStateManager {
mFoldedDeviceStates = context.getResources().getIntArray(
com.android.internal.R.array.config_foldedDeviceStates);
mDelegate = listener;
+ mFeatureFlags = new android.hardware.devicestate.feature.flags.FeatureFlagsImpl();
}
@Override
- public final void onStateChanged(int state) {
- final boolean folded = ArrayUtils.contains(mFoldedDeviceStates, state);
+ public final void onStateChanged(int state) {}
+
+ @Override
+ public final void onDeviceStateChanged(@NonNull DeviceState deviceState) {
+ final boolean folded;
+ if (mFeatureFlags.deviceStatePropertyApi()) {
+ // TODO(b/325124054): Update when system server refactor is completed
+ folded = deviceState.hasProperty(
+ DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY)
+ || ArrayUtils.contains(mFoldedDeviceStates, deviceState.getIdentifier());
+ } else {
+ folded = ArrayUtils.contains(mFoldedDeviceStates, deviceState.getIdentifier());
+ }
if (lastResult == null || !lastResult.equals(folded)) {
lastResult = folded;
diff --git a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
index c3791888b44f..6db5aee106a8 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
@@ -19,6 +19,7 @@ package android.hardware.devicestate;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.app.ActivityThread;
import android.content.Context;
import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
import android.os.Binder;
@@ -32,9 +33,13 @@ import android.util.ArrayMap;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.internal.util.ArrayUtils;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
import java.util.concurrent.Executor;
/**
@@ -50,12 +55,18 @@ public final class DeviceStateManagerGlobal {
private static final String TAG = "DeviceStateManagerGlobal";
private static final boolean DEBUG = Build.IS_DEBUGGABLE;
+ // TODO(b/325124054): Remove when system server refactor is completed
+ private static int[] sFoldedDeviceStates = new int[0];
+
/**
* Returns an instance of {@link DeviceStateManagerGlobal}. May return {@code null} if a
* connection with the device state service couldn't be established.
*/
@Nullable
public static DeviceStateManagerGlobal getInstance() {
+ // TODO(b/325124054): Remove when system server refactor is completed
+ instantiateFoldedStateArray();
+
synchronized (DeviceStateManagerGlobal.class) {
if (sInstance == null) {
IBinder b = ServiceManager.getService(Context.DEVICE_STATE_SERVICE);
@@ -68,6 +79,16 @@ public final class DeviceStateManagerGlobal {
}
}
+ // TODO(b/325124054): Remove when system server refactor is completed
+ // TODO(b/325330654): Investigate if we need a Context passed in to DSMGlobal
+ private static void instantiateFoldedStateArray() {
+ Context context = ActivityThread.currentApplication();
+ if (context != null) {
+ sFoldedDeviceStates = context.getResources().getIntArray(
+ com.android.internal.R.array.config_foldedDeviceStates);
+ }
+ }
+
private final Object mLock = new Object();
@NonNull
private final IDeviceStateManager mDeviceStateManager;
@@ -116,6 +137,32 @@ public final class DeviceStateManagerGlobal {
}
/**
+ * Returns the {@link List} of supported device states.
+ *
+ * @see DeviceStateManager#getSupportedDeviceStates()
+ */
+ public List<DeviceState> getSupportedDeviceStates() {
+ synchronized (mLock) {
+ final DeviceStateInfo currentInfo;
+ if (mLastReceivedInfo != null) {
+ // If we have mLastReceivedInfo a callback is registered for this instance and it
+ // is receiving the most recent info from the server. Use that info here.
+ currentInfo = mLastReceivedInfo;
+ } else {
+ // If mLastReceivedInfo is null there is no registered callback so we manually
+ // fetch the current info.
+ try {
+ currentInfo = mDeviceStateManager.getDeviceStateInfo();
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ return createDeviceStateList(currentInfo.supportedStates);
+ }
+ }
+
+ /**
* Submits a {@link DeviceStateRequest request} to modify the device state.
*
* @see DeviceStateManager#requestState(DeviceStateRequest, Executor,
@@ -241,8 +288,10 @@ public final class DeviceStateManagerGlobal {
final int[] supportedStates = Arrays.copyOf(mLastReceivedInfo.supportedStates,
mLastReceivedInfo.supportedStates.length);
wrapper.notifySupportedStatesChanged(supportedStates);
+ wrapper.notifySupportedDeviceStatesChanged(createDeviceStateList(supportedStates));
wrapper.notifyBaseStateChanged(mLastReceivedInfo.baseState);
wrapper.notifyStateChanged(mLastReceivedInfo.currentState);
+ wrapper.notifyDeviceStateChanged(createDeviceState(mLastReceivedInfo.currentState));
}
}
}
@@ -327,6 +376,8 @@ public final class DeviceStateManagerGlobal {
final int[] supportedStates = Arrays.copyOf(info.supportedStates,
info.supportedStates.length);
callbacks.get(i).notifySupportedStatesChanged(supportedStates);
+ callbacks.get(i).notifySupportedDeviceStatesChanged(
+ createDeviceStateList(supportedStates));
}
}
if ((diff & DeviceStateInfo.CHANGED_BASE_STATE) > 0) {
@@ -337,6 +388,7 @@ public final class DeviceStateManagerGlobal {
if ((diff & DeviceStateInfo.CHANGED_CURRENT_STATE) > 0) {
for (int i = 0; i < callbacks.size(); i++) {
callbacks.get(i).notifyStateChanged(info.currentState);
+ callbacks.get(i).notifyDeviceStateChanged(createDeviceState(info.currentState));
}
}
}
@@ -369,6 +421,36 @@ public final class DeviceStateManagerGlobal {
}
}
+ /**
+ * Creates a {@link DeviceState} object from a device state identifier, with the
+ * {@link DeviceState} property that corresponds to what display is primary.
+ *
+ */
+ // TODO(b/325124054): Remove when system server refactor is completed
+ @NonNull
+ private DeviceState createDeviceState(int stateIdentifier) {
+ final Set<@DeviceState.DeviceStateProperties Integer> properties = new HashSet<>();
+ if (ArrayUtils.contains(sFoldedDeviceStates, stateIdentifier)) {
+ properties.add(DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY);
+ } else {
+ properties.add(DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY);
+ }
+ return new DeviceState(stateIdentifier, "" /* name */, properties);
+ }
+
+ /**
+ * Creates a list of {@link DeviceState} objects from an array of state identifiers.
+ */
+ // TODO(b/325124054): Remove when system server refactor is completed
+ @NonNull
+ private List<DeviceState> createDeviceStateList(int[] supportedStates) {
+ List<DeviceState> deviceStateList = new ArrayList<>();
+ for (int i = 0; i < supportedStates.length; i++) {
+ deviceStateList.add(createDeviceState(supportedStates[i]));
+ }
+ return deviceStateList;
+ }
+
private final class DeviceStateManagerCallback extends IDeviceStateManagerCallback.Stub {
@Override
public void onDeviceStateInfoChanged(DeviceStateInfo info) {
@@ -403,6 +485,11 @@ public final class DeviceStateManagerGlobal {
mDeviceStateCallback.onSupportedStatesChanged(newSupportedStates));
}
+ void notifySupportedDeviceStatesChanged(List<DeviceState> newSupportedDeviceStates) {
+ mExecutor.execute(() ->
+ mDeviceStateCallback.onSupportedStatesChanged(newSupportedDeviceStates));
+ }
+
void notifyBaseStateChanged(int newBaseState) {
execute("notifyBaseStateChanged",
() -> mDeviceStateCallback.onBaseStateChanged(newBaseState));
@@ -413,6 +500,11 @@ public final class DeviceStateManagerGlobal {
() -> mDeviceStateCallback.onStateChanged(newDeviceState));
}
+ void notifyDeviceStateChanged(DeviceState newDeviceState) {
+ execute("notifyDeviceStateChanged",
+ () -> mDeviceStateCallback.onDeviceStateChanged(newDeviceState));
+ }
+
private void execute(String traceName, Runnable r) {
mExecutor.execute(() -> {
if (DEBUG) {
diff --git a/core/java/android/hardware/devicestate/feature/flags.aconfig b/core/java/android/hardware/devicestate/feature/flags.aconfig
new file mode 100644
index 000000000000..73a9e346bd5d
--- /dev/null
+++ b/core/java/android/hardware/devicestate/feature/flags.aconfig
@@ -0,0 +1,9 @@
+package: "android.hardware.devicestate.feature.flags"
+
+flag {
+ name: "device_state_property_api"
+ namespace: "windowing_sdk"
+ description: "Updated DeviceState hasProperty API"
+ bug: "293636629"
+ is_fixed_read_only: true
+} \ No newline at end of file
diff --git a/core/java/android/hardware/display/BrightnessInfo.java b/core/java/android/hardware/display/BrightnessInfo.java
index 53a9a75fbca0..c09106206c25 100644
--- a/core/java/android/hardware/display/BrightnessInfo.java
+++ b/core/java/android/hardware/display/BrightnessInfo.java
@@ -80,6 +80,11 @@ public final class BrightnessInfo implements Parcelable {
*/
public static final int BRIGHTNESS_MAX_REASON_POWER_IC = 2;
+ /**
+ * Maximum brightness is restricted due to the Wear bedtime mode.
+ */
+ public static final int BRIGHTNESS_MAX_REASON_WEAR_BEDTIME_MODE = 3;
+
/** Brightness */
public final float brightness;
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 8f0e0c911f56..eb26a768f2a6 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -28,6 +28,7 @@ import android.annotation.LongDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
@@ -367,6 +368,8 @@ public final class DisplayManager {
* @see #createVirtualDisplay
* @hide
*/
+ @SuppressLint("UnflaggedApi") // @TestApi without associated feature.
+ @TestApi
public static final int VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH = 1 << 6;
/**
diff --git a/core/java/android/service/voice/HotwordTrainingData.aidl b/core/java/android/hardware/face/FaceEnrollOptions.aidl
index 03cc8413d780..336de9db899c 100644
--- a/core/java/android/service/voice/HotwordTrainingData.aidl
+++ b/core/java/android/hardware/face/FaceEnrollOptions.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.service.voice;
+package android.hardware.face;
-parcelable HotwordTrainingData;
+parcelable FaceEnrollOptions;
diff --git a/core/java/android/hardware/face/FaceEnrollOptions.java b/core/java/android/hardware/face/FaceEnrollOptions.java
new file mode 100644
index 000000000000..440105584f74
--- /dev/null
+++ b/core/java/android/hardware/face/FaceEnrollOptions.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2024 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.hardware.face;
+
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * Additional options when requesting Face enrollment.
+ *
+ * @hide
+ */
+@DataClass(
+ genParcelable = true,
+ genAidl = true,
+ genBuilder = true,
+ genSetters = true,
+ genEqualsHashCode = true
+)
+public class FaceEnrollOptions implements Parcelable {
+ public static final int ENROLL_REASON_UNKNOWN = 0;
+ public static final int ENROLL_REASON_RE_ENROLL_NOTIFICATION = 1;
+ public static final int ENROLL_REASON_SETTINGS = 2;
+ public static final int ENROLL_REASON_SUW = 3;
+
+ /**
+ * The reason for enrollment.
+ */
+ @EnrollReason
+ private final int mEnrollReason;
+ private static int defaultEnrollReason() {
+ return ENROLL_REASON_UNKNOWN;
+ }
+
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/hardware/face/FaceEnrollOptions.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @android.annotation.IntDef(prefix = "ENROLL_REASON_", value = {
+ ENROLL_REASON_UNKNOWN,
+ ENROLL_REASON_RE_ENROLL_NOTIFICATION,
+ ENROLL_REASON_SETTINGS,
+ ENROLL_REASON_SUW
+ })
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE)
+ @DataClass.Generated.Member
+ public @interface EnrollReason {}
+
+ @DataClass.Generated.Member
+ public static String enrollReasonToString(@EnrollReason int value) {
+ switch (value) {
+ case ENROLL_REASON_UNKNOWN:
+ return "ENROLL_REASON_UNKNOWN";
+ case ENROLL_REASON_RE_ENROLL_NOTIFICATION:
+ return "ENROLL_REASON_RE_ENROLL_NOTIFICATION";
+ case ENROLL_REASON_SETTINGS:
+ return "ENROLL_REASON_SETTINGS";
+ case ENROLL_REASON_SUW:
+ return "ENROLL_REASON_SUW";
+ default: return Integer.toHexString(value);
+ }
+ }
+
+ @DataClass.Generated.Member
+ /* package-private */ FaceEnrollOptions(
+ @EnrollReason int enrollReason) {
+ this.mEnrollReason = enrollReason;
+
+ if (!(mEnrollReason == ENROLL_REASON_UNKNOWN)
+ && !(mEnrollReason == ENROLL_REASON_RE_ENROLL_NOTIFICATION)
+ && !(mEnrollReason == ENROLL_REASON_SETTINGS)
+ && !(mEnrollReason == ENROLL_REASON_SUW)) {
+ throw new java.lang.IllegalArgumentException(
+ "enrollReason was " + mEnrollReason + " but must be one of: "
+ + "ENROLL_REASON_UNKNOWN(" + ENROLL_REASON_UNKNOWN + "), "
+ + "ENROLL_REASON_RE_ENROLL_NOTIFICATION(" + ENROLL_REASON_RE_ENROLL_NOTIFICATION + "), "
+ + "ENROLL_REASON_SETTINGS(" + ENROLL_REASON_SETTINGS + "), "
+ + "ENROLL_REASON_SUW(" + ENROLL_REASON_SUW + ")");
+ }
+
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * The reason for enrollment.
+ */
+ @DataClass.Generated.Member
+ public @EnrollReason int getEnrollReason() {
+ return mEnrollReason;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@android.annotation.Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(FaceEnrollOptions other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ FaceEnrollOptions that = (FaceEnrollOptions) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && mEnrollReason == that.mEnrollReason;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + mEnrollReason;
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@android.annotation.NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeInt(mEnrollReason);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ protected FaceEnrollOptions(@android.annotation.NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ int enrollReason = in.readInt();
+
+ this.mEnrollReason = enrollReason;
+
+ if (!(mEnrollReason == ENROLL_REASON_UNKNOWN)
+ && !(mEnrollReason == ENROLL_REASON_RE_ENROLL_NOTIFICATION)
+ && !(mEnrollReason == ENROLL_REASON_SETTINGS)
+ && !(mEnrollReason == ENROLL_REASON_SUW)) {
+ throw new java.lang.IllegalArgumentException(
+ "enrollReason was " + mEnrollReason + " but must be one of: "
+ + "ENROLL_REASON_UNKNOWN(" + ENROLL_REASON_UNKNOWN + "), "
+ + "ENROLL_REASON_RE_ENROLL_NOTIFICATION(" + ENROLL_REASON_RE_ENROLL_NOTIFICATION + "), "
+ + "ENROLL_REASON_SETTINGS(" + ENROLL_REASON_SETTINGS + "), "
+ + "ENROLL_REASON_SUW(" + ENROLL_REASON_SUW + ")");
+ }
+
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @android.annotation.NonNull Parcelable.Creator<FaceEnrollOptions> CREATOR
+ = new Parcelable.Creator<FaceEnrollOptions>() {
+ @Override
+ public FaceEnrollOptions[] newArray(int size) {
+ return new FaceEnrollOptions[size];
+ }
+
+ @Override
+ public FaceEnrollOptions createFromParcel(@android.annotation.NonNull android.os.Parcel in) {
+ return new FaceEnrollOptions(in);
+ }
+ };
+
+ /**
+ * A builder for {@link FaceEnrollOptions}
+ */
+ @SuppressWarnings("WeakerAccess")
+ @DataClass.Generated.Member
+ public static class Builder {
+
+ private @EnrollReason int mEnrollReason;
+
+ private long mBuilderFieldsSet = 0L;
+
+ public Builder() {
+ }
+
+ /**
+ * The reason for enrollment.
+ */
+ @DataClass.Generated.Member
+ public @android.annotation.NonNull Builder setEnrollReason(@EnrollReason int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1;
+ mEnrollReason = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @android.annotation.NonNull FaceEnrollOptions build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2; // Mark builder used
+
+ if ((mBuilderFieldsSet & 0x1) == 0) {
+ mEnrollReason = defaultEnrollReason();
+ }
+ FaceEnrollOptions o = new FaceEnrollOptions(
+ mEnrollReason);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x2) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+
+ @DataClass.Generated(
+ time = 1707949032303L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/hardware/face/FaceEnrollOptions.java",
+ inputSignatures = "public static final int ENROLL_REASON_UNKNOWN\npublic static final int ENROLL_REASON_RE_ENROLL_NOTIFICATION\npublic static final int ENROLL_REASON_SETTINGS\npublic static final int ENROLL_REASON_SUW\nprivate final @android.hardware.face.FaceEnrollOptions.EnrollReason int mEnrollReason\nprivate static int defaultEnrollReason()\nclass FaceEnrollOptions extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true, genSetters=true, genEqualsHashCode=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index bae5e7f83569..066c45f03ec4 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -393,7 +393,9 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
public void enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel,
EnrollmentCallback callback, int[] disabledFeatures) {
enroll(userId, hardwareAuthToken, cancel, callback, disabledFeatures,
- null /* previewSurface */, false /* debugConsent */);
+ null /* previewSurface */, false /* debugConsent */,
+ (new FaceEnrollOptions.Builder()).build());
+
}
/**
@@ -418,7 +420,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
@RequiresPermission(MANAGE_BIOMETRIC)
public void enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel,
EnrollmentCallback callback, int[] disabledFeatures, @Nullable Surface previewSurface,
- boolean debugConsent) {
+ boolean debugConsent, FaceEnrollOptions options) {
if (callback == null) {
throw new IllegalArgumentException("Must supply an enrollment callback");
}
@@ -449,7 +451,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
Trace.beginSection("FaceManager#enroll");
final long enrollId = mService.enroll(userId, mToken, hardwareAuthToken,
mServiceReceiver, mContext.getOpPackageName(), disabledFeatures,
- previewSurface, debugConsent);
+ previewSurface, debugConsent, options);
if (cancel != null) {
cancel.setOnCancelListener(new OnEnrollCancelListener(enrollId));
}
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index 8e234fa11866..b98c0cb41ac9 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -26,6 +26,7 @@ import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
import android.hardware.face.IFaceServiceReceiver;
import android.hardware.face.Face;
import android.hardware.face.FaceAuthenticateOptions;
+import android.hardware.face.FaceEnrollOptions;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.face.FaceSensorConfigurations;
import android.view.Surface;
@@ -100,7 +101,7 @@ interface IFaceService {
@EnforcePermission("MANAGE_BIOMETRIC")
long enroll(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver,
String opPackageName, in int [] disabledFeatures,
- in Surface previewSurface, boolean debugConsent);
+ in Surface previewSurface, boolean debugConsent, in FaceEnrollOptions options);
// Start remote face enrollment
@EnforcePermission("MANAGE_BIOMETRIC")
diff --git a/core/java/android/hardware/fingerprint/FingerprintEnrollOptions.aidl b/core/java/android/hardware/fingerprint/FingerprintEnrollOptions.aidl
new file mode 100644
index 000000000000..77803f3e3a9b
--- /dev/null
+++ b/core/java/android/hardware/fingerprint/FingerprintEnrollOptions.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 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.hardware.fingerprint;
+
+parcelable FingerprintEnrollOptions; \ No newline at end of file
diff --git a/core/java/android/hardware/fingerprint/FingerprintEnrollOptions.java b/core/java/android/hardware/fingerprint/FingerprintEnrollOptions.java
new file mode 100644
index 000000000000..9f9f322a8876
--- /dev/null
+++ b/core/java/android/hardware/fingerprint/FingerprintEnrollOptions.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2024 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.hardware.fingerprint;
+
+import android.os.Parcelable;
+
+
+import com.android.internal.util.DataClass;
+
+/**
+ * Additional options when requesting Fingerprint enrollment.
+ *
+ * @hide
+ */
+@DataClass(
+ genParcelable = true,
+ genAidl = true,
+ genBuilder = true,
+ genSetters = true,
+ genEqualsHashCode = true
+)
+public class FingerprintEnrollOptions implements Parcelable {
+ public static final int ENROLL_REASON_UNKNOWN = 0;
+ public static final int ENROLL_REASON_RE_ENROLL_NOTIFICATION = 1;
+ public static final int ENROLL_REASON_SETTINGS = 2;
+ public static final int ENROLL_REASON_SUW = 3;
+
+ /**
+ * The reason for enrollment.
+ */
+ @EnrollReason
+ private final int mEnrollReason;
+
+ private static int defaultEnrollReason() {
+ return ENROLL_REASON_UNKNOWN;
+ }
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/hardware/fingerprint/FingerprintEnrollOptions.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @android.annotation.IntDef(prefix = "ENROLL_REASON_", value = {
+ ENROLL_REASON_UNKNOWN,
+ ENROLL_REASON_RE_ENROLL_NOTIFICATION,
+ ENROLL_REASON_SETTINGS,
+ ENROLL_REASON_SUW
+ })
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE)
+ @DataClass.Generated.Member
+ public @interface EnrollReason {}
+
+ @DataClass.Generated.Member
+ public static String enrollReasonToString(@EnrollReason int value) {
+ switch (value) {
+ case ENROLL_REASON_UNKNOWN:
+ return "ENROLL_REASON_UNKNOWN";
+ case ENROLL_REASON_RE_ENROLL_NOTIFICATION:
+ return "ENROLL_REASON_RE_ENROLL_NOTIFICATION";
+ case ENROLL_REASON_SETTINGS:
+ return "ENROLL_REASON_SETTINGS";
+ case ENROLL_REASON_SUW:
+ return "ENROLL_REASON_SUW";
+ default: return Integer.toHexString(value);
+ }
+ }
+
+ @DataClass.Generated.Member
+ /* package-private */ FingerprintEnrollOptions(
+ @EnrollReason int enrollReason) {
+ this.mEnrollReason = enrollReason;
+
+ if (!(mEnrollReason == ENROLL_REASON_UNKNOWN)
+ && !(mEnrollReason == ENROLL_REASON_RE_ENROLL_NOTIFICATION)
+ && !(mEnrollReason == ENROLL_REASON_SETTINGS)
+ && !(mEnrollReason == ENROLL_REASON_SUW)) {
+ throw new java.lang.IllegalArgumentException(
+ "enrollReason was " + mEnrollReason + " but must be one of: "
+ + "ENROLL_REASON_UNKNOWN(" + ENROLL_REASON_UNKNOWN + "), "
+ + "ENROLL_REASON_RE_ENROLL_NOTIFICATION(" + ENROLL_REASON_RE_ENROLL_NOTIFICATION + "), "
+ + "ENROLL_REASON_SETTINGS(" + ENROLL_REASON_SETTINGS + "), "
+ + "ENROLL_REASON_SUW(" + ENROLL_REASON_SUW + ")");
+ }
+
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * The reason for enrollment.
+ */
+ @DataClass.Generated.Member
+ public @EnrollReason int getEnrollReason() {
+ return mEnrollReason;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@android.annotation.Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(FingerprintEnrollOptions other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ FingerprintEnrollOptions that = (FingerprintEnrollOptions) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && mEnrollReason == that.mEnrollReason;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + mEnrollReason;
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@android.annotation.NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeInt(mEnrollReason);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ protected FingerprintEnrollOptions(@android.annotation.NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ int enrollReason = in.readInt();
+
+ this.mEnrollReason = enrollReason;
+
+ if (!(mEnrollReason == ENROLL_REASON_UNKNOWN)
+ && !(mEnrollReason == ENROLL_REASON_RE_ENROLL_NOTIFICATION)
+ && !(mEnrollReason == ENROLL_REASON_SETTINGS)
+ && !(mEnrollReason == ENROLL_REASON_SUW)) {
+ throw new java.lang.IllegalArgumentException(
+ "enrollReason was " + mEnrollReason + " but must be one of: "
+ + "ENROLL_REASON_UNKNOWN(" + ENROLL_REASON_UNKNOWN + "), "
+ + "ENROLL_REASON_RE_ENROLL_NOTIFICATION(" + ENROLL_REASON_RE_ENROLL_NOTIFICATION + "), "
+ + "ENROLL_REASON_SETTINGS(" + ENROLL_REASON_SETTINGS + "), "
+ + "ENROLL_REASON_SUW(" + ENROLL_REASON_SUW + ")");
+ }
+
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @android.annotation.NonNull Parcelable.Creator<FingerprintEnrollOptions> CREATOR
+ = new Parcelable.Creator<FingerprintEnrollOptions>() {
+ @Override
+ public FingerprintEnrollOptions[] newArray(int size) {
+ return new FingerprintEnrollOptions[size];
+ }
+
+ @Override
+ public FingerprintEnrollOptions createFromParcel(@android.annotation.NonNull android.os.Parcel in) {
+ return new FingerprintEnrollOptions(in);
+ }
+ };
+
+ /**
+ * A builder for {@link FingerprintEnrollOptions}
+ */
+ @SuppressWarnings("WeakerAccess")
+ @DataClass.Generated.Member
+ public static class Builder {
+
+ private @EnrollReason int mEnrollReason;
+
+ private long mBuilderFieldsSet = 0L;
+
+ public Builder() {
+ }
+
+ /**
+ * The reason for enrollment.
+ */
+ @DataClass.Generated.Member
+ public @android.annotation.NonNull Builder setEnrollReason(@EnrollReason int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1;
+ mEnrollReason = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @android.annotation.NonNull FingerprintEnrollOptions build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2; // Mark builder used
+
+ if ((mBuilderFieldsSet & 0x1) == 0) {
+ mEnrollReason = defaultEnrollReason();
+ }
+ FingerprintEnrollOptions o = new FingerprintEnrollOptions(
+ mEnrollReason);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x2) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+
+ @DataClass.Generated(
+ time = 1704407629121L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/hardware/fingerprint/FingerprintEnrollOptions.java",
+ inputSignatures = "public static final int ENROLL_REASON_UNKNOWN\npublic static final int ENROLL_REASON_RE_ENROLL_NOTIFICATION\npublic static final int ENROLL_REASON_SETTINGS\npublic static final int ENROLL_REASON_SUW\nprivate final @android.hardware.fingerprint.FingerprintEnrollOptions.EnrollReason int mEnrollReason\nprivate static int defaultEnrollReason()\nclass FingerprintEnrollOptions extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true, genSetters=true, genEqualsHashCode=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index fe7de8333784..81e321d96aa6 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -744,7 +744,8 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
*/
@RequiresPermission(MANAGE_FINGERPRINT)
public void enroll(byte [] hardwareAuthToken, CancellationSignal cancel, int userId,
- EnrollmentCallback callback, @EnrollReason int enrollReason) {
+ EnrollmentCallback callback, @EnrollReason int enrollReason,
+ FingerprintEnrollOptions options) {
if (userId == UserHandle.USER_CURRENT) {
userId = getCurrentUserId();
}
@@ -768,7 +769,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
try {
mEnrollmentCallback = callback;
final long enrollId = mService.enroll(mToken, hardwareAuthToken, userId,
- mServiceReceiver, mContext.getOpPackageName(), enrollReason);
+ mServiceReceiver, mContext.getOpPackageName(), enrollReason, options);
if (cancel != null) {
cancel.setOnCancelListener(new OnEnrollCancelListener(enrollId));
}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 606b171f36ba..8b37c24527d7 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -30,6 +30,7 @@ import android.hardware.fingerprint.IUdfpsOverlayController;
import android.hardware.fingerprint.ISidefpsController;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintAuthenticateOptions;
+import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.FingerprintSensorConfigurations;
import java.util.List;
@@ -98,7 +99,7 @@ interface IFingerprintService {
// Start fingerprint enrollment
@EnforcePermission("MANAGE_FINGERPRINT")
long enroll(IBinder token, in byte [] hardwareAuthToken, int userId, IFingerprintServiceReceiver receiver,
- String opPackageName, int enrollReason);
+ String opPackageName, int enrollReason, in FingerprintEnrollOptions options);
// Cancel enrollment in progress
@EnforcePermission("MANAGE_FINGERPRINT")
diff --git a/core/java/android/hardware/input/KeyboardLayoutPreviewDrawable.java b/core/java/android/hardware/input/KeyboardLayoutPreviewDrawable.java
index 1cc910c87b4f..e47a48d7aabc 100644
--- a/core/java/android/hardware/input/KeyboardLayoutPreviewDrawable.java
+++ b/core/java/android/hardware/input/KeyboardLayoutPreviewDrawable.java
@@ -48,14 +48,13 @@ final class KeyboardLayoutPreviewDrawable extends Drawable {
private static final int GRAVITY_RIGHT = 0x2;
private static final int GRAVITY_TOP = 0x4;
private static final int GRAVITY_BOTTOM = 0x8;
- private static final int GRAVITY_CENTER =
- GRAVITY_LEFT | GRAVITY_RIGHT | GRAVITY_TOP | GRAVITY_BOTTOM;
- private static final int GRAVITY_CENTER_HORIZONTAL = GRAVITY_LEFT | GRAVITY_RIGHT;
+ private static final int TEXT_PADDING_IN_DP = 1;
private static final int KEY_PADDING_IN_DP = 3;
private static final int KEYBOARD_PADDING_IN_DP = 10;
private static final int KEY_RADIUS_IN_DP = 5;
private static final int KEYBOARD_RADIUS_IN_DP = 10;
- private static final int GLYPH_TEXT_SIZE_IN_SP = 10;
+ private static final int MIN_GLYPH_TEXT_SIZE_IN_SP = 10;
+ private static final int MAX_GLYPH_TEXT_SIZE_IN_SP = 20;
private final List<KeyDrawable> mKeyDrawables = new ArrayList<>();
@@ -107,6 +106,8 @@ final class KeyboardLayoutPreviewDrawable extends Drawable {
}
int rowCount = keys.length;
float keyHeight = (float) (height - rowCount * 2 * keyPadding) / rowCount;
+ // Based on key height calculate the max text size that can fit for typing keys
+ mResourceProvider.calculateBestTextSizeForKey(keyHeight);
float isoEnterKeyLeft = 0;
float isoEnterKeyTop = 0;
float isoEnterWidthUnit = 0;
@@ -136,16 +137,19 @@ final class KeyboardLayoutPreviewDrawable extends Drawable {
}
if (PhysicalKeyLayout.isSpecialKey(row[j])) {
mKeyDrawables.add(new TypingKey(null, keyRect, keyRadius,
+ mResourceProvider.getTextPadding(),
mResourceProvider.getSpecialKeyPaint(),
mResourceProvider.getSpecialKeyPaint(),
mResourceProvider.getSpecialKeyPaint()));
} else if (PhysicalKeyLayout.isKeyPositionUnsure(row[j])) {
mKeyDrawables.add(new UnsureTypingKey(row[j].glyph(), keyRect,
- keyRadius, mResourceProvider.getTypingKeyPaint(),
+ keyRadius, mResourceProvider.getTextPadding(),
+ mResourceProvider.getTypingKeyPaint(),
mResourceProvider.getPrimaryGlyphPaint(),
mResourceProvider.getSecondaryGlyphPaint()));
} else {
mKeyDrawables.add(new TypingKey(row[j].glyph(), keyRect, keyRadius,
+ mResourceProvider.getTextPadding(),
mResourceProvider.getTypingKeyPaint(),
mResourceProvider.getPrimaryGlyphPaint(),
mResourceProvider.getSecondaryGlyphPaint()));
@@ -192,15 +196,18 @@ final class KeyboardLayoutPreviewDrawable extends Drawable {
private final RectF mKeyRect;
private final float mKeyRadius;
+ private final float mTextPadding;
private final Paint mKeyPaint;
private final Paint mBaseTextPaint;
private final Paint mModifierTextPaint;
private final List<GlyphDrawable> mGlyphDrawables = new ArrayList<>();
private TypingKey(@Nullable PhysicalKeyLayout.KeyGlyph glyphData, RectF keyRect,
- float keyRadius, Paint keyPaint, Paint baseTextPaint, Paint modifierTextPaint) {
+ float keyRadius, float textPadding, Paint keyPaint, Paint baseTextPaint,
+ Paint modifierTextPaint) {
mKeyRect = keyRect;
mKeyRadius = keyRadius;
+ mTextPadding = textPadding;
mKeyPaint = keyPaint;
mBaseTextPaint = baseTextPaint;
mModifierTextPaint = modifierTextPaint;
@@ -219,20 +226,17 @@ final class KeyboardLayoutPreviewDrawable extends Drawable {
if (!glyphData.hasBaseText()) {
return;
}
- boolean isCenter = !glyphData.hasValidAltGrText() && !glyphData.hasValidAltShiftText();
mGlyphDrawables.add(new GlyphDrawable(glyphData.getBaseText(), new RectF(),
- GRAVITY_BOTTOM | (isCenter ? GRAVITY_CENTER_HORIZONTAL : GRAVITY_LEFT),
- mBaseTextPaint));
+ GRAVITY_BOTTOM | GRAVITY_LEFT, mBaseTextPaint));
if (glyphData.hasValidShiftText()) {
mGlyphDrawables.add(new GlyphDrawable(glyphData.getShiftText(), new RectF(),
- GRAVITY_TOP | (isCenter ? GRAVITY_CENTER_HORIZONTAL : GRAVITY_LEFT),
- mModifierTextPaint));
+ GRAVITY_TOP | GRAVITY_LEFT, mModifierTextPaint));
}
if (glyphData.hasValidAltGrText()) {
mGlyphDrawables.add(new GlyphDrawable(glyphData.getAltGrText(), new RectF(),
GRAVITY_BOTTOM | GRAVITY_RIGHT, mModifierTextPaint));
}
- if (glyphData.hasValidAltShiftText()) {
+ if (glyphData.hasValidAltGrShiftText()) {
mGlyphDrawables.add(new GlyphDrawable(glyphData.getAltGrShiftText(), new RectF(),
GRAVITY_TOP | GRAVITY_RIGHT, mModifierTextPaint));
}
@@ -246,15 +250,19 @@ final class KeyboardLayoutPreviewDrawable extends Drawable {
float centerY = keyHeight / 2;
if ((glyph.gravity & GRAVITY_LEFT) != 0) {
centerX -= keyWidth / 4;
+ centerX += mTextPadding / 2;
}
if ((glyph.gravity & GRAVITY_RIGHT) != 0) {
centerX += keyWidth / 4;
+ centerX -= mTextPadding / 2;
}
if ((glyph.gravity & GRAVITY_TOP) != 0) {
centerY -= keyHeight / 4;
+ centerY += mTextPadding / 2;
}
if ((glyph.gravity & GRAVITY_BOTTOM) != 0) {
centerY += keyHeight / 4;
+ centerY -= mTextPadding / 2;
}
Rect textBounds = new Rect();
glyph.paint.getTextBounds(glyph.text, 0, glyph.text.length(), textBounds);
@@ -285,9 +293,9 @@ final class KeyboardLayoutPreviewDrawable extends Drawable {
private static class UnsureTypingKey extends TypingKey {
private UnsureTypingKey(@Nullable PhysicalKeyLayout.KeyGlyph glyphData,
- RectF keyRect, float keyRadius, Paint keyPaint, Paint baseTextPaint,
- Paint modifierTextPaint) {
- super(glyphData, keyRect, keyRadius, createGreyedOutPaint(keyPaint),
+ RectF keyRect, float keyRadius, float textPadding, Paint keyPaint,
+ Paint baseTextPaint, Paint modifierTextPaint) {
+ super(glyphData, keyRect, keyRadius, textPadding, createGreyedOutPaint(keyPaint),
createGreyedOutPaint(baseTextPaint), createGreyedOutPaint(modifierTextPaint));
}
}
@@ -402,8 +410,11 @@ final class KeyboardLayoutPreviewDrawable extends Drawable {
private final Paint mSecondaryGlyphPaint;
private final int mKeyPadding;
private final int mKeyboardPadding;
+ private final float mTextPadding;
private final float mKeyRadius;
private final float mBackgroundRadius;
+ private final float mSpToPxMultiplier;
+ private final Paint.FontMetrics mFontMetrics;
private ResourceProvider(Context context) {
mKeyPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
@@ -414,8 +425,10 @@ final class KeyboardLayoutPreviewDrawable extends Drawable {
KEY_RADIUS_IN_DP, context.getResources().getDisplayMetrics());
mBackgroundRadius = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
KEYBOARD_RADIUS_IN_DP, context.getResources().getDisplayMetrics());
- int textSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
- GLYPH_TEXT_SIZE_IN_SP, context.getResources().getDisplayMetrics());
+ mSpToPxMultiplier = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 1,
+ context.getResources().getDisplayMetrics());
+ mTextPadding = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ TEXT_PADDING_IN_DP, context.getResources().getDisplayMetrics());
boolean isDark = (context.getResources().getConfiguration().uiMode
& Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
int typingKeyColor = context.getColor(
@@ -430,15 +443,37 @@ final class KeyboardLayoutPreviewDrawable extends Drawable {
int backgroundColor = context.getColor(
isDark ? android.R.color.system_surface_container_dark
: android.R.color.system_surface_container_light);
- mPrimaryGlyphPaint = createTextPaint(primaryGlyphColor, textSize,
+ mPrimaryGlyphPaint = createTextPaint(primaryGlyphColor,
+ MIN_GLYPH_TEXT_SIZE_IN_SP * mSpToPxMultiplier,
Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD));
- mSecondaryGlyphPaint = createTextPaint(secondaryGlyphColor, textSize,
+ mSecondaryGlyphPaint = createTextPaint(secondaryGlyphColor,
+ MIN_GLYPH_TEXT_SIZE_IN_SP * mSpToPxMultiplier,
Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL));
+ mFontMetrics = mPrimaryGlyphPaint.getFontMetrics();
mTypingKeyPaint = createFillPaint(typingKeyColor);
mSpecialKeyPaint = createFillPaint(specialKeyColor);
mBackgroundPaint = createFillPaint(backgroundColor);
}
+ private void calculateBestTextSizeForKey(float keyHeight) {
+ int textSize = (int) (mSpToPxMultiplier * MIN_GLYPH_TEXT_SIZE_IN_SP) + 1;
+ while (textSize < mSpToPxMultiplier * MAX_GLYPH_TEXT_SIZE_IN_SP) {
+ updateTextSize(textSize);
+ if (mFontMetrics.bottom - mFontMetrics.top + 3 * mTextPadding > keyHeight / 2) {
+ textSize--;
+ break;
+ }
+ textSize++;
+ }
+ updateTextSize(textSize);
+ }
+
+ private void updateTextSize(float textSize) {
+ mPrimaryGlyphPaint.setTextSize(textSize);
+ mSecondaryGlyphPaint.setTextSize(textSize);
+ mPrimaryGlyphPaint.getFontMetrics(mFontMetrics);
+ }
+
private Paint getBackgroundPaint() {
return mBackgroundPaint;
}
@@ -467,6 +502,10 @@ final class KeyboardLayoutPreviewDrawable extends Drawable {
return mKeyboardPadding;
}
+ private float getTextPadding() {
+ return mTextPadding;
+ }
+
private float getKeyRadius() {
return mKeyRadius;
}
@@ -476,7 +515,8 @@ final class KeyboardLayoutPreviewDrawable extends Drawable {
}
}
- private static Paint createTextPaint(@ColorInt int textColor, int textSize, Typeface typeface) {
+ private static Paint createTextPaint(@ColorInt int textColor, float textSize,
+ Typeface typeface) {
Paint paint = new Paint();
paint.setColor(textColor);
paint.setStyle(Paint.Style.FILL);
diff --git a/core/java/android/hardware/input/PhysicalKeyLayout.java b/core/java/android/hardware/input/PhysicalKeyLayout.java
index 844e02f00777..cff444fc84bf 100644
--- a/core/java/android/hardware/input/PhysicalKeyLayout.java
+++ b/core/java/android/hardware/input/PhysicalKeyLayout.java
@@ -336,11 +336,13 @@ final class PhysicalKeyLayout {
return "";
}
int utf8Char = (kcm.get(keyCode, modifierState) & KeyCharacterMap.COMBINING_ACCENT_MASK);
+ if (utf8Char == 0) {
+ return "";
+ }
if (Character.isValidCodePoint(utf8Char)) {
return String.valueOf(Character.toChars(utf8Char));
- } else {
- return String.valueOf(kcm.getDisplayLabel(keyCode));
}
+ return "□";
}
private static LayoutKey getKey(int keyCode, float keyWeight) {
@@ -434,10 +436,11 @@ final class PhysicalKeyLayout {
}
public boolean hasValidAltGrText() {
- return !TextUtils.isEmpty(mAltGrText) && !TextUtils.equals(mBaseText, mAltGrText);
+ return !TextUtils.isEmpty(mAltGrText) && !TextUtils.equals(mBaseText, mAltGrText)
+ && !TextUtils.equals(mShiftText, mAltGrText);
}
- public boolean hasValidAltShiftText() {
+ public boolean hasValidAltGrShiftText() {
return !TextUtils.isEmpty(mAltGrShiftText)
&& !TextUtils.equals(mBaseText, mAltGrShiftText)
&& !TextUtils.equals(mAltGrText, mAltGrShiftText)
diff --git a/core/java/android/hardware/input/VirtualKeyboard.java b/core/java/android/hardware/input/VirtualKeyboard.java
index 6eb2ae38ed82..6a7d19535ac9 100644
--- a/core/java/android/hardware/input/VirtualKeyboard.java
+++ b/core/java/android/hardware/input/VirtualKeyboard.java
@@ -18,7 +18,9 @@ package android.hardware.input;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.companion.virtual.IVirtualDevice;
import android.os.IBinder;
import android.os.RemoteException;
@@ -66,4 +68,15 @@ public class VirtualKeyboard extends VirtualInputDevice {
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * @return The id of the {@link android.view.InputDevice} corresponding to this keyboard.
+ * @hide
+ */
+ @SuppressLint("UnflaggedApi") // @TestApi without associated feature.
+ @TestApi
+ @Override
+ public int getInputDeviceId() {
+ return super.getInputDeviceId();
+ }
}
diff --git a/core/java/android/net/flags.aconfig b/core/java/android/net/flags.aconfig
index 311dc09eb516..6efb87225183 100644
--- a/core/java/android/net/flags.aconfig
+++ b/core/java/android/net/flags.aconfig
@@ -9,3 +9,11 @@ flag {
description: "The flag controls the access for getIpSecTransformState and IpSecTransformState"
bug: "308011229"
}
+
+flag {
+ name: "powered_off_finding_platform"
+ namespace: "nearby"
+ description: "Controls whether the Powered Off Finding feature is enabled"
+ bug: "307898240"
+}
+
diff --git a/core/java/android/net/thread/flags.aconfig b/core/java/android/net/thread/flags.aconfig
index 6e72f8ebd8d1..d679f9c3acb8 100644
--- a/core/java/android/net/thread/flags.aconfig
+++ b/core/java/android/net/thread/flags.aconfig
@@ -1,4 +1,7 @@
-package: "com.android.net.thread.flags"
+package: "com.android.net.thread.platform.flags"
+
+# This file contains aconfig flags used from platform code
+# Flags used for module APIs must be in aconfig files under each modules
flag {
name: "thread_user_restriction_enabled"
@@ -6,3 +9,10 @@ flag {
description: "Controls whether user restriction on thread networks is enabled"
bug: "307679182"
}
+
+flag {
+ name: "thread_enabled_platform"
+ namespace: "thread_network"
+ description: "Controls whether the Android Thread feature is enabled"
+ bug: "301473012"
+}
diff --git a/core/java/android/os/BugreportParams.java b/core/java/android/os/BugreportParams.java
index f2ef185a0500..f7b417337911 100644
--- a/core/java/android/os/BugreportParams.java
+++ b/core/java/android/os/BugreportParams.java
@@ -21,7 +21,6 @@ import android.annotation.IntDef;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.admin.flags.Flags;
-import android.compat.annotation.UnsupportedAppUsage;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -127,12 +126,8 @@ public final class BugreportParams {
/**
* Options for a lightweight bugreport intended to be taken for onboarding-related flows.
- *
- * @hide
*/
- @TestApi
@FlaggedApi(Flags.FLAG_ONBOARDING_BUGREPORT_V2_ENABLED)
- @UnsupportedAppUsage
public static final int BUGREPORT_MODE_ONBOARDING = IDumpstate.BUGREPORT_MODE_ONBOARDING;
/**
@@ -180,10 +175,7 @@ public final class BugreportParams {
* The bugreport may be retrieved multiple times using
* {@link BugreportManager#retrieveBugreport(
* String, ParcelFileDescriptor, Executor, BugreportManager.BugreportCallback)}.
- *
- * @hide
*/
- @TestApi
@FlaggedApi(Flags.FLAG_ONBOARDING_BUGREPORT_V2_ENABLED)
public static final int BUGREPORT_FLAG_KEEP_BUGREPORT_ON_RETRIEVAL =
IDumpstate.BUGREPORT_FLAG_KEEP_BUGREPORT_ON_RETRIEVAL;
diff --git a/core/java/android/os/ExternalVibrationScale.aidl b/core/java/android/os/ExternalVibrationScale.aidl
new file mode 100644
index 000000000000..cf6f8ed52f7d
--- /dev/null
+++ b/core/java/android/os/ExternalVibrationScale.aidl
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+ * ExternalVibrationScale holds the vibration scale level and adaptive haptics scale. These
+ * can be used to scale external vibrations.
+ *
+ * @hide
+ */
+parcelable ExternalVibrationScale {
+ @Backing(type="int")
+ enum ScaleLevel {
+ SCALE_MUTE = -100,
+ SCALE_VERY_LOW = -2,
+ SCALE_LOW = -1,
+ SCALE_NONE = 0,
+ SCALE_HIGH = 1,
+ SCALE_VERY_HIGH = 2
+ }
+
+ /**
+ * The scale level that will be applied to external vibrations.
+ */
+ ScaleLevel scaleLevel = ScaleLevel.SCALE_NONE;
+
+ /**
+ * The adaptive haptics scale that will be applied to external vibrations.
+ */
+ float adaptiveHapticsScale = 1f;
+}
diff --git a/core/java/android/os/IExternalVibratorService.aidl b/core/java/android/os/IExternalVibratorService.aidl
index 666171fcbcb3..a9df15a088bf 100644
--- a/core/java/android/os/IExternalVibratorService.aidl
+++ b/core/java/android/os/IExternalVibratorService.aidl
@@ -17,6 +17,7 @@
package android.os;
import android.os.ExternalVibration;
+import android.os.ExternalVibrationScale;
/**
* The communication channel by which an external system that wants to control the system
@@ -32,29 +33,24 @@ import android.os.ExternalVibration;
* {@hide}
*/
interface IExternalVibratorService {
- const int SCALE_MUTE = -100;
- const int SCALE_VERY_LOW = -2;
- const int SCALE_LOW = -1;
- const int SCALE_NONE = 0;
- const int SCALE_HIGH = 1;
- const int SCALE_VERY_HIGH = 2;
-
/**
* A method called by the external system to start a vibration.
*
- * If this returns {@code SCALE_MUTE}, then the vibration should <em>not</em> play. If this
- * returns any other scale level, then any currently playing vibration controlled by the
- * requesting system must be muted and this vibration can begin playback.
+ * This returns an {@link ExternalVibrationScale} which includes the vibration scale level and
+ * the adaptive haptics scale.
+ *
+ * If the returned scale level is {@link ExternalVibrationScale.ScaleLevel#SCALE_MUTE}, then
+ * the vibration should <em>not</em> play. If it returns any other scale level, then
+ * any currently playing vibration controlled by the requesting system must be muted and this
+ * vibration can begin playback.
*
* Note that the IExternalVibratorService implementation will not call mute on any currently
* playing external vibrations in order to avoid re-entrancy with the system on the other side.
*
- * @param vibration An ExternalVibration
- *
- * @return {@code SCALE_MUTE} if the external vibration should not play, and any other scale
- * level if it should.
+ * @param vib The external vibration starting.
+ * @return {@link ExternalVibrationScale} including scale level and adaptive haptics scale.
*/
- int onExternalVibrationStart(in ExternalVibration vib);
+ ExternalVibrationScale onExternalVibrationStart(in ExternalVibration vib);
/**
* A method called by the external system when a vibration no longer wants to play.
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index fbec518e4a29..3950c25675d8 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -42,7 +42,7 @@ import java.util.ArrayList;
*/
@android.ravenwood.annotation.RavenwoodKeepWholeClass
@android.ravenwood.annotation.RavenwoodNativeSubstitutionClass(
- "com.android.hoststubgen.nativesubstitution.MessageQueue_host")
+ "com.android.platform.test.ravenwood.nativesubstitution.MessageQueue_host")
public final class MessageQueue {
private static final String TAG = "MessageQueue";
private static final boolean DEBUG = false;
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index 4b170f3786e9..b1c24a7fc64a 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -87,6 +87,7 @@ per-file DdmSyncStageUpdater.java = sanglardf@google.com, rpaquay@google.com
# PerformanceHintManager
per-file PerformanceHintManager.java = file:/ADPF_OWNERS
+per-file WorkDuration.java = file:/ADPF_OWNERS
# IThermal interfaces
per-file IThermal* = file:/THERMAL_OWNERS
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 8e860c35388d..ccfb6326d941 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -233,7 +233,8 @@ import java.util.function.IntFunction;
* {@link #readSparseArray(ClassLoader, Class)}.
*/
@RavenwoodKeepWholeClass
-@RavenwoodNativeSubstitutionClass("com.android.hoststubgen.nativesubstitution.Parcel_host")
+@RavenwoodNativeSubstitutionClass(
+ "com.android.platform.test.ravenwood.nativesubstitution.Parcel_host")
public final class Parcel {
private static final boolean DEBUG_RECYCLE = false;
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 6532d5c8784a..17dfdda7dffc 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -75,7 +75,8 @@ import java.nio.ByteOrder;
* you to close it when done with it.
*/
@RavenwoodKeepWholeClass
-@RavenwoodNativeSubstitutionClass("com.android.hoststubgen.nativesubstitution.ParcelFileDescriptor_host")
+@RavenwoodNativeSubstitutionClass(
+ "com.android.platform.test.ravenwood.nativesubstitution.ParcelFileDescriptor_host")
public class ParcelFileDescriptor implements Parcelable, Closeable {
private static final String TAG = "ParcelFileDescriptor";
diff --git a/core/java/android/os/PermissionEnforcer.java b/core/java/android/os/PermissionEnforcer.java
index 91d22698d7df..3cc6fb5ad0ef 100644
--- a/core/java/android/os/PermissionEnforcer.java
+++ b/core/java/android/os/PermissionEnforcer.java
@@ -24,6 +24,7 @@ import android.content.Context;
import android.content.PermissionChecker;
import android.content.pm.PackageManager;
import android.permission.PermissionCheckerManager;
+import android.permission.PermissionManager;
/**
* PermissionEnforcer check permissions for AIDL-generated services which use
@@ -71,6 +72,7 @@ import android.permission.PermissionCheckerManager;
* @hide
*/
@SystemService(Context.PERMISSION_ENFORCER_SERVICE)
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PermissionEnforcer {
private final Context mContext;
@@ -84,6 +86,8 @@ public class PermissionEnforcer {
}
/** Constructor, prefer using the fromContext static method when possible */
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = PermissionManager.class,
+ reason = "Use subclass for unit tests, such as FakePermissionEnforcer")
public PermissionEnforcer(@NonNull Context context) {
mContext = context;
}
@@ -103,9 +107,19 @@ public class PermissionEnforcer {
return PermissionCheckerManager.PERMISSION_HARD_DENIED;
}
+ @android.ravenwood.annotation.RavenwoodReplace(blockedBy = AppOpsManager.class,
+ reason = "Blocked on Mainline dependencies")
+ private static int permissionToOpCode(String permission) {
+ return AppOpsManager.permissionToOpCode(permission);
+ }
+
+ private static int permissionToOpCode$ravenwood(String permission) {
+ return AppOpsManager.OP_NONE;
+ }
+
private boolean anyAppOps(@NonNull String[] permissions) {
for (String permission : permissions) {
- if (AppOpsManager.permissionToOpCode(permission) != AppOpsManager.OP_NONE) {
+ if (permissionToOpCode(permission) != AppOpsManager.OP_NONE) {
return true;
}
}
@@ -122,7 +136,7 @@ public class PermissionEnforcer {
public void enforcePermission(@NonNull String permission, int pid, int uid)
throws SecurityException {
- if (AppOpsManager.permissionToOpCode(permission) != AppOpsManager.OP_NONE) {
+ if (permissionToOpCode(permission) != AppOpsManager.OP_NONE) {
AttributionSource source = new AttributionSource(uid, null, null);
enforcePermission(permission, source);
return;
diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java
index e96c24d677f1..0be2d3e30c33 100644
--- a/core/java/android/os/ServiceManager.java
+++ b/core/java/android/os/ServiceManager.java
@@ -25,6 +25,7 @@ import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.BinderInternal;
+import com.android.internal.util.Preconditions;
import com.android.internal.util.StatLogger;
import java.util.Map;
@@ -38,6 +39,7 @@ import java.util.Map;
* @hide
**/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+@android.ravenwood.annotation.RavenwoodKeepPartialClass
public final class ServiceManager {
private static final String TAG = "ServiceManager";
private static final Object sLock = new Object();
@@ -48,9 +50,16 @@ public final class ServiceManager {
/**
* Cache for the "well known" services, such as WM and AM.
*/
+ // NOTE: this cache is designed to be populated exactly once at process
+ // start to avoid any overhead from locking
@UnsupportedAppUsage
private static Map<String, IBinder> sCache = new ArrayMap<String, IBinder>();
+ @GuardedBy("ServiceManager.class")
+ // NOTE: this cache is designed to support mutation by tests, so we require
+ // a lock to be held for all accesses
+ private static Map<String, IBinder> sCache$ravenwood;
+
/**
* We do the "slow log" at most once every this interval.
*/
@@ -115,9 +124,27 @@ public final class ServiceManager {
/** @hide */
@UnsupportedAppUsage
+ @android.ravenwood.annotation.RavenwoodKeep
public ServiceManager() {
}
+ /** @hide */
+ @android.ravenwood.annotation.RavenwoodKeep
+ public static void init$ravenwood() {
+ synchronized (ServiceManager.class) {
+ sCache$ravenwood = new ArrayMap<>();
+ }
+ }
+
+ /** @hide */
+ @android.ravenwood.annotation.RavenwoodKeep
+ public static void reset$ravenwood() {
+ synchronized (ServiceManager.class) {
+ sCache$ravenwood.clear();
+ sCache$ravenwood = null;
+ }
+ }
+
@UnsupportedAppUsage
private static IServiceManager getIServiceManager() {
if (sServiceManager != null) {
@@ -138,6 +165,7 @@ public final class ServiceManager {
* @hide
*/
@UnsupportedAppUsage
+ @android.ravenwood.annotation.RavenwoodReplace
public static IBinder getService(String name) {
try {
IBinder service = sCache.get(name);
@@ -152,12 +180,21 @@ public final class ServiceManager {
return null;
}
+ /** @hide */
+ public static IBinder getService$ravenwood(String name) {
+ synchronized (ServiceManager.class) {
+ // Ravenwood is a single-process environment, so it only needs to store locally
+ return Preconditions.requireNonNullViaRavenwoodRule(sCache$ravenwood).get(name);
+ }
+ }
+
/**
* Returns a reference to a service with the given name, or throws
* {@link ServiceNotFoundException} if none is found.
*
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public static IBinder getServiceOrThrow(String name) throws ServiceNotFoundException {
final IBinder binder = getService(name);
if (binder != null) {
@@ -176,6 +213,7 @@ public final class ServiceManager {
* @hide
*/
@UnsupportedAppUsage
+ @android.ravenwood.annotation.RavenwoodKeep
public static void addService(String name, IBinder service) {
addService(name, service, false, IServiceManager.DUMP_FLAG_PRIORITY_DEFAULT);
}
@@ -191,6 +229,7 @@ public final class ServiceManager {
* @hide
*/
@UnsupportedAppUsage
+ @android.ravenwood.annotation.RavenwoodKeep
public static void addService(String name, IBinder service, boolean allowIsolated) {
addService(name, service, allowIsolated, IServiceManager.DUMP_FLAG_PRIORITY_DEFAULT);
}
@@ -207,6 +246,7 @@ public final class ServiceManager {
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @android.ravenwood.annotation.RavenwoodReplace
public static void addService(String name, IBinder service, boolean allowIsolated,
int dumpPriority) {
try {
@@ -216,6 +256,15 @@ public final class ServiceManager {
}
}
+ /** @hide */
+ public static void addService$ravenwood(String name, IBinder service, boolean allowIsolated,
+ int dumpPriority) {
+ synchronized (ServiceManager.class) {
+ // Ravenwood is a single-process environment, so it only needs to store locally
+ Preconditions.requireNonNullViaRavenwoodRule(sCache$ravenwood).put(name, service);
+ }
+ }
+
/**
* Retrieve an existing service called @a name from the
* service manager. Non-blocking.
@@ -366,6 +415,7 @@ public final class ServiceManager {
*
* @hide
*/
+ @android.ravenwood.annotation.RavenwoodKeepWholeClass
public static class ServiceNotFoundException extends Exception {
public ServiceNotFoundException(String name) {
super("No service published for: " + name);
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index a818919d184e..0a386913de59 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -56,7 +56,8 @@ import java.util.function.Predicate;
*/
@SystemApi
@RavenwoodKeepWholeClass
-@RavenwoodNativeSubstitutionClass("com.android.hoststubgen.nativesubstitution.SystemProperties_host")
+@RavenwoodNativeSubstitutionClass(
+ "com.android.platform.test.ravenwood.nativesubstitution.SystemProperties_host")
public class SystemProperties {
private static final String TAG = "SystemProperties";
private static final boolean TRACK_KEY_ACCESS = false;
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 89576ed62afe..be17d7c0f0db 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -163,19 +163,16 @@ public class UserManager {
* User type representing a managed profile, which is a profile that is to be managed by a
* device policy controller (DPC).
* The intended purpose is for work profiles, which are managed by a corporate entity.
- * @hide
*/
- @SystemApi
+ @FlaggedApi(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE)
public static final String USER_TYPE_PROFILE_MANAGED = "android.os.usertype.profile.MANAGED";
/**
* User type representing a clone profile. Clone profile is a user profile type used to run
* second instance of an otherwise single user App (eg, messengers). Currently only the
* {@link android.content.pm.UserInfo#isMain()} user can have a clone profile.
- *
- * @hide
*/
- @SystemApi
+ @FlaggedApi(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE)
public static final String USER_TYPE_PROFILE_CLONE = "android.os.usertype.profile.CLONE";
@@ -184,10 +181,8 @@ public class UserManager {
* as an alternative user-space to install and use sensitive apps.
* UI surfaces can adopt an alternative strategy to show apps belonging to this profile, in line
* with their sensitive nature.
- * @hide
*/
@FlaggedApi(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE)
- @SystemApi
public static final String USER_TYPE_PROFILE_PRIVATE = "android.os.usertype.profile.PRIVATE";
/**
@@ -1785,7 +1780,11 @@ public class UserManager {
/**
* Specifies whether the user is allowed to modify default apps in settings.
*
- * <p>This restriction can be set by device or profile owner.
+ * <p>A device owner and a profile owner can set this restriction. When it is set by a
+ * device owner, it applies globally - i.e., modifying of default apps in Settings for all
+ * users is disallowed. When it is set by a profile owner on the primary user or by a profile
+ * owner of an organization-owned managed profile on the parent profile, modifying of
+ * default apps in Settings for the primary user is disallowed.
*
* <p>The default value is <code>false</code>.
*
@@ -1921,6 +1920,8 @@ public class UserManager {
* @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
+ // TODO (b/325886480): update the flag to
+ // "com.android.net.thread.platform.flags.Flags.FLAG_THREAD_USER_RESTRICTION_ENABLED"
@FlaggedApi("com.android.net.thread.flags.thread_user_restriction_enabled")
public static final String DISALLOW_THREAD_NETWORK = "no_thread_network";
@@ -3259,7 +3260,11 @@ public class UserManager {
return isProfile(mUserId);
}
- private boolean isProfile(@UserIdInt int userId) {
+ /**
+ * Returns whether the specified user is a profile.
+ * @hide
+ */
+ public boolean isProfile(@UserIdInt int userId) {
final String profileType = getProfileType(userId);
return profileType != null && !profileType.equals("");
}
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index c9c91fc49aeb..efbd96bc35cb 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -591,9 +591,14 @@ public abstract class VibrationEffect implements Parcelable {
/**
* Scale given vibration intensity by the given factor.
*
+ * <p> This scale is not necessarily linear and may apply a gamma correction to the scale
+ * factor before using it.
+ *
* @param intensity relative intensity of the effect, must be between 0 and 1
* @param scaleFactor scale factor to be applied to the intensity. Values within [0,1) will
* scale down the intensity, values larger than 1 will scale up
+ * @return the scaled intensity which will be values within [0, 1].
+ *
* @hide
*/
public static float scale(float intensity, float scaleFactor) {
@@ -624,6 +629,20 @@ public abstract class VibrationEffect implements Parcelable {
}
/**
+ * Performs a linear scaling on the given vibration intensity by the given factor.
+ *
+ * @param intensity relative intensity of the effect, must be between 0 and 1.
+ * @param scaleFactor scale factor to be applied to the intensity. Values within [0,1) will
+ * scale down the intensity, values larger than 1 will scale up.
+ * @return the scaled intensity which will be values within [0, 1].
+ *
+ * @hide
+ */
+ public static float scaleLinearly(float intensity, float scaleFactor) {
+ return MathUtils.constrain(intensity * scaleFactor, 0f, 1f);
+ }
+
+ /**
* Returns a compact version of the {@link #toString()} result for debugging purposes.
*
* @hide
diff --git a/core/java/android/os/WakeLockStats.java b/core/java/android/os/WakeLockStats.java
index 69e70a0a8b07..3769f38a9fbf 100644
--- a/core/java/android/os/WakeLockStats.java
+++ b/core/java/android/os/WakeLockStats.java
@@ -23,17 +23,21 @@ import java.util.List;
/**
* Snapshot of wake lock stats.
- * @hide
+ *
+ * @hide
*/
@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class WakeLockStats implements Parcelable {
- /** @hide */
- public static class WakeLock {
- public final int uid;
- @NonNull
- public final String name;
+ public static class WakeLockData {
+
+ public static final WakeLockData EMPTY = new WakeLockData(
+ /* timesAcquired= */ 0, /* totalTimeHeldMs= */ 0, /* timeHeldMs= */ 0);
+
+ /** How many times this wakelock has been acquired. */
public final int timesAcquired;
+
+ /** Time in milliseconds that the lock has been held in total. */
public final long totalTimeHeldMs;
/**
@@ -41,26 +45,34 @@ public final class WakeLockStats implements Parcelable {
*/
public final long timeHeldMs;
- public WakeLock(int uid, @NonNull String name, int timesAcquired, long totalTimeHeldMs,
- long timeHeldMs) {
- this.uid = uid;
- this.name = name;
+ public WakeLockData(int timesAcquired, long totalTimeHeldMs, long timeHeldMs) {
this.timesAcquired = timesAcquired;
this.totalTimeHeldMs = totalTimeHeldMs;
this.timeHeldMs = timeHeldMs;
}
- private WakeLock(Parcel in) {
- uid = in.readInt();
- name = in.readString();
+ /**
+ * Whether the fields are able to construct a valid wakelock.
+ */
+ public boolean isDataValid() {
+ final boolean isDataReasonable = timesAcquired > 0
+ && totalTimeHeldMs > 0
+ && timeHeldMs >= 0
+ && totalTimeHeldMs >= timeHeldMs;
+ return isEmpty() || isDataReasonable;
+ }
+
+ private boolean isEmpty() {
+ return timesAcquired == 0 && totalTimeHeldMs == 0 && timeHeldMs == 0;
+ }
+
+ private WakeLockData(Parcel in) {
timesAcquired = in.readInt();
totalTimeHeldMs = in.readLong();
timeHeldMs = in.readLong();
}
private void writeToParcel(Parcel out) {
- out.writeInt(uid);
- out.writeString(name);
out.writeInt(timesAcquired);
out.writeLong(totalTimeHeldMs);
out.writeLong(timeHeldMs);
@@ -68,21 +80,98 @@ public final class WakeLockStats implements Parcelable {
@Override
public String toString() {
+ return "WakeLockData{"
+ + "timesAcquired="
+ + timesAcquired
+ + ", totalTimeHeldMs="
+ + totalTimeHeldMs
+ + ", timeHeldMs="
+ + timeHeldMs
+ + "}";
+ }
+ }
+
+ /** @hide */
+ public static class WakeLock {
+
+ public static final String NAME_AGGREGATED = "wakelockstats_aggregated";
+
+ public final int uid;
+ @NonNull public final String name;
+ public final boolean isAggregated;
+
+ /** Wakelock data on both foreground and background. */
+ @NonNull public final WakeLockData totalWakeLockData;
+
+ /** Wakelock data on background. */
+ @NonNull public final WakeLockData backgroundWakeLockData;
+
+ public WakeLock(
+ int uid,
+ @NonNull String name,
+ boolean isAggregated,
+ @NonNull WakeLockData totalWakeLockData,
+ @NonNull WakeLockData backgroundWakeLockData) {
+ this.uid = uid;
+ this.name = name;
+ this.isAggregated = isAggregated;
+ this.totalWakeLockData = totalWakeLockData;
+ this.backgroundWakeLockData = backgroundWakeLockData;
+ }
+
+ /** Whether the combination of total and background wakelock data is invalid. */
+ public static boolean isDataValid(
+ WakeLockData totalWakeLockData, WakeLockData backgroundWakeLockData) {
+ return totalWakeLockData.totalTimeHeldMs > 0
+ && totalWakeLockData.isDataValid()
+ && backgroundWakeLockData.isDataValid()
+ && totalWakeLockData.timesAcquired >= backgroundWakeLockData.timesAcquired
+ && totalWakeLockData.totalTimeHeldMs >= backgroundWakeLockData.totalTimeHeldMs
+ && totalWakeLockData.timeHeldMs >= backgroundWakeLockData.timeHeldMs;
+ }
+
+ private WakeLock(Parcel in) {
+ uid = in.readInt();
+ name = in.readString();
+ isAggregated = in.readBoolean();
+ totalWakeLockData = new WakeLockData(in);
+ backgroundWakeLockData = new WakeLockData(in);
+ }
+
+ private void writeToParcel(Parcel out) {
+ out.writeInt(uid);
+ out.writeString(name);
+ out.writeBoolean(isAggregated);
+ totalWakeLockData.writeToParcel(out);
+ backgroundWakeLockData.writeToParcel(out);
+ }
+
+ @Override
+ public String toString() {
return "WakeLock{"
- + "uid=" + uid
- + ", name='" + name + '\''
- + ", timesAcquired=" + timesAcquired
- + ", totalTimeHeldMs=" + totalTimeHeldMs
- + ", timeHeldMs=" + timeHeldMs
- + '}';
+ + "uid="
+ + uid
+ + ", name='"
+ + name
+ + '\''
+ + ", isAggregated="
+ + isAggregated
+ + ", totalWakeLockData="
+ + totalWakeLockData
+ + ", backgroundWakeLockData="
+ + backgroundWakeLockData
+ + '}';
}
}
private final List<WakeLock> mWakeLocks;
+ private final List<WakeLock> mAggregatedWakeLocks;
- /** @hide **/
- public WakeLockStats(@NonNull List<WakeLock> wakeLocks) {
+ /** @hide */
+ public WakeLockStats(
+ @NonNull List<WakeLock> wakeLocks, @NonNull List<WakeLock> aggregatedWakeLocks) {
mWakeLocks = wakeLocks;
+ mAggregatedWakeLocks = aggregatedWakeLocks;
}
@NonNull
@@ -90,22 +179,38 @@ public final class WakeLockStats implements Parcelable {
return mWakeLocks;
}
+ @NonNull
+ public List<WakeLock> getAggregatedWakeLocks() {
+ return mAggregatedWakeLocks;
+ }
+
private WakeLockStats(Parcel in) {
- final int size = in.readInt();
- mWakeLocks = new ArrayList<>(size);
- for (int i = 0; i < size; i++) {
+ final int wakelockSize = in.readInt();
+ mWakeLocks = new ArrayList<>(wakelockSize);
+ for (int i = 0; i < wakelockSize; i++) {
mWakeLocks.add(new WakeLock(in));
}
+ final int aggregatedWakelockSize = in.readInt();
+ mAggregatedWakeLocks = new ArrayList<>(aggregatedWakelockSize);
+ for (int i = 0; i < aggregatedWakelockSize; i++) {
+ mAggregatedWakeLocks.add(new WakeLock(in));
+ }
}
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
- final int size = mWakeLocks.size();
- out.writeInt(size);
- for (int i = 0; i < size; i++) {
+ final int wakelockSize = mWakeLocks.size();
+ out.writeInt(wakelockSize);
+ for (int i = 0; i < wakelockSize; i++) {
WakeLock stats = mWakeLocks.get(i);
stats.writeToParcel(out);
}
+ final int aggregatedWakelockSize = mAggregatedWakeLocks.size();
+ out.writeInt(aggregatedWakelockSize);
+ for (int i = 0; i < aggregatedWakelockSize; i++) {
+ WakeLock stats = mAggregatedWakeLocks.get(i);
+ stats.writeToParcel(out);
+ }
}
@NonNull
@@ -127,6 +232,13 @@ public final class WakeLockStats implements Parcelable {
@Override
public String toString() {
- return "WakeLockStats " + mWakeLocks;
+ return "WakeLockStats{"
+ + "mWakeLocks: ["
+ + mWakeLocks
+ + "]"
+ + ", mAggregatedWakeLocks: ["
+ + mAggregatedWakeLocks
+ + "]"
+ + '}';
}
}
diff --git a/core/java/android/os/vibrator/PrebakedSegment.java b/core/java/android/os/vibrator/PrebakedSegment.java
index a035092e314f..39f841226e4e 100644
--- a/core/java/android/os/vibrator/PrebakedSegment.java
+++ b/core/java/android/os/vibrator/PrebakedSegment.java
@@ -137,6 +137,14 @@ public final class PrebakedSegment extends VibrationEffectSegment {
/** @hide */
@NonNull
@Override
+ public PrebakedSegment scaleLinearly(float scaleFactor) {
+ // Prebaked effect strength cannot be scaled with this method.
+ return this;
+ }
+
+ /** @hide */
+ @NonNull
+ @Override
public PrebakedSegment applyEffectStrength(int effectStrength) {
if (effectStrength != mEffectStrength && isValidEffectStrength(effectStrength)) {
return new PrebakedSegment(mEffectId, mFallback, effectStrength);
diff --git a/core/java/android/os/vibrator/PrimitiveSegment.java b/core/java/android/os/vibrator/PrimitiveSegment.java
index 95d97bfe4ad1..3c84bcda639b 100644
--- a/core/java/android/os/vibrator/PrimitiveSegment.java
+++ b/core/java/android/os/vibrator/PrimitiveSegment.java
@@ -98,8 +98,24 @@ public final class PrimitiveSegment extends VibrationEffectSegment {
@NonNull
@Override
public PrimitiveSegment scale(float scaleFactor) {
- return new PrimitiveSegment(mPrimitiveId, VibrationEffect.scale(mScale, scaleFactor),
- mDelay);
+ float newScale = VibrationEffect.scale(mScale, scaleFactor);
+ if (Float.compare(mScale, newScale) == 0) {
+ return this;
+ }
+
+ return new PrimitiveSegment(mPrimitiveId, newScale, mDelay);
+ }
+
+ /** @hide */
+ @NonNull
+ @Override
+ public PrimitiveSegment scaleLinearly(float scaleFactor) {
+ float newScale = VibrationEffect.scaleLinearly(mScale, scaleFactor);
+ if (Float.compare(mScale, newScale) == 0) {
+ return this;
+ }
+
+ return new PrimitiveSegment(mPrimitiveId, newScale, mDelay);
}
/** @hide */
diff --git a/core/java/android/os/vibrator/RampSegment.java b/core/java/android/os/vibrator/RampSegment.java
index 5f9d1024d9a5..09d2e26be2a5 100644
--- a/core/java/android/os/vibrator/RampSegment.java
+++ b/core/java/android/os/vibrator/RampSegment.java
@@ -159,6 +159,21 @@ public final class RampSegment extends VibrationEffectSegment {
/** @hide */
@NonNull
@Override
+ public RampSegment scaleLinearly(float scaleFactor) {
+ float newStartAmplitude = VibrationEffect.scaleLinearly(mStartAmplitude, scaleFactor);
+ float newEndAmplitude = VibrationEffect.scaleLinearly(mEndAmplitude, scaleFactor);
+ if (Float.compare(mStartAmplitude, newStartAmplitude) == 0
+ && Float.compare(mEndAmplitude, newEndAmplitude) == 0) {
+ return this;
+ }
+ return new RampSegment(newStartAmplitude, newEndAmplitude, mStartFrequencyHz,
+ mEndFrequencyHz,
+ mDuration);
+ }
+
+ /** @hide */
+ @NonNull
+ @Override
public RampSegment applyEffectStrength(int effectStrength) {
return this;
}
diff --git a/core/java/android/os/vibrator/StepSegment.java b/core/java/android/os/vibrator/StepSegment.java
index 9576a5bba1f1..fa083c176a27 100644
--- a/core/java/android/os/vibrator/StepSegment.java
+++ b/core/java/android/os/vibrator/StepSegment.java
@@ -137,8 +137,25 @@ public final class StepSegment extends VibrationEffectSegment {
if (Float.compare(mAmplitude, VibrationEffect.DEFAULT_AMPLITUDE) == 0) {
return this;
}
- return new StepSegment(VibrationEffect.scale(mAmplitude, scaleFactor), mFrequencyHz,
- mDuration);
+ float newAmplitude = VibrationEffect.scale(mAmplitude, scaleFactor);
+ if (Float.compare(newAmplitude, mAmplitude) == 0) {
+ return this;
+ }
+ return new StepSegment(newAmplitude, mFrequencyHz, mDuration);
+ }
+
+ /** @hide */
+ @NonNull
+ @Override
+ public StepSegment scaleLinearly(float scaleFactor) {
+ if (Float.compare(mAmplitude, VibrationEffect.DEFAULT_AMPLITUDE) == 0) {
+ return this;
+ }
+ float newAmplitude = VibrationEffect.scaleLinearly(mAmplitude, scaleFactor);
+ if (Float.compare(newAmplitude, mAmplitude) == 0) {
+ return this;
+ }
+ return new StepSegment(newAmplitude, mFrequencyHz, mDuration);
}
/** @hide */
diff --git a/core/java/android/os/vibrator/VibrationEffectSegment.java b/core/java/android/os/vibrator/VibrationEffectSegment.java
index 17ac36f3ab37..e1fb4e361008 100644
--- a/core/java/android/os/vibrator/VibrationEffectSegment.java
+++ b/core/java/android/os/vibrator/VibrationEffectSegment.java
@@ -96,6 +96,9 @@ public abstract class VibrationEffectSegment implements Parcelable {
/**
* Scale the segment intensity with the given factor.
*
+ * <p> This scale is not necessarily linear and may apply a gamma correction to the scale
+ * factor before using it.
+ *
* @param scaleFactor scale factor to be applied to the intensity. Values within [0,1) will
* scale down the intensity, values larger than 1 will scale up
*
@@ -105,6 +108,17 @@ public abstract class VibrationEffectSegment implements Parcelable {
public abstract <T extends VibrationEffectSegment> T scale(float scaleFactor);
/**
+ * Performs a linear scaling on the segment intensity with the given factor.
+ *
+ * @param scaleFactor scale factor to be applied to the intensity. Values within [0,1) will
+ * scale down the intensity, values larger than 1 will scale up
+ *
+ * @hide
+ */
+ @NonNull
+ public abstract <T extends VibrationEffectSegment> T scaleLinearly(float scaleFactor);
+
+ /**
* Applies given effect strength to prebaked effects.
*
* @param effectStrength new effect strength to be applied, one of
diff --git a/core/java/android/permission/PermissionGroupUsage.java b/core/java/android/permission/PermissionGroupUsage.java
index 49b7463533e4..6895d3c67f06 100644
--- a/core/java/android/permission/PermissionGroupUsage.java
+++ b/core/java/android/permission/PermissionGroupUsage.java
@@ -17,6 +17,7 @@
package android.permission;
import android.annotation.CurrentTimeMillisLong;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -26,8 +27,8 @@ import com.android.internal.util.DataClass;
/**
* Represents the usage of a permission group by an app. Supports package name, user, permission
- * group, whether or not the access is running or recent, whether the access is tied to a phone
- * call, and an optional special attribution tag, label and proxy label.
+ * group, persistent device Id, whether or not the access is running or recent, whether the access
+ * is tied to a phone call, and an optional special attribution tag, label and proxy label.
*
* @hide
*/
@@ -48,6 +49,7 @@ public final class PermissionGroupUsage implements Parcelable {
private final @Nullable CharSequence mAttributionTag;
private final @Nullable CharSequence mAttributionLabel;
private final @Nullable CharSequence mProxyLabel;
+ private final @NonNull String mPersistentDeviceId;
@@ -79,7 +81,8 @@ public final class PermissionGroupUsage implements Parcelable {
boolean phoneCall,
@Nullable CharSequence attributionTag,
@Nullable CharSequence attributionLabel,
- @Nullable CharSequence proxyLabel) {
+ @Nullable CharSequence proxyLabel,
+ @NonNull String persistentDeviceId) {
this.mPackageName = packageName;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mPackageName);
@@ -93,6 +96,9 @@ public final class PermissionGroupUsage implements Parcelable {
this.mAttributionTag = attributionTag;
this.mAttributionLabel = attributionLabel;
this.mProxyLabel = proxyLabel;
+ this.mPersistentDeviceId = persistentDeviceId;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mPersistentDeviceId);
// onConstructed(); // You can define this method to get a callback
}
@@ -170,6 +176,12 @@ public final class PermissionGroupUsage implements Parcelable {
return mProxyLabel;
}
+ @DataClass.Generated.Member
+ @FlaggedApi(android.permission.flags.Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
+ public @NonNull String getPersistentDeviceId() {
+ return mPersistentDeviceId;
+ }
+
@Override
@DataClass.Generated.Member
public String toString() {
@@ -185,7 +197,8 @@ public final class PermissionGroupUsage implements Parcelable {
"phoneCall = " + mPhoneCall + ", " +
"attributionTag = " + mAttributionTag + ", " +
"attributionLabel = " + mAttributionLabel + ", " +
- "proxyLabel = " + mProxyLabel +
+ "proxyLabel = " + mProxyLabel + ", " +
+ "persistentDeviceId = " + mPersistentDeviceId +
" }";
}
@@ -210,7 +223,8 @@ public final class PermissionGroupUsage implements Parcelable {
&& mPhoneCall == that.mPhoneCall
&& java.util.Objects.equals(mAttributionTag, that.mAttributionTag)
&& java.util.Objects.equals(mAttributionLabel, that.mAttributionLabel)
- && java.util.Objects.equals(mProxyLabel, that.mProxyLabel);
+ && java.util.Objects.equals(mProxyLabel, that.mProxyLabel)
+ && java.util.Objects.equals(mPersistentDeviceId, that.mPersistentDeviceId);
}
@Override
@@ -229,6 +243,7 @@ public final class PermissionGroupUsage implements Parcelable {
_hash = 31 * _hash + java.util.Objects.hashCode(mAttributionTag);
_hash = 31 * _hash + java.util.Objects.hashCode(mAttributionLabel);
_hash = 31 * _hash + java.util.Objects.hashCode(mProxyLabel);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mPersistentDeviceId);
return _hash;
}
@@ -252,6 +267,7 @@ public final class PermissionGroupUsage implements Parcelable {
if (mAttributionTag != null) dest.writeCharSequence(mAttributionTag);
if (mAttributionLabel != null) dest.writeCharSequence(mAttributionLabel);
if (mProxyLabel != null) dest.writeCharSequence(mProxyLabel);
+ dest.writeString(mPersistentDeviceId);
}
@Override
@@ -275,6 +291,7 @@ public final class PermissionGroupUsage implements Parcelable {
CharSequence attributionTag = (flg & 0x40) == 0 ? null : (CharSequence) in.readCharSequence();
CharSequence attributionLabel = (flg & 0x80) == 0 ? null : (CharSequence) in.readCharSequence();
CharSequence proxyLabel = (flg & 0x100) == 0 ? null : (CharSequence) in.readCharSequence();
+ String persistentDeviceId = in.readString();
this.mPackageName = packageName;
com.android.internal.util.AnnotationValidations.validate(
@@ -289,6 +306,9 @@ public final class PermissionGroupUsage implements Parcelable {
this.mAttributionTag = attributionTag;
this.mAttributionLabel = attributionLabel;
this.mProxyLabel = proxyLabel;
+ this.mPersistentDeviceId = persistentDeviceId;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mPersistentDeviceId);
// onConstructed(); // You can define this method to get a callback
}
@@ -308,10 +328,10 @@ public final class PermissionGroupUsage implements Parcelable {
};
@DataClass.Generated(
- time = 1645067417023L,
+ time = 1706285211875L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/permission/PermissionGroupUsage.java",
- inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final int mUid\nprivate final long mLastAccessTimeMillis\nprivate final @android.annotation.NonNull java.lang.String mPermissionGroupName\nprivate final boolean mActive\nprivate final boolean mPhoneCall\nprivate final @android.annotation.Nullable java.lang.CharSequence mAttributionTag\nprivate final @android.annotation.Nullable java.lang.CharSequence mAttributionLabel\nprivate final @android.annotation.Nullable java.lang.CharSequence mProxyLabel\nclass PermissionGroupUsage extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genEqualsHashCode=true, genToString=true)")
+ inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final int mUid\nprivate final long mLastAccessTimeMillis\nprivate final @android.annotation.NonNull java.lang.String mPermissionGroupName\nprivate final boolean mActive\nprivate final boolean mPhoneCall\nprivate final @android.annotation.Nullable java.lang.CharSequence mAttributionTag\nprivate final @android.annotation.Nullable java.lang.CharSequence mAttributionLabel\nprivate final @android.annotation.Nullable java.lang.CharSequence mProxyLabel\nprivate final @android.annotation.NonNull java.lang.String mPersistentDeviceId\nclass PermissionGroupUsage extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genEqualsHashCode=true, genToString=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index e6b8102764eb..8495f3747573 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -1329,7 +1329,9 @@ public final class PermissionManager {
public List<PermissionGroupUsage> getIndicatorAppOpUsageData(boolean micMuted) {
// Lazily initialize the usage helper
initializeUsageHelper();
- return mUsageHelper.getOpUsageData(micMuted);
+ boolean includeMicrophoneUsage = !micMuted;
+ return mUsageHelper.getOpUsageDataByDevice(includeMicrophoneUsage,
+ VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT);
}
/**
@@ -1942,25 +1944,27 @@ public final class PermissionManager {
*
* @param permissionName The name of the permission you are checking for.
* @param packageName The name of the package you are checking against.
- * @param persistentDeviceId The persistent device id you are checking against.
- * @param userId The user Id associated with context.
+ * @param persistentDeviceId The id of the physical device that you are checking permission
+ * against.
*
* @return If the package has the permission on the device, PERMISSION_GRANTED is
* returned. If it does not have the permission on the device, PERMISSION_DENIED
* is returned.
*
+ * @see VirtualDevice#getPersistentDeviceId()
* @see PackageManager#PERMISSION_GRANTED
* @see PackageManager#PERMISSION_DENIED
*
* @hide
*/
@SystemApi
+ @PermissionResult
@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
- public static int checkPermission(@NonNull String permissionName, @NonNull String packageName,
- @NonNull String persistentDeviceId, @UserIdInt int userId) {
+ public int checkPermission(@NonNull String permissionName, @NonNull String packageName,
+ @NonNull String persistentDeviceId) {
return sPackageNamePermissionCache.query(
new PackageNamePermissionQuery(permissionName, packageName, persistentDeviceId,
- userId));
+ mContext.getUserId()));
}
/**
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index 1f798baf1bd6..460b4dd9b86c 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -41,6 +41,8 @@ import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_AC
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
+import android.companion.virtual.VirtualDevice;
+import android.companion.virtual.VirtualDeviceManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.Attribution;
@@ -52,10 +54,12 @@ import android.location.LocationManager;
import android.media.AudioManager;
import android.os.Process;
import android.os.UserHandle;
+import android.permission.flags.Flags;
import android.provider.DeviceConfig;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
@@ -75,6 +79,8 @@ import java.util.Objects;
public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedListener,
AppOpsManager.OnOpStartedListener {
+ private static final String LOG_TAG = PermissionUsageHelper.class.getName();
+
/**
* Whether to show the mic and camera icons.
*/
@@ -159,6 +165,7 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis
private ArrayMap<UserHandle, Context> mUserContexts;
private PackageManager mPkgManager;
private AppOpsManager mAppOpsManager;
+ private VirtualDeviceManager mVirtualDeviceManager;
@GuardedBy("mAttributionChains")
private final ArrayMap<Integer, ArrayList<AccessChainLink>> mAttributionChains =
new ArrayMap<>();
@@ -172,6 +179,7 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis
mContext = context;
mPkgManager = context.getPackageManager();
mAppOpsManager = context.getSystemService(AppOpsManager.class);
+ mVirtualDeviceManager = context.getSystemService(VirtualDeviceManager.class);
mUserContexts = new ArrayMap<>();
mUserContexts.put(Process.myUserHandle(), mContext);
// TODO ntmyren: make this listen for flag enable/disable changes
@@ -280,9 +288,11 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis
}
/**
- * @see PermissionManager.getIndicatorAppOpUsageData
+ * Return Op usage for CAMERA, LOCATION AND MICROPHONE for all packages for a device.
+ * The returned data is to power privacy indicator.
*/
- public @NonNull List<PermissionGroupUsage> getOpUsageData(boolean isMicMuted) {
+ public @NonNull List<PermissionGroupUsage> getOpUsageDataByDevice(
+ boolean includeMicrophoneUsage, String deviceId) {
List<PermissionGroupUsage> usages = new ArrayList<>();
if (!shouldShowIndicators()) {
@@ -293,11 +303,11 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis
if (shouldShowLocationIndicator()) {
ops.addAll(LOCATION_OPS);
}
- if (!isMicMuted) {
+ if (includeMicrophoneUsage) {
ops.addAll(MIC_OPS);
}
- Map<String, List<OpUsage>> rawUsages = getOpUsages(ops);
+ Map<String, List<OpUsage>> rawUsages = getOpUsagesByDevice(ops, deviceId);
ArrayList<String> usedPermGroups = new ArrayList<>(rawUsages.keySet());
@@ -349,13 +359,40 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis
new PermissionGroupUsage(usage.packageName, usage.uid, usage.lastAccessTime,
permGroup,
usage.isRunning, isPhone, usage.attributionTag, attributionLabel,
- usagesWithLabels.valueAt(usageNum)));
+ usagesWithLabels.valueAt(usageNum),
+ VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT));
}
}
return usages;
}
+ /**
+ * Return Op usage for CAMERA, LOCATION AND MICROPHONE for all packages and all connected
+ * devices.
+ * The returned data is to power privacy indicator.
+ */
+ public @NonNull List<PermissionGroupUsage> getOpUsageDataForAllDevices(
+ boolean includeMicrophoneUsage) {
+ List<PermissionGroupUsage> allUsages = new ArrayList<>();
+ List<VirtualDevice> virtualDevices = mVirtualDeviceManager.getVirtualDevices();
+ ArraySet<String> persistentDeviceIds = new ArraySet<>();
+
+ for (int num = 0; num < virtualDevices.size(); num++) {
+ persistentDeviceIds.add(virtualDevices.get(num).getPersistentDeviceId());
+ }
+ persistentDeviceIds.add(VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT);
+
+ for (int index = 0; index < persistentDeviceIds.size(); index++) {
+ allUsages.addAll(
+ getOpUsageDataByDevice(includeMicrophoneUsage,
+ persistentDeviceIds.valueAt(index)));
+ }
+
+ return allUsages;
+ }
+
+
private void updateSubattributionLabelsMap(List<OpUsage> usages,
ArrayMap<String, Map<String, String>> subAttributionLabelsMap) {
if (usages == null || usages.isEmpty()) {
@@ -443,12 +480,24 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis
* running/recent info, if the usage is a phone call, per permission group.
*
* @param opNames a list of op names to get usage for
+ * @param deviceId which device to get op usage for
* @return A map of permission group -> list of usages that are recent or running
*/
- private Map<String, List<OpUsage>> getOpUsages(List<String> opNames) {
+ private Map<String, List<OpUsage>> getOpUsagesByDevice(List<String> opNames, String deviceId) {
List<AppOpsManager.PackageOps> ops;
try {
- ops = mAppOpsManager.getPackagesForOps(opNames.toArray(new String[opNames.size()]));
+ if (Flags.deviceAwarePermissionApisEnabled()) {
+ ops = mAppOpsManager.getPackagesForOps(opNames.toArray(new String[opNames.size()]),
+ deviceId);
+ } else if (!Objects.equals(deviceId,
+ VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT)) {
+ Slog.w(LOG_TAG,
+ "device_aware_permission_apis_enabled flag not enabled when deviceId is "
+ + "not default");
+ return Collections.emptyMap();
+ } else {
+ ops = mAppOpsManager.getPackagesForOps(opNames.toArray(new String[opNames.size()]));
+ }
} catch (NullPointerException e) {
// older builds might not support all the app-ops requested
return Collections.emptyMap();
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index 9d7fb7018d52..de7008b19f54 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -96,9 +96,45 @@ flag {
}
flag {
+ name: "sensitive_notification_app_protection"
+ namespace: "permissions"
+ description: "This flag controls the sensitive notification app protections while screen sharing"
+ bug: "312784351"
+ # Referenced in WM where WM starts before DeviceConfig
+ is_fixed_read_only: true
+}
+
+flag {
name: "device_aware_permissions_enabled"
is_fixed_read_only: true
namespace: "permissions"
description: "When the flag is off no permissions can be device aware"
bug: "274852670"
-} \ No newline at end of file
+}
+
+flag {
+ name: "get_emergency_role_holder_api_enabled"
+ is_fixed_read_only: true
+ namespace: "permissions"
+ description: "Enables the getEmergencyRoleHolder API."
+ bug: "323157319"
+}
+
+flag {
+ name: "new_permission_gid_enabled"
+ is_fixed_read_only: true
+ namespace: "permissions"
+ description: "Enable new permission GID implementation"
+ bug: "325137277"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "ignore_process_text"
+ namespace: "permissions"
+ description: "Ignore activities that handle PROCESS_TEXT in TextView"
+ bug: "325356776"
+}
+
diff --git a/core/java/android/print/pdf/TEST_MAPPING b/core/java/android/print/pdf/TEST_MAPPING
deleted file mode 100644
index d763598f5ba0..000000000000
--- a/core/java/android/print/pdf/TEST_MAPPING
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "presubmit": [
- {
- "name": "CtsPdfTestCases"
- }
- ]
-}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ec4d5876070a..50adc40e719d 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -12481,6 +12481,24 @@ public final class Settings {
public static void setLocationProviderEnabled(ContentResolver cr,
String provider, boolean enabled) {
}
+
+ /**
+ * List of system components that support restore in a V-> U OS downgrade but do not have
+ * RestoreAnyVersion set to true. Value set before system restore.
+ * This setting is not B&Rd
+ * List is stored as a comma-separated string of package names e.g. "a,b,c"
+ * @hide
+ */
+ public static final String V_TO_U_RESTORE_ALLOWLIST = "v_to_u_restore_allowlist";
+
+ /**
+ * List of system components that have RestoreAnyVersion set to true but do not support
+ * restore in a V-> U OS downgrade. Value set before system restore.
+ * This setting is not B&Rd
+ * List is stored as a comma-separated string of package names e.g. "a,b,c"
+ * @hide
+ */
+ public static final String V_TO_U_RESTORE_DENYLIST = "v_to_u_restore_denylist";
}
/**
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index 43163b3b9051..76314546b4f0 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -15,10 +15,11 @@ flag {
}
flag {
- name: "mgf1_digest_setter"
+ name: "mgf1_digest_setter_v2"
namespace: "hardware_backed_security"
description: "Feature flag for mgf1 digest setter in key generation and import parameters."
bug: "308378912"
+ is_fixed_read_only: true
}
flag {
diff --git a/core/java/android/service/chooser/ChooserResult.java b/core/java/android/service/chooser/ChooserResult.java
index 4603be114508..2d56ec730836 100644
--- a/core/java/android/service/chooser/ChooserResult.java
+++ b/core/java/android/service/chooser/ChooserResult.java
@@ -20,6 +20,7 @@ import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.compat.annotation.Overridable;
@@ -91,6 +92,7 @@ public final class ChooserResult implements Parcelable {
}
/** @hide */
+ @TestApi
public ChooserResult(@ResultType int type, @Nullable ComponentName componentName,
boolean isShortcut) {
mType = type;
diff --git a/core/java/android/service/chooser/CustomChoosers.java b/core/java/android/service/chooser/CustomChoosers.java
deleted file mode 100644
index 5b89432b956d..000000000000
--- a/core/java/android/service/chooser/CustomChoosers.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.service.chooser;
-
-import android.annotation.FlaggedApi;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.content.Intent;
-import android.content.pm.ResolveInfo;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Static helper methods that privileged clients can use to initiate Share sessions with extra
- * customization options that aren't usually available in the stock "Resolver/Chooser" flows.
- *
- * @hide
- */
-@FlaggedApi(Flags.FLAG_SUPPORT_NFC_RESOLVER)
-@SystemApi
-public class CustomChoosers {
- /**
- * Intent action to start a Share session with additional customization options. Clients should
- * use the helper methods in this class to configure their customized share intents, and should
- * avoid using this action to construct their own intents directly.
- */
- private static final String ACTION_SHOW_CUSTOMIZED_RESOLVER =
- "android.service.chooser.action.SHOW_CUSTOMIZED_RESOLVER";
-
- /**
- * "Extras" key for an ArrayList of {@link ResolveInfo} records which are to be shown as the
- * targets in the customized share session.
- *
- * @hide
- */
- public static final String EXTRA_RESOLVE_INFOS = "android.service.chooser.extra.RESOLVE_INFOS";
-
- /**
- * Build an {@link Intent} to dispatch a "Chooser flow" that picks a target resolution for the
- * specified {@code target} intent, styling the Chooser UI according to the specified
- * customization parameters.
- *
- * @param target The ambiguous intent that should be resolved to a specific target selected
- * via the Chooser flow.
- * @param title An optional "headline" string to display at the top of the Chooser UI, or null
- * to use the system default.
- * @param resolutionList Explicit resolution info for targets that should be shown in the
- * dispatched Share UI.
- *
- * @hide
- */
- @FlaggedApi(Flags.FLAG_SUPPORT_NFC_RESOLVER)
- @SystemApi
- @NonNull
- public static Intent createNfcResolverIntent(
- @NonNull Intent target,
- @Nullable CharSequence title,
- @NonNull List<ResolveInfo> resolutionList) {
- Intent resolverIntent = new Intent(ACTION_SHOW_CUSTOMIZED_RESOLVER);
- resolverIntent.putExtra(Intent.EXTRA_INTENT, target);
- resolverIntent.putExtra(Intent.EXTRA_TITLE, title);
- resolverIntent.putParcelableArrayListExtra(
- EXTRA_RESOLVE_INFOS, new ArrayList<>(resolutionList));
- return resolverIntent;
- }
-
- private CustomChoosers() {}
-}
diff --git a/core/java/android/service/chooser/flags.aconfig b/core/java/android/service/chooser/flags.aconfig
index add575b23792..00236dfa7876 100644
--- a/core/java/android/service/chooser/flags.aconfig
+++ b/core/java/android/service/chooser/flags.aconfig
@@ -15,13 +15,6 @@ flag {
}
flag {
- name: "support_nfc_resolver"
- namespace: "systemui"
- description: "This flag controls the new NFC 'resolver' activity"
- bug: "268089816"
-}
-
-flag {
name: "chooser_payload_toggling"
namespace: "intentresolver"
description: "This flag controls content toggling in Chooser"
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index f169ecd1a4e5..d9ca935b35b3 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -216,7 +216,7 @@ public class ZenModeConfig implements Parcelable {
private static final String ALLOW_ATT_SCREEN_ON = "visualScreenOn";
private static final String ALLOW_ATT_CONV = "convos";
private static final String ALLOW_ATT_CONV_FROM = "convosFrom";
- private static final String ALLOW_ATT_CHANNELS = "priorityChannels";
+ private static final String ALLOW_ATT_CHANNELS = "priorityChannelsAllowed";
private static final String POLICY_USER_MODIFIED_FIELDS = "policyUserModifiedFields";
private static final String DISALLOW_TAG = "disallow";
private static final String DISALLOW_ATT_VISUAL_EFFECTS = "visualEffects";
@@ -813,9 +813,9 @@ public class ZenModeConfig implements Parcelable {
rt.enabler = parser.getAttributeValue(null, RULE_ATT_ENABLER);
rt.condition = readConditionXml(parser);
- // all default rules and user created rules updated to zenMode important interruptions
- if (rt.zenMode != Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+ if (!Flags.modesApi() && rt.zenMode != Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
&& Condition.isValidId(rt.conditionId, SYSTEM_AUTHORITY)) {
+ // all default rules and user created rules updated to zenMode important interruptions
Slog.i(TAG, "Updating zenMode of automatic rule " + rt.name);
rt.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
}
diff --git a/core/java/android/service/notification/flags.aconfig b/core/java/android/service/notification/flags.aconfig
index 446fe3de6482..c5acc2ceb968 100644
--- a/core/java/android/service/notification/flags.aconfig
+++ b/core/java/android/service/notification/flags.aconfig
@@ -1,4 +1,5 @@
package: "android.service.notification"
+container: "system"
flag {
name: "ranking_update_ashmem"
@@ -12,6 +13,7 @@ flag {
namespace: "systemui"
description: "This flag controls the redacting of sensitive notifications from untrusted NotificationListenerServices"
bug: "306271190"
+ is_exported: true
}
flag {
diff --git a/core/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl b/core/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl
new file mode 100644
index 000000000000..bbb4bc6c0272
--- /dev/null
+++ b/core/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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 android.service.ondeviceintelligence;
+
+import android.os.PersistableBundle;
+import android.os.ParcelFileDescriptor;
+import android.os.ICancellationSignal;
+import android.os.RemoteCallback;
+import android.app.ondeviceintelligence.IDownloadCallback;
+import android.app.ondeviceintelligence.Feature;
+import android.app.ondeviceintelligence.IFeatureCallback;
+import android.app.ondeviceintelligence.IListFeaturesCallback;
+import android.app.ondeviceintelligence.IFeatureDetailsCallback;
+import com.android.internal.infra.AndroidFuture;
+
+
+/**
+ * Interface for a concrete implementation to provide on device intelligence services.
+ *
+ * @hide
+ */
+oneway interface IOnDeviceIntelligenceService {
+ void getVersion(in RemoteCallback remoteCallback);
+ void getFeature(in int featureId, in IFeatureCallback featureCallback);
+ void listFeatures(in IListFeaturesCallback listFeaturesCallback);
+ void getFeatureDetails(in Feature feature, in IFeatureDetailsCallback featureDetailsCallback);
+ void getReadOnlyFileDescriptor(in String fileName, in AndroidFuture<ParcelFileDescriptor> future);
+ void getReadOnlyFeatureFileDescriptorMap(in Feature feature, in RemoteCallback remoteCallback);
+ void requestFeatureDownload(in Feature feature, in ICancellationSignal cancellationSignal, in IDownloadCallback downloadCallback);
+} \ No newline at end of file
diff --git a/core/java/android/service/ondeviceintelligence/IOnDeviceTrustedInferenceService.aidl b/core/java/android/service/ondeviceintelligence/IOnDeviceTrustedInferenceService.aidl
new file mode 100644
index 000000000000..08eb9278fcc4
--- /dev/null
+++ b/core/java/android/service/ondeviceintelligence/IOnDeviceTrustedInferenceService.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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 android.service.ondeviceintelligence;
+
+import android.app.ondeviceintelligence.IStreamingResponseCallback;
+import android.app.ondeviceintelligence.IResponseCallback;
+import android.app.ondeviceintelligence.ITokenCountCallback;
+import android.app.ondeviceintelligence.IProcessingSignal;
+import android.app.ondeviceintelligence.Content;
+import android.app.ondeviceintelligence.Feature;
+import android.os.ICancellationSignal;
+import android.service.ondeviceintelligence.IRemoteStorageService;
+
+
+/**
+ * Interface for a concrete implementation to provide on device trusted inference.
+ *
+ * @hide
+ */
+oneway interface IOnDeviceTrustedInferenceService {
+ void registerRemoteStorageService(in IRemoteStorageService storageService);
+ void requestTokenCount(in Feature feature, in Content request, in ICancellationSignal cancellationSignal,
+ in ITokenCountCallback tokenCountCallback);
+ void processRequest(in Feature feature, in Content request, in int requestType,
+ in ICancellationSignal cancellationSignal, in IProcessingSignal processingSignal,
+ in IResponseCallback callback);
+ void processRequestStreaming(in Feature feature, in Content request, in int requestType,
+ in ICancellationSignal cancellationSignal, in IProcessingSignal processingSignal,
+ in IStreamingResponseCallback callback);
+} \ No newline at end of file
diff --git a/core/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl b/core/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl
new file mode 100644
index 000000000000..a6f49e17d80e
--- /dev/null
+++ b/core/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl
@@ -0,0 +1,34 @@
+/*
+ * 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 android.service.ondeviceintelligence;
+
+import android.app.ondeviceintelligence.Feature;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteCallback;
+
+import com.android.internal.infra.AndroidFuture;
+
+/**
+ * Interface for a concrete implementation to provide access to storage read access
+ * for the isolated process.
+ *
+ * @hide
+ */
+oneway interface IRemoteStorageService {
+ void getReadOnlyFileDescriptor(in String filePath, in AndroidFuture<ParcelFileDescriptor> future);
+ void getReadOnlyFeatureFileDescriptorMap(in Feature feature, in RemoteCallback remoteCallback);
+} \ No newline at end of file
diff --git a/core/java/android/service/ondeviceintelligence/OWNERS b/core/java/android/service/ondeviceintelligence/OWNERS
new file mode 100644
index 000000000000..09774f78d712
--- /dev/null
+++ b/core/java/android/service/ondeviceintelligence/OWNERS
@@ -0,0 +1 @@
+file:/core/java/android/app/ondeviceintelligence/OWNERS
diff --git a/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java b/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
new file mode 100644
index 000000000000..0cba1d37721a
--- /dev/null
+++ b/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
@@ -0,0 +1,383 @@
+/*
+ * 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.service.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.app.ondeviceintelligence.DownloadCallback;
+import android.app.ondeviceintelligence.Feature;
+import android.app.ondeviceintelligence.FeatureDetails;
+import android.app.ondeviceintelligence.IDownloadCallback;
+import android.app.ondeviceintelligence.IFeatureCallback;
+import android.app.ondeviceintelligence.IFeatureDetailsCallback;
+import android.app.ondeviceintelligence.IListFeaturesCallback;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceManager;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.IBinder;
+import android.os.ICancellationSignal;
+import android.os.OutcomeReceiver;
+import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.infra.AndroidFuture;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Consumer;
+import java.util.function.LongConsumer;
+
+/**
+ * Abstract base class for performing setup for on-device inference and providing file access to
+ * the isolated counter part {@link OnDeviceTrustedInferenceService}.
+ *
+ * <p> A service that provides configuration and model files relevant to performing inference on
+ * device. The system's default OnDeviceIntelligenceService implementation is configured in
+ * {@code config_defaultOnDeviceIntelligenceService}. If this config has no value, a stub is
+ * returned.
+ *
+ * <pre>
+ * {@literal
+ * <service android:name=".SampleOnDeviceIntelligenceService"
+ * android:permission="android.permission.BIND_ON_DEVICE_INTELLIGENCE_SERVICE">
+ * </service>}
+ * </pre>
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public abstract class OnDeviceIntelligenceService extends Service {
+ private static final String TAG = OnDeviceIntelligenceService.class.getSimpleName();
+
+ /**
+ * The {@link Intent} that must be declared as handled by the service. To be supported, the
+ * service must also require the
+ * {@link android.Manifest.permission#BIND_ON_DEVICE_INTELLIGENCE_SERVICE}
+ * permission so that other applications can not abuse it.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE =
+ "android.service.ondeviceintelligence.OnDeviceIntelligenceService";
+
+ /**
+ * @hide
+ */
+ @Nullable
+ @Override
+ public final IBinder onBind(@NonNull Intent intent) {
+ if (SERVICE_INTERFACE.equals(intent.getAction())) {
+ return new IOnDeviceIntelligenceService.Stub() {
+ /** {@inheritDoc} */
+ @Override
+ public void getVersion(RemoteCallback remoteCallback) {
+ Objects.requireNonNull(remoteCallback);
+ OnDeviceIntelligenceService.this.onGetVersion(l -> {
+ Bundle b = new Bundle();
+ b.putLong(OnDeviceIntelligenceManager.API_VERSION_BUNDLE_KEY, l);
+ remoteCallback.sendResult(b);
+ });
+ }
+
+ @Override
+ public void listFeatures(IListFeaturesCallback listFeaturesCallback) {
+ Objects.requireNonNull(listFeaturesCallback);
+ OnDeviceIntelligenceService.this.onListFeatures(
+ wrapListFeaturesCallback(listFeaturesCallback));
+ }
+
+ @Override
+ public void getFeature(int id, IFeatureCallback featureCallback) {
+ Objects.requireNonNull(featureCallback);
+ OnDeviceIntelligenceService.this.onGetFeature(id,
+ wrapFeatureCallback(featureCallback));
+ }
+
+
+ @Override
+ public void getFeatureDetails(Feature feature,
+ IFeatureDetailsCallback featureDetailsCallback) {
+ Objects.requireNonNull(feature);
+ Objects.requireNonNull(featureDetailsCallback);
+
+ OnDeviceIntelligenceService.this.onGetFeatureDetails(feature,
+ wrapFeatureDetailsCallback(featureDetailsCallback));
+ }
+
+ @Override
+ public void requestFeatureDownload(Feature feature,
+ ICancellationSignal cancellationSignal,
+ IDownloadCallback downloadCallback) {
+ Objects.requireNonNull(feature);
+ Objects.requireNonNull(downloadCallback);
+
+ OnDeviceIntelligenceService.this.onDownloadFeature(feature,
+ CancellationSignal.fromTransport(cancellationSignal),
+ wrapDownloadCallback(downloadCallback));
+ }
+
+ @Override
+ public void getReadOnlyFileDescriptor(String fileName,
+ AndroidFuture<ParcelFileDescriptor> future) {
+ Objects.requireNonNull(fileName);
+ Objects.requireNonNull(future);
+
+ OnDeviceIntelligenceService.this.onGetReadOnlyFileDescriptor(fileName,
+ future);
+ }
+
+ @Override
+ public void getReadOnlyFeatureFileDescriptorMap(
+ Feature feature, RemoteCallback remoteCallback) {
+ Objects.requireNonNull(feature);
+ Objects.requireNonNull(remoteCallback);
+
+ OnDeviceIntelligenceService.this.onGetReadOnlyFeatureFileDescriptorMap(
+ feature, parcelFileDescriptorMap -> {
+ Bundle bundle = new Bundle();
+ parcelFileDescriptorMap.forEach(bundle::putParcelable);
+ remoteCallback.sendResult(bundle);
+ });
+ }
+ };
+ }
+ Slog.w(TAG, "Incorrect service interface, returning null.");
+ return null;
+ }
+
+ private OutcomeReceiver<Feature,
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException> wrapFeatureCallback(
+ IFeatureCallback featureCallback) {
+ return new OutcomeReceiver<>() {
+ @Override
+ public void onResult(@NonNull Feature feature) {
+ try {
+ featureCallback.onSuccess(feature);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending feature: " + e);
+ }
+ }
+
+ @Override
+ public void onError(
+ @NonNull OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException exception) {
+ try {
+ featureCallback.onFailure(exception.getErrorCode(), exception.getMessage(),
+ exception.getErrorParams());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending download feature: " + e);
+ }
+ }
+ };
+
+ }
+
+ private OutcomeReceiver<List<Feature>,
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException> wrapListFeaturesCallback(
+ IListFeaturesCallback listFeaturesCallback) {
+ return new OutcomeReceiver<>() {
+ @Override
+ public void onResult(@NonNull List<Feature> features) {
+ try {
+ listFeaturesCallback.onSuccess(features);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending feature: " + e);
+ }
+ }
+
+ @Override
+ public void onError(
+ @NonNull OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException exception) {
+ try {
+ listFeaturesCallback.onFailure(exception.getErrorCode(), exception.getMessage(),
+ exception.getErrorParams());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending download feature: " + e);
+ }
+ }
+ };
+ }
+
+ private OutcomeReceiver<FeatureDetails,
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException> wrapFeatureDetailsCallback(
+ IFeatureDetailsCallback featureStatusCallback) {
+ return new OutcomeReceiver<>() {
+ @Override
+ public void onResult(FeatureDetails result) {
+ try {
+ featureStatusCallback.onSuccess(result);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending feature status: " + e);
+ }
+ }
+
+ @Override
+ public void onError(
+ @NonNull OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException exception) {
+ try {
+ featureStatusCallback.onFailure(exception.getErrorCode(),
+ exception.getMessage(), exception.getErrorParams());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending feature status: " + e);
+ }
+ }
+ };
+ }
+
+
+ private DownloadCallback wrapDownloadCallback(IDownloadCallback downloadCallback) {
+ return new DownloadCallback() {
+ @Override
+ public void onDownloadStarted(long bytesToDownload) {
+ try {
+ downloadCallback.onDownloadStarted(bytesToDownload);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending download status: " + e);
+ }
+ }
+
+ @Override
+ public void onDownloadFailed(int failureStatus,
+ String errorMessage, @NonNull PersistableBundle errorParams) {
+ try {
+ downloadCallback.onDownloadFailed(failureStatus, errorMessage, errorParams);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending download status: " + e);
+ }
+ }
+
+ @Override
+ public void onDownloadProgress(long totalBytesDownloaded) {
+ try {
+ downloadCallback.onDownloadProgress(totalBytesDownloaded);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending download status: " + e);
+ }
+ }
+
+ @Override
+ public void onDownloadCompleted(@NonNull PersistableBundle persistableBundle) {
+ try {
+ downloadCallback.onDownloadCompleted(persistableBundle);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending download status: " + e);
+ }
+ }
+ };
+ }
+
+ private void onGetReadOnlyFileDescriptor(@NonNull String fileName,
+ @NonNull AndroidFuture<ParcelFileDescriptor> future) {
+ Slog.v(TAG, "onGetReadOnlyFileDescriptor " + fileName);
+ Binder.withCleanCallingIdentity(() -> {
+ Slog.v(TAG,
+ "onGetReadOnlyFileDescriptor: " + fileName + " under internal app storage.");
+ File f = new File(getBaseContext().getFilesDir(), fileName);
+ ParcelFileDescriptor pfd = null;
+ try {
+ pfd = ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY);
+ Slog.d(TAG, "Successfully opened a file with ParcelFileDescriptor.");
+ } catch (FileNotFoundException e) {
+ Slog.e(TAG, "Cannot open file. No ParcelFileDescriptor returned.");
+ } finally {
+ future.complete(pfd);
+ }
+ });
+ }
+
+ /**
+ * Provide implementation for a scenario when caller wants to get all feature related
+ * file-descriptors that might be required for processing a request for the corresponding the
+ * feature.
+ *
+ * @param feature the feature for which files need to be opened.
+ * @param fileDescriptorMapConsumer callback to be populated with a map of file-path and
+ * corresponding ParcelDescriptor to be used in a remote
+ * service.
+ */
+ public abstract void onGetReadOnlyFeatureFileDescriptorMap(
+ @NonNull Feature feature,
+ @NonNull Consumer<Map<String, ParcelFileDescriptor>> fileDescriptorMapConsumer);
+
+ /**
+ * Request download for feature that is requested and listen to download progress updates. If
+ * the download completes successfully, success callback should be populated.
+ *
+ * @param feature the feature for which files need to be downlaoded.
+ * process.
+ * @param cancellationSignal signal to attach a listener to, and receive cancellation signals
+ * from thw client.
+ * @param downloadCallback callback to populate download updates for clients to listen on..
+ */
+ public abstract void onDownloadFeature(
+ @NonNull Feature feature,
+ @Nullable CancellationSignal cancellationSignal,
+ @NonNull DownloadCallback downloadCallback);
+
+ /**
+ * Provide feature details for the passed in feature. Usually the client and remote
+ * implementation use the {@link Feature#getFeatureParams()} as a hint to communicate what
+ * details the client is looking for.
+ *
+ * @param feature the feature for which status needs to be known.
+ * @param featureStatusCallback callback to populate the resulting feature status.
+ */
+ public abstract void onGetFeatureDetails(@NonNull Feature feature,
+ @NonNull OutcomeReceiver<FeatureDetails,
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException> featureStatusCallback);
+
+
+ /**
+ * Get feature using the provided identifier to the remote implementation.
+ *
+ * @param featureCallback callback to populate the features list.
+ */
+ public abstract void onGetFeature(int featureId,
+ @NonNull OutcomeReceiver<Feature,
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException> featureCallback);
+
+ /**
+ * List all features which are available in the remote implementation. The implementation might
+ * choose to provide only a certain list of features based on the caller.
+ *
+ * @param listFeaturesCallback callback to populate the features list.
+ */
+ public abstract void onListFeatures(@NonNull OutcomeReceiver<List<Feature>,
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException> listFeaturesCallback);
+
+ /**
+ * Provides a long value representing the version of the remote implementation processing
+ * requests.
+ *
+ * @param versionConsumer consumer to populate the version.
+ */
+ public abstract void onGetVersion(@NonNull LongConsumer versionConsumer);
+}
diff --git a/core/java/android/service/ondeviceintelligence/OnDeviceTrustedInferenceService.java b/core/java/android/service/ondeviceintelligence/OnDeviceTrustedInferenceService.java
new file mode 100644
index 000000000000..96982e3d7829
--- /dev/null
+++ b/core/java/android/service/ondeviceintelligence/OnDeviceTrustedInferenceService.java
@@ -0,0 +1,410 @@
+/*
+ * 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.service.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.app.ondeviceintelligence.Content;
+import android.app.ondeviceintelligence.Feature;
+import android.app.ondeviceintelligence.IProcessingSignal;
+import android.app.ondeviceintelligence.IResponseCallback;
+import android.app.ondeviceintelligence.IStreamingResponseCallback;
+import android.app.ondeviceintelligence.ITokenCountCallback;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceManager;
+import android.app.ondeviceintelligence.ProcessingSignal;
+import android.app.ondeviceintelligence.StreamingResponseReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.CancellationSignal;
+import android.os.IBinder;
+import android.os.ICancellationSignal;
+import android.os.OutcomeReceiver;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.infra.AndroidFuture;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * Abstract base class for performing inference in a isolated process. This service exposes its
+ * methods via {@link android.app.ondeviceintelligence.OnDeviceIntelligenceManager}.
+ *
+ * <p> A service that provides methods to perform on-device inference both in streaming and
+ * non-streaming fashion. Also, provides a way to register a storage service that will be used to
+ * read-only access files from the {@link OnDeviceIntelligenceService} counterpart. </p>
+ *
+ * <pre>
+ * {@literal
+ * <service android:name=".SampleTrustedInferenceService"
+ * android:permission="android.permission.BIND_ONDEVICE_TRUSTED_INFERENCE_SERVICE"
+ * android:isolatedProcess="true">
+ * </service>}
+ * </pre>
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public abstract class OnDeviceTrustedInferenceService extends Service {
+ private static final String TAG = OnDeviceTrustedInferenceService.class.getSimpleName();
+
+ /**
+ * The {@link Intent} that must be declared as handled by the service. To be supported, the
+ * service must also require the
+ * {@link android.Manifest.permission#BIND_ON_DEVICE_TRUSTED_INFERENCE_SERVICE}
+ * permission so that other applications can not abuse it.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE =
+ "android.service.ondeviceintelligence.OnDeviceTrustedInferenceService";
+
+ private IRemoteStorageService mRemoteStorageService;
+
+ /**
+ * @hide
+ */
+ @Nullable
+ @Override
+ public final IBinder onBind(@NonNull Intent intent) {
+ if (SERVICE_INTERFACE.equals(intent.getAction())) {
+ return new IOnDeviceTrustedInferenceService.Stub() {
+ @Override
+ public void registerRemoteStorageService(IRemoteStorageService storageService) {
+ Objects.requireNonNull(storageService);
+ mRemoteStorageService = storageService;
+ }
+
+ @Override
+ public void requestTokenCount(Feature feature, Content request,
+ ICancellationSignal cancellationSignal,
+ ITokenCountCallback tokenCountCallback) {
+ Objects.requireNonNull(feature);
+ Objects.requireNonNull(tokenCountCallback);
+ OnDeviceTrustedInferenceService.this.onCountTokens(feature,
+ request,
+ CancellationSignal.fromTransport(cancellationSignal),
+ wrapTokenCountCallback(tokenCountCallback));
+ }
+
+ @Override
+ public void processRequestStreaming(Feature feature, Content request,
+ int requestType, ICancellationSignal cancellationSignal,
+ IProcessingSignal processingSignal,
+ IStreamingResponseCallback callback) {
+ Objects.requireNonNull(feature);
+ Objects.requireNonNull(request);
+ Objects.requireNonNull(callback);
+
+ OnDeviceTrustedInferenceService.this.onProcessRequestStreaming(feature,
+ request,
+ requestType,
+ CancellationSignal.fromTransport(cancellationSignal),
+ ProcessingSignal.fromTransport(processingSignal),
+ wrapStreamingResponseCallback(callback)
+ );
+ }
+
+ @Override
+ public void processRequest(Feature feature, Content request,
+ int requestType, ICancellationSignal cancellationSignal,
+ IProcessingSignal processingSignal,
+ IResponseCallback callback) {
+ Objects.requireNonNull(feature);
+ Objects.requireNonNull(request);
+ Objects.requireNonNull(callback);
+
+
+ OnDeviceTrustedInferenceService.this.onProcessRequest(feature, request,
+ requestType, CancellationSignal.fromTransport(cancellationSignal),
+ ProcessingSignal.fromTransport(processingSignal),
+ wrapResponseCallback(callback)
+ );
+ }
+ };
+ }
+ Slog.w(TAG, "Incorrect service interface, returning null.");
+ return null;
+ }
+
+ /**
+ * Invoked when caller wants to obtain a count of number of tokens present in the passed in
+ * Request associated with the provided feature.
+ * The expectation from the implementation is that when processing is complete, it
+ * should provide the token count in the {@link OutcomeReceiver#onResult}.
+ *
+ * @param feature feature which is associated with the request.
+ * @param request request that requires processing.
+ * @param cancellationSignal Cancellation Signal to receive cancellation events from client and
+ * configure a listener to.
+ * @param callback callback to populate failure and full response for the provided
+ * request.
+ */
+ @NonNull
+ public abstract void onCountTokens(
+ @NonNull Feature feature,
+ @NonNull Content request,
+ @Nullable CancellationSignal cancellationSignal,
+ @NonNull OutcomeReceiver<Long,
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException> callback);
+
+ /**
+ * Invoked when caller provides a request for a particular feature to be processed in a
+ * streaming manner. The expectation from the implementation is that when processing the
+ * request,
+ * it periodically populates the {@link StreamingResponseReceiver#onNewContent} to continuously
+ * provide partial Content results for the caller to utilize. Optionally the implementation can
+ * provide the complete response in the {@link StreamingResponseReceiver#onResult} upon
+ * processing completion.
+ *
+ * @param feature feature which is associated with the request.
+ * @param request request that requires processing.
+ * @param requestType identifier representing the type of request.
+ * @param cancellationSignal Cancellation Signal to receive cancellation events from client and
+ * configure a listener to.
+ * @param processingSignal Signal to receive custom action instructions from client.
+ * @param callback callback to populate the partial responses, failure and optionally
+ * full response for the provided request.
+ */
+ @NonNull
+ public abstract void onProcessRequestStreaming(
+ @NonNull Feature feature,
+ @NonNull Content request,
+ @OnDeviceIntelligenceManager.RequestType int requestType,
+ @Nullable CancellationSignal cancellationSignal,
+ @Nullable ProcessingSignal processingSignal,
+ @NonNull StreamingResponseReceiver<Content, Content,
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException> callback);
+
+ /**
+ * Invoked when caller provides a request for a particular feature to be processed in one shot
+ * completely.
+ * The expectation from the implementation is that when processing the request is complete, it
+ * should
+ * provide the complete response in the {@link OutcomeReceiver#onResult}.
+ *
+ * @param feature feature which is associated with the request.
+ * @param request request that requires processing.
+ * @param requestType identifier representing the type of request.
+ * @param cancellationSignal Cancellation Signal to receive cancellation events from client and
+ * configure a listener to.
+ * @param processingSignal Signal to receive custom action instructions from client.
+ * @param callback callback to populate failure and full response for the provided
+ * request.
+ */
+ @NonNull
+ public abstract void onProcessRequest(
+ @NonNull Feature feature,
+ @NonNull Content request,
+ @OnDeviceIntelligenceManager.RequestType int requestType,
+ @Nullable CancellationSignal cancellationSignal,
+ @Nullable ProcessingSignal processingSignal,
+ @NonNull OutcomeReceiver<Content,
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException> callback);
+
+ /**
+ * Overrides {@link Context#openFileInput} to read files with the given file names under the
+ * internal app storage of the {@link OnDeviceIntelligenceService}, i.e., only files stored in
+ * {@link Context#getFilesDir()} can be opened.
+ */
+ @Override
+ public final FileInputStream openFileInput(@NonNull String filename) throws
+ FileNotFoundException {
+ try {
+ AndroidFuture<ParcelFileDescriptor> future = new AndroidFuture<>();
+ mRemoteStorageService.getReadOnlyFileDescriptor(filename, future);
+ ParcelFileDescriptor pfd = future.get();
+ return new FileInputStream(pfd.getFileDescriptor());
+ } catch (RemoteException | ExecutionException | InterruptedException e) {
+ Log.w(TAG, "Cannot open file due to remote service failure");
+ throw new FileNotFoundException(e.getMessage());
+ }
+ }
+
+ /**
+ * Provides read-only access to the internal app storage via the
+ * {@link OnDeviceIntelligenceService}. This is an asynchronous implementation for
+ * {@link #openFileInput(String)}.
+ *
+ * @param fileName File name relative to the {@link Context#getFilesDir()}.
+ * @param resultConsumer Consumer to populate the corresponding file stream in.
+ */
+ public final void openFileInputAsync(@NonNull String fileName,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<FileInputStream> resultConsumer) throws FileNotFoundException {
+ AndroidFuture<ParcelFileDescriptor> future = new AndroidFuture<>();
+ try {
+ mRemoteStorageService.getReadOnlyFileDescriptor(fileName, future);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Cannot open file due to remote service failure");
+ throw new FileNotFoundException(e.getMessage());
+ }
+ future.whenCompleteAsync((pfd, err) -> {
+ if (err != null) {
+ Log.e(TAG, "Failure when reading file: " + fileName + err);
+ executor.execute(() -> resultConsumer.accept(null));
+ } else {
+ executor.execute(
+ () -> resultConsumer.accept(new FileInputStream(pfd.getFileDescriptor())));
+ }
+ }, executor);
+ }
+
+ /**
+ * Provides access to all file streams required for feature via the
+ * {@link OnDeviceIntelligenceService}.
+ *
+ * @param feature Feature for which the associated files should be fetched.
+ * @param executor Executor to run the consumer callback on.
+ * @param resultConsumer Consumer to receive a map of filePath to the corresponding file input
+ * stream.
+ */
+ public final void fetchFeatureFileInputStreamMap(@NonNull Feature feature,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<Map<String, FileInputStream>> resultConsumer) {
+ try {
+ mRemoteStorageService.getReadOnlyFeatureFileDescriptorMap(feature,
+ wrapResultReceiverAsReadOnly(resultConsumer, executor));
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private RemoteCallback wrapResultReceiverAsReadOnly(
+ @NonNull Consumer<Map<String, FileInputStream>> resultConsumer,
+ @NonNull Executor executor) {
+ return new RemoteCallback(result -> {
+ if (result == null) {
+ executor.execute(() -> resultConsumer.accept(new HashMap<>()));
+ } else {
+ Map<String, FileInputStream> bundleMap = new HashMap<>();
+ result.keySet().forEach(key -> {
+ ParcelFileDescriptor pfd = result.getParcelable(key,
+ ParcelFileDescriptor.class);
+ if (pfd != null) {
+ bundleMap.put(key, new FileInputStream(pfd.getFileDescriptor()));
+ }
+ });
+ executor.execute(() -> resultConsumer.accept(bundleMap));
+ }
+ });
+ }
+
+ private OutcomeReceiver<Content,
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException> wrapResponseCallback(
+ IResponseCallback callback) {
+ return new OutcomeReceiver<>() {
+ @Override
+ public void onResult(@androidx.annotation.NonNull Content response) {
+ try {
+ callback.onSuccess(response);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending result: " + e);
+ }
+ }
+
+ @Override
+ public void onError(
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException exception) {
+ try {
+ callback.onFailure(exception.getErrorCode(), exception.getMessage(),
+ exception.getErrorParams());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending result: " + e);
+ }
+ }
+ };
+ }
+
+ private StreamingResponseReceiver<Content, Content,
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException> wrapStreamingResponseCallback(
+ IStreamingResponseCallback callback) {
+ return new StreamingResponseReceiver<>() {
+ @Override
+ public void onNewContent(@androidx.annotation.NonNull Content content) {
+ try {
+ callback.onNewContent(content);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending result: " + e);
+ }
+ }
+
+ @Override
+ public void onResult(@androidx.annotation.NonNull Content response) {
+ try {
+ callback.onSuccess(response);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending result: " + e);
+ }
+ }
+
+ @Override
+ public void onError(
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException exception) {
+ try {
+ callback.onFailure(exception.getErrorCode(), exception.getMessage(),
+ exception.getErrorParams());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending result: " + e);
+ }
+ }
+ };
+ }
+
+ private OutcomeReceiver<Long,
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException> wrapTokenCountCallback(
+ ITokenCountCallback tokenCountCallback) {
+ return new OutcomeReceiver<>() {
+ @Override
+ public void onResult(Long tokenCount) {
+ try {
+ tokenCountCallback.onSuccess(tokenCount);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending result: " + e);
+ }
+ }
+
+ @Override
+ public void onError(
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException exception) {
+ try {
+ tokenCountCallback.onFailure(exception.getErrorCode(), exception.getMessage(),
+ exception.getErrorParams());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending failure: " + e);
+ }
+ }
+ };
+ }
+}
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 94d851603064..a08264e625df 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -22,6 +22,7 @@ import static android.service.voice.SoundTriggerFailure.ERROR_CODE_UNKNOWN;
import static android.service.voice.VoiceInteractionService.MULTIPLE_ACTIVE_HOTWORD_DETECTORS;
import android.annotation.ElapsedRealtimeLong;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -432,7 +433,10 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
@ElapsedRealtimeLong
private final long mHalEventReceivedMillis;
- private EventPayload(boolean captureAvailable,
+ private final boolean mIsRecognitionStopped;
+
+ private EventPayload(
+ boolean captureAvailable,
@Nullable AudioFormat audioFormat,
int captureSession,
@DataFormat int dataFormat,
@@ -440,7 +444,8 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
@Nullable HotwordDetectedResult hotwordDetectedResult,
@Nullable ParcelFileDescriptor audioStream,
@NonNull List<KeyphraseRecognitionExtra> keyphraseExtras,
- @ElapsedRealtimeLong long halEventReceivedMillis) {
+ @ElapsedRealtimeLong long halEventReceivedMillis,
+ boolean isRecognitionStopped) {
mCaptureAvailable = captureAvailable;
mCaptureSession = captureSession;
mAudioFormat = audioFormat;
@@ -450,6 +455,7 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
mAudioStream = audioStream;
mKephraseExtras = keyphraseExtras;
mHalEventReceivedMillis = halEventReceivedMillis;
+ mIsRecognitionStopped = isRecognitionStopped;
}
/**
@@ -592,6 +598,12 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
return mHalEventReceivedMillis;
}
+ /** Returns whether the system has stopped hotword recognition because of this detection. */
+ @FlaggedApi(android.app.wearable.Flags.FLAG_ENABLE_HOTWORD_WEARABLE_SENSING_API)
+ public boolean isRecognitionStopped() {
+ return mIsRecognitionStopped;
+ }
+
/**
* Builder class for {@link EventPayload} objects
*
@@ -610,6 +622,8 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
private List<KeyphraseRecognitionExtra> mKeyphraseExtras = Collections.emptyList();
@ElapsedRealtimeLong
private long mHalEventReceivedMillis = -1;
+ // default to true to keep prior behavior
+ private boolean mIsRecognitionStopped = true;
public Builder() {}
@@ -746,13 +760,31 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
}
/**
+ * Sets whether the system has stopped hotword recognition because of this detection.
+ */
+ @FlaggedApi(android.app.wearable.Flags.FLAG_ENABLE_HOTWORD_WEARABLE_SENSING_API)
+ @NonNull
+ public Builder setIsRecognitionStopped(boolean isRecognitionStopped) {
+ mIsRecognitionStopped = isRecognitionStopped;
+ return this;
+ }
+
+ /**
* Builds an {@link EventPayload} instance
*/
@NonNull
public EventPayload build() {
- return new EventPayload(mCaptureAvailable, mAudioFormat, mCaptureSession,
- mDataFormat, mData, mHotwordDetectedResult, mAudioStream,
- mKeyphraseExtras, mHalEventReceivedMillis);
+ return new EventPayload(
+ mCaptureAvailable,
+ mAudioFormat,
+ mCaptureSession,
+ mDataFormat,
+ mData,
+ mHotwordDetectedResult,
+ mAudioStream,
+ mKeyphraseExtras,
+ mHalEventReceivedMillis,
+ mIsRecognitionStopped);
}
}
}
@@ -786,14 +818,20 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
/**
* Called when the keyphrase is spoken.
- * This implicitly stops listening for the keyphrase once it's detected.
- * Clients should start a recognition again once they are done handling this
- * detection.
*
- * @param eventPayload Payload data for the detection event.
- * This may contain the trigger audio, if requested when calling
- * {@link AlwaysOnHotwordDetector#startRecognition(int)}.
+ * <p>This implicitly stops listening for the keyphrase once it's detected. Clients should
+ * start a recognition again once they are done handling this detection.
+ *
+ * @param eventPayload Payload data for the detection event. This may contain the trigger
+ * audio, if requested when calling {@link
+ * AlwaysOnHotwordDetector#startRecognition(int)}.
*/
+ // TODO(b/324635656): Update Javadoc for 24Q3 release:
+ // 1. Prepend to the first paragraph:
+ // If {@code eventPayload.isRecognitionStopped()} returns true, this...
+ // 2. Append to the description for @param eventPayload:
+ // ...or if the audio comes from {@link
+ // android.service.wearable.WearableSensingService}.
public abstract void onDetected(@NonNull EventPayload eventPayload);
/**
@@ -1632,6 +1670,20 @@ public class AlwaysOnHotwordDetector extends AbstractDetector {
}
@Override
+ public void onKeyphraseDetectedFromExternalSource(HotwordDetectedResult result) {
+ Slog.i(TAG, "onKeyphraseDetectedFromExternalSource");
+ EventPayload.Builder eventPayloadBuilder = new EventPayload.Builder();
+ if (android.app.wearable.Flags.enableHotwordWearableSensingApi()) {
+ eventPayloadBuilder.setIsRecognitionStopped(false);
+ }
+ Message.obtain(
+ mHandler,
+ MSG_HOTWORD_DETECTED,
+ eventPayloadBuilder.setHotwordDetectedResult(result).build())
+ .sendToTarget();
+ }
+
+ @Override
public void onGenericSoundTriggerDetected(SoundTrigger.GenericRecognitionEvent event) {
Slog.w(TAG, "Generic sound trigger event detected at AOHD: " + event);
}
diff --git a/core/java/android/service/voice/HotwordDetectionService.java b/core/java/android/service/voice/HotwordDetectionService.java
index ccf8b67826c8..60e9de72f154 100644
--- a/core/java/android/service/voice/HotwordDetectionService.java
+++ b/core/java/android/service/voice/HotwordDetectionService.java
@@ -19,6 +19,7 @@ package android.service.voice;
import static java.util.Objects.requireNonNull;
import android.annotation.DurationMillisLong;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -79,6 +80,16 @@ public abstract class HotwordDetectionService extends Service
private static final long UPDATE_TIMEOUT_MILLIS = 20000;
/**
+ * The PersistableBundle options key used in {@link #onDetect(ParcelFileDescriptor, AudioFormat,
+ * PersistableBundle, Callback)} to indicate whether the system will close the audio stream
+ * after {@code Callback} is invoked.
+ */
+ @FlaggedApi(android.app.wearable.Flags.FLAG_ENABLE_HOTWORD_WEARABLE_SENSING_API)
+ public static final String KEY_SYSTEM_WILL_CLOSE_AUDIO_STREAM_AFTER_CALLBACK =
+ "android.service.voice.HotwordDetectionService."
+ + "KEY_SYSTEM_WILL_CLOSE_AUDIO_STREAM_AFTER_CALLBACK";
+
+ /**
* Feature flag for Attention Service.
*
* @hide
@@ -364,6 +375,11 @@ public abstract class HotwordDetectionService extends Service
* PersistableBundle)}.
* @param callback The callback to use for responding to the detection request.
*/
+ // TODO(b/324635656): Update Javadoc for 24Q3 release. Change the last paragraph to:
+ // <p>Upon invoking the {@code callback}, the system will send the detection result to
+ // the {@link HotwordDetector}'s callback. If {@code
+ // options.getBoolean(KEY_SYSTEM_WILL_CLOSE_AUDIO_STREAM_AFTER_CALLBACK, true)} returns true,
+ // the system will also close the {@code audioStream} after {@code callback} is invoked.
public void onDetect(
@NonNull ParcelFileDescriptor audioStream,
@NonNull AudioFormat audioFormat,
diff --git a/core/java/android/service/voice/HotwordTrainingAudio.java b/core/java/android/service/voice/HotwordTrainingAudio.java
deleted file mode 100644
index 916fa36b2339..000000000000
--- a/core/java/android/service/voice/HotwordTrainingAudio.java
+++ /dev/null
@@ -1,388 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.service.voice;
-
-import android.annotation.FlaggedApi;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.media.AudioFormat;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.service.voice.flags.Flags;
-
-import com.android.internal.util.DataClass;
-
-import java.util.Objects;
-
-/**
- * Represents audio supporting hotword model training.
- *
- * @hide
- */
-@FlaggedApi(Flags.FLAG_ALLOW_TRAINING_DATA_EGRESS_FROM_HDS)
-@DataClass(
- genConstructor = false,
- genBuilder = true,
- genEqualsHashCode = true,
- genHiddenConstDefs = true,
- genParcelable = true,
- genToString = true
-)
-@SystemApi
-public final class HotwordTrainingAudio implements Parcelable {
- /** Represents unset value for the hotword offset. */
- public static final int HOTWORD_OFFSET_UNSET = -1;
-
- /**
- * Buffer of hotword audio data for training models. The data format is expected to match
- * {@link #getAudioFormat()}.
- */
- @NonNull
- private final byte[] mHotwordAudio;
-
- private String hotwordAudioToString() {
- return "length=" + mHotwordAudio.length;
- }
-
- /**
- * The {@link AudioFormat} of the {@link HotwordTrainingAudio#mHotwordAudio}.
- */
- @NonNull
- private final AudioFormat mAudioFormat;
-
- /**
- * App-defined identifier to distinguish hotword training audio instances.
- * <p> Returns -1 if unset. */
- @NonNull
- private final int mAudioType;
-
- private static int defaultAudioType() {
- return -1;
- }
-
- /**
- * App-defined offset in milliseconds relative to start of
- * {@link HotwordTrainingAudio#mHotwordAudio}. Default value is
- * {@link HotwordTrainingAudio#HOTWORD_OFFSET_UNSET}.
- */
- private int mHotwordOffsetMillis = HOTWORD_OFFSET_UNSET;
-
- @DataClass.Suppress("setHotwordAudio")
- abstract static class BaseBuilder {
-
- /**
- * Buffer of hotword audio data for training models. The data format is expected to match
- * {@link #getAudioFormat()}.
- */
- @SuppressLint("UnflaggedApi")
- public @NonNull HotwordTrainingAudio.Builder setHotwordAudio(@NonNull byte[] value) {
- Objects.requireNonNull(value, "value should not be null");
- final HotwordTrainingAudio.Builder builder = (HotwordTrainingAudio.Builder) this;
- // If the code gen flag in build() is changed, we must update the flag e.g. 0x1 here.
- builder.mBuilderFieldsSet |= 0x1;
- builder.mHotwordAudio = value;
- return builder;
- }
- }
-
-
-
- // Code below generated by codegen v1.0.23.
- //
- // DO NOT MODIFY!
- // CHECKSTYLE:OFF Generated code
- //
- // To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/voice/HotwordTrainingAudio.java
- //
- // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
- // Settings > Editor > Code Style > Formatter Control
- //@formatter:off
-
-
- @DataClass.Generated.Member
- /* package-private */ HotwordTrainingAudio(
- @NonNull byte[] hotwordAudio,
- @NonNull AudioFormat audioFormat,
- @NonNull int audioType,
- int hotwordOffsetMillis) {
- this.mHotwordAudio = hotwordAudio;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mHotwordAudio);
- this.mAudioFormat = audioFormat;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mAudioFormat);
- this.mAudioType = audioType;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mAudioType);
- this.mHotwordOffsetMillis = hotwordOffsetMillis;
-
- // onConstructed(); // You can define this method to get a callback
- }
-
- /**
- * Buffer of hotword audio data for training models. The data format is expected to match
- * {@link #getAudioFormat()}.
- */
- @DataClass.Generated.Member
- public @NonNull byte[] getHotwordAudio() {
- return mHotwordAudio;
- }
-
- /**
- * The {@link AudioFormat} of the {@link HotwordTrainingAudio#mHotwordAudio}.
- */
- @DataClass.Generated.Member
- public @NonNull AudioFormat getAudioFormat() {
- return mAudioFormat;
- }
-
- /**
- * App-defined identifier to distinguish hotword training audio instances.
- * <p> Returns -1 if unset.
- */
- @DataClass.Generated.Member
- public @NonNull int getAudioType() {
- return mAudioType;
- }
-
- /**
- * App-defined offset in milliseconds relative to start of
- * {@link HotwordTrainingAudio#mHotwordAudio}. Default value is
- * {@link HotwordTrainingAudio#HOTWORD_OFFSET_UNSET}.
- */
- @DataClass.Generated.Member
- public int getHotwordOffsetMillis() {
- return mHotwordOffsetMillis;
- }
-
- @Override
- @DataClass.Generated.Member
- public String toString() {
- // You can override field toString logic by defining methods like:
- // String fieldNameToString() { ... }
-
- return "HotwordTrainingAudio { " +
- "hotwordAudio = " + hotwordAudioToString() + ", " +
- "audioFormat = " + mAudioFormat + ", " +
- "audioType = " + mAudioType + ", " +
- "hotwordOffsetMillis = " + mHotwordOffsetMillis +
- " }";
- }
-
- @Override
- @DataClass.Generated.Member
- public boolean equals(@Nullable Object o) {
- // You can override field equality logic by defining either of the methods like:
- // boolean fieldNameEquals(HotwordTrainingAudio other) { ... }
- // boolean fieldNameEquals(FieldType otherValue) { ... }
-
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- @SuppressWarnings("unchecked")
- HotwordTrainingAudio that = (HotwordTrainingAudio) o;
- //noinspection PointlessBooleanExpression
- return true
- && java.util.Arrays.equals(mHotwordAudio, that.mHotwordAudio)
- && Objects.equals(mAudioFormat, that.mAudioFormat)
- && mAudioType == that.mAudioType
- && mHotwordOffsetMillis == that.mHotwordOffsetMillis;
- }
-
- @Override
- @DataClass.Generated.Member
- public int hashCode() {
- // You can override field hashCode logic by defining methods like:
- // int fieldNameHashCode() { ... }
-
- int _hash = 1;
- _hash = 31 * _hash + java.util.Arrays.hashCode(mHotwordAudio);
- _hash = 31 * _hash + Objects.hashCode(mAudioFormat);
- _hash = 31 * _hash + mAudioType;
- _hash = 31 * _hash + mHotwordOffsetMillis;
- return _hash;
- }
-
- @Override
- @DataClass.Generated.Member
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- // You can override field parcelling by defining methods like:
- // void parcelFieldName(Parcel dest, int flags) { ... }
-
- dest.writeByteArray(mHotwordAudio);
- dest.writeTypedObject(mAudioFormat, flags);
- dest.writeInt(mAudioType);
- dest.writeInt(mHotwordOffsetMillis);
- }
-
- @Override
- @DataClass.Generated.Member
- public int describeContents() { return 0; }
-
- /** @hide */
- @SuppressWarnings({"unchecked", "RedundantCast"})
- @DataClass.Generated.Member
- /* package-private */ HotwordTrainingAudio(@NonNull Parcel in) {
- // You can override field unparcelling by defining methods like:
- // static FieldType unparcelFieldName(Parcel in) { ... }
-
- byte[] hotwordAudio = in.createByteArray();
- AudioFormat audioFormat = (AudioFormat) in.readTypedObject(AudioFormat.CREATOR);
- int audioType = in.readInt();
- int hotwordOffsetMillis = in.readInt();
-
- this.mHotwordAudio = hotwordAudio;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mHotwordAudio);
- this.mAudioFormat = audioFormat;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mAudioFormat);
- this.mAudioType = audioType;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mAudioType);
- this.mHotwordOffsetMillis = hotwordOffsetMillis;
-
- // onConstructed(); // You can define this method to get a callback
- }
-
- @DataClass.Generated.Member
- public static final @NonNull Parcelable.Creator<HotwordTrainingAudio> CREATOR
- = new Parcelable.Creator<HotwordTrainingAudio>() {
- @Override
- public HotwordTrainingAudio[] newArray(int size) {
- return new HotwordTrainingAudio[size];
- }
-
- @Override
- public HotwordTrainingAudio createFromParcel(@NonNull Parcel in) {
- return new HotwordTrainingAudio(in);
- }
- };
-
- /**
- * A builder for {@link HotwordTrainingAudio}
- */
- @FlaggedApi(Flags.FLAG_ALLOW_TRAINING_DATA_EGRESS_FROM_HDS)
- @SuppressWarnings("WeakerAccess")
- @DataClass.Generated.Member
- public static final class Builder extends BaseBuilder {
-
- private @NonNull byte[] mHotwordAudio;
- private @NonNull AudioFormat mAudioFormat;
- private @NonNull int mAudioType;
- private int mHotwordOffsetMillis;
-
- private long mBuilderFieldsSet = 0L;
-
- /**
- * Creates a new Builder.
- *
- * @param hotwordAudio
- * Buffer of hotword audio data for training models. The data format is expected to match
- * {@link #getAudioFormat()}.
- * @param audioFormat
- * The {@link AudioFormat} of the {@link HotwordTrainingAudio#mHotwordAudio}.
- */
- public Builder(
- @NonNull byte[] hotwordAudio,
- @NonNull AudioFormat audioFormat) {
- mHotwordAudio = hotwordAudio;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mHotwordAudio);
- mAudioFormat = audioFormat;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mAudioFormat);
- }
-
- /**
- * The {@link AudioFormat} of the {@link HotwordTrainingAudio#mHotwordAudio}.
- */
- @DataClass.Generated.Member
- public @NonNull Builder setAudioFormat(@NonNull AudioFormat value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x2;
- mAudioFormat = value;
- return this;
- }
-
- /**
- * App-defined identifier to distinguish hotword training audio instances.
- * <p> Returns -1 if unset.
- */
- @DataClass.Generated.Member
- public @NonNull Builder setAudioType(@NonNull int value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x4;
- mAudioType = value;
- return this;
- }
-
- /**
- * App-defined offset in milliseconds relative to start of
- * {@link HotwordTrainingAudio#mHotwordAudio}. Default value is
- * {@link HotwordTrainingAudio#HOTWORD_OFFSET_UNSET}.
- */
- @DataClass.Generated.Member
- public @NonNull Builder setHotwordOffsetMillis(int value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x8;
- mHotwordOffsetMillis = value;
- return this;
- }
-
- /** Builds the instance. This builder should not be touched after calling this! */
- public @NonNull HotwordTrainingAudio build() {
- checkNotUsed();
- mBuilderFieldsSet |= 0x10; // Mark builder used
-
- if ((mBuilderFieldsSet & 0x4) == 0) {
- mAudioType = defaultAudioType();
- }
- if ((mBuilderFieldsSet & 0x8) == 0) {
- mHotwordOffsetMillis = HOTWORD_OFFSET_UNSET;
- }
- HotwordTrainingAudio o = new HotwordTrainingAudio(
- mHotwordAudio,
- mAudioFormat,
- mAudioType,
- mHotwordOffsetMillis);
- return o;
- }
-
- private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x10) != 0) {
- throw new IllegalStateException(
- "This Builder should not be reused. Use a new Builder instance instead");
- }
- }
- }
-
- @DataClass.Generated(
- time = 1697827049629L,
- codegenVersion = "1.0.23",
- sourceFile = "frameworks/base/core/java/android/service/voice/HotwordTrainingAudio.java",
- inputSignatures = "public static final int HOTWORD_OFFSET_UNSET\nprivate final @android.annotation.NonNull byte[] mHotwordAudio\nprivate final @android.annotation.NonNull android.media.AudioFormat mAudioFormat\nprivate final @android.annotation.NonNull int mAudioType\nprivate int mHotwordOffsetMillis\nprivate java.lang.String hotwordAudioToString()\nprivate static int defaultAudioType()\nclass HotwordTrainingAudio extends java.lang.Object implements [android.os.Parcelable]\npublic @android.annotation.SuppressLint @android.annotation.NonNull android.service.voice.HotwordTrainingAudio.Builder setHotwordAudio(byte[])\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)\npublic @android.annotation.SuppressLint @android.annotation.NonNull android.service.voice.HotwordTrainingAudio.Builder setHotwordAudio(byte[])\nclass BaseBuilder extends java.lang.Object implements []")
- @Deprecated
- private void __metadata() {}
-
-
- //@formatter:on
- // End of generated code
-
-}
diff --git a/core/java/android/service/voice/HotwordTrainingData.java b/core/java/android/service/voice/HotwordTrainingData.java
deleted file mode 100644
index aa6dab3629b4..000000000000
--- a/core/java/android/service/voice/HotwordTrainingData.java
+++ /dev/null
@@ -1,305 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.service.voice;
-
-import android.annotation.FlaggedApi;
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.service.voice.flags.Flags;
-import android.text.TextUtils;
-
-import com.android.internal.util.DataClass;
-import com.android.internal.util.Preconditions;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Contains training data related to hotword detection service.
- *
- * <p>The constructed object's size must be within
- * {@link HotwordTrainingData#getMaxTrainingDataBytes()} or an
- * {@link IllegalArgumentException} will be thrown on construction. Size of the object is calculated
- * by converting object to a {@link Parcel} and using the {@link Parcel#dataSize()}.
- *
- * @hide
- */
-@DataClass(
- genConstructor = false,
- genBuilder = true,
- genEqualsHashCode = true,
- genHiddenConstDefs = true,
- genParcelable = true,
- genToString = true)
-@SystemApi
-@FlaggedApi(Flags.FLAG_ALLOW_TRAINING_DATA_EGRESS_FROM_HDS)
-public final class HotwordTrainingData implements Parcelable {
- /** Max size for hotword training data in bytes. */
- public static int getMaxTrainingDataBytes() {
- return 1024 * 1024; // 1 MB;
- }
-
- /** The list containing hotword audio that is useful for training. */
- @NonNull
- @DataClass.PluralOf("trainingAudio")
- private final List<HotwordTrainingAudio> mTrainingAudioList;
-
- private static List<HotwordTrainingAudio> defaultTrainingAudioList() {
- return Collections.emptyList();
- }
-
- /** App-defined stage when hotword model timed-out while running.
- * <p> Returns -1 if unset. */
- private final int mTimeoutStage;
-
- private static int defaultTimeoutStage() {
- return -1;
- }
-
- private void onConstructed() {
- // Verify size of object is within limit.
- Parcel parcel = Parcel.obtain();
- parcel.writeValue(this);
- int dataSizeBytes = parcel.dataSize();
- parcel.recycle();
- Preconditions.checkArgument(
- dataSizeBytes < getMaxTrainingDataBytes(),
- TextUtils.formatSimple(
- "Hotword training data of size %s exceeds size limit of %s bytes!",
- dataSizeBytes, getMaxTrainingDataBytes()));
- }
-
-
-
- // Code below generated by codegen v1.0.23.
- //
- // DO NOT MODIFY!
- // CHECKSTYLE:OFF Generated code
- //
- // To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/voice/HotwordTrainingData.java
- //
- // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
- // Settings > Editor > Code Style > Formatter Control
- //@formatter:off
-
-
- @DataClass.Generated.Member
- /* package-private */ HotwordTrainingData(
- @NonNull List<HotwordTrainingAudio> trainingAudioList,
- int timeoutStage) {
- this.mTrainingAudioList = trainingAudioList;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mTrainingAudioList);
- this.mTimeoutStage = timeoutStage;
-
- onConstructed();
- }
-
- /**
- * The list containing hotword audio that is useful for training.
- */
- @DataClass.Generated.Member
- public @NonNull List<HotwordTrainingAudio> getTrainingAudioList() {
- return mTrainingAudioList;
- }
-
- /**
- * App-defined stage when hotword model timed-out while running.
- * <p> Returns -1 if unset.
- */
- @DataClass.Generated.Member
- public int getTimeoutStage() {
- return mTimeoutStage;
- }
-
- @Override
- @DataClass.Generated.Member
- public String toString() {
- // You can override field toString logic by defining methods like:
- // String fieldNameToString() { ... }
-
- return "HotwordTrainingData { " +
- "trainingAudioList = " + mTrainingAudioList + ", " +
- "timeoutStage = " + mTimeoutStage +
- " }";
- }
-
- @Override
- @DataClass.Generated.Member
- public boolean equals(@android.annotation.Nullable Object o) {
- // You can override field equality logic by defining either of the methods like:
- // boolean fieldNameEquals(HotwordTrainingData other) { ... }
- // boolean fieldNameEquals(FieldType otherValue) { ... }
-
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- @SuppressWarnings("unchecked")
- HotwordTrainingData that = (HotwordTrainingData) o;
- //noinspection PointlessBooleanExpression
- return true
- && java.util.Objects.equals(mTrainingAudioList, that.mTrainingAudioList)
- && mTimeoutStage == that.mTimeoutStage;
- }
-
- @Override
- @DataClass.Generated.Member
- public int hashCode() {
- // You can override field hashCode logic by defining methods like:
- // int fieldNameHashCode() { ... }
-
- int _hash = 1;
- _hash = 31 * _hash + java.util.Objects.hashCode(mTrainingAudioList);
- _hash = 31 * _hash + mTimeoutStage;
- return _hash;
- }
-
- @Override
- @DataClass.Generated.Member
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- // You can override field parcelling by defining methods like:
- // void parcelFieldName(Parcel dest, int flags) { ... }
-
- dest.writeParcelableList(mTrainingAudioList, flags);
- dest.writeInt(mTimeoutStage);
- }
-
- @Override
- @DataClass.Generated.Member
- public int describeContents() { return 0; }
-
- /** @hide */
- @SuppressWarnings({"unchecked", "RedundantCast"})
- @DataClass.Generated.Member
- /* package-private */ HotwordTrainingData(@NonNull Parcel in) {
- // You can override field unparcelling by defining methods like:
- // static FieldType unparcelFieldName(Parcel in) { ... }
-
- List<HotwordTrainingAudio> trainingAudioList = new ArrayList<>();
- in.readParcelableList(trainingAudioList, HotwordTrainingAudio.class.getClassLoader());
- int timeoutStage = in.readInt();
-
- this.mTrainingAudioList = trainingAudioList;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mTrainingAudioList);
- this.mTimeoutStage = timeoutStage;
-
- onConstructed();
- }
-
- @DataClass.Generated.Member
- public static final @NonNull Parcelable.Creator<HotwordTrainingData> CREATOR
- = new Parcelable.Creator<HotwordTrainingData>() {
- @Override
- public HotwordTrainingData[] newArray(int size) {
- return new HotwordTrainingData[size];
- }
-
- @Override
- public HotwordTrainingData createFromParcel(@NonNull Parcel in) {
- return new HotwordTrainingData(in);
- }
- };
-
- /**
- * A builder for {@link HotwordTrainingData}
- */
- @FlaggedApi(Flags.FLAG_ALLOW_TRAINING_DATA_EGRESS_FROM_HDS)
- @SuppressWarnings("WeakerAccess")
- @DataClass.Generated.Member
- public static final class Builder {
-
- private @NonNull List<HotwordTrainingAudio> mTrainingAudioList;
- private int mTimeoutStage;
-
- private long mBuilderFieldsSet = 0L;
-
- public Builder() {
- }
-
- /**
- * The list containing hotword audio that is useful for training.
- */
- @DataClass.Generated.Member
- public @NonNull Builder setTrainingAudioList(@NonNull List<HotwordTrainingAudio> value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x1;
- mTrainingAudioList = value;
- return this;
- }
-
- /** @see #setTrainingAudioList */
- @DataClass.Generated.Member
- public @NonNull Builder addTrainingAudio(@NonNull HotwordTrainingAudio value) {
- if (mTrainingAudioList == null) setTrainingAudioList(new ArrayList<>());
- mTrainingAudioList.add(value);
- return this;
- }
-
- /**
- * App-defined stage when hotword model timed-out while running.
- * <p> Returns -1 if unset.
- */
- @DataClass.Generated.Member
- public @NonNull Builder setTimeoutStage(int value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x2;
- mTimeoutStage = value;
- return this;
- }
-
- /** Builds the instance. This builder should not be touched after calling this! */
- public @NonNull HotwordTrainingData build() {
- checkNotUsed();
- mBuilderFieldsSet |= 0x4; // Mark builder used
-
- if ((mBuilderFieldsSet & 0x1) == 0) {
- mTrainingAudioList = defaultTrainingAudioList();
- }
- if ((mBuilderFieldsSet & 0x2) == 0) {
- mTimeoutStage = defaultTimeoutStage();
- }
- HotwordTrainingData o = new HotwordTrainingData(
- mTrainingAudioList,
- mTimeoutStage);
- return o;
- }
-
- private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x4) != 0) {
- throw new IllegalStateException(
- "This Builder should not be reused. Use a new Builder instance instead");
- }
- }
- }
-
- @DataClass.Generated(
- time = 1697826948280L,
- codegenVersion = "1.0.23",
- sourceFile = "frameworks/base/core/java/android/service/voice/HotwordTrainingData.java",
- inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"trainingAudio\") java.util.List<android.service.voice.HotwordTrainingAudio> mTrainingAudioList\nprivate final int mTimeoutStage\npublic static int getMaxTrainingDataBytes()\nprivate static java.util.List<android.service.voice.HotwordTrainingAudio> defaultTrainingAudioList()\nprivate static int defaultTimeoutStage()\nprivate void onConstructed()\nclass HotwordTrainingData extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
- @Deprecated
- private void __metadata() {}
-
-
- //@formatter:on
- // End of generated code
-
-}
diff --git a/core/java/android/service/voice/SoftwareHotwordDetector.java b/core/java/android/service/voice/SoftwareHotwordDetector.java
index f1bc792696d6..a835b0f57998 100644
--- a/core/java/android/service/voice/SoftwareHotwordDetector.java
+++ b/core/java/android/service/voice/SoftwareHotwordDetector.java
@@ -223,6 +223,13 @@ class SoftwareHotwordDetector extends AbstractDetector {
}
@Override
+ public void onKeyphraseDetectedFromExternalSource(HotwordDetectedResult result) {
+ if (DEBUG) {
+ Slog.i(TAG, "Ignored #onKeyphraseDetectedFromExternalSource event");
+ }
+ }
+
+ @Override
public void onGenericSoundTriggerDetected(
SoundTrigger.GenericRecognitionEvent recognitionEvent) throws RemoteException {
if (DEBUG) {
diff --git a/core/java/android/service/voice/VisualQueryDetectedResult.java b/core/java/android/service/voice/VisualQueryDetectedResult.java
index 322148acdca5..3b617948b41e 100644
--- a/core/java/android/service/voice/VisualQueryDetectedResult.java
+++ b/core/java/android/service/voice/VisualQueryDetectedResult.java
@@ -68,6 +68,22 @@ public final class VisualQueryDetectedResult implements Parcelable {
return 15;
}
+ /**
+ * Detected signal representing the arbitrary data that will make accessibility detections work.
+ *
+ * This field should only be set if the device setting for allowing accessibility data is on
+ * based on the result of {@link VisualQueryDetector#isAccessibilityDetectionEnabled()}. If the
+ * enable bit return by the method is {@code false}, it would suggest a failure to egress the
+ * {@link VisualQueryDetectedResult} object with this field set. The system server will prevent
+ * egress and invoke
+ * {@link VisualQueryDetector.Callback#onFailure(VisualQueryDetectionServiceFailure)}.
+ */
+ @Nullable
+ private final byte[] mAccessibilityDetectionData;
+ private static byte[] defaultAccessibilityDetectionData() {
+ return null;
+ }
+
private void onConstructed() {
Preconditions.checkArgumentInRange(mSpeakerId, 0, getMaxSpeakerId(), "speakerId");
}
@@ -78,7 +94,10 @@ public final class VisualQueryDetectedResult implements Parcelable {
* @hide
*/
public Builder buildUpon() {
- return new Builder().setPartialQuery(mPartialQuery).setSpeakerId(mSpeakerId);
+ return new Builder()
+ .setPartialQuery(mPartialQuery)
+ .setSpeakerId(mSpeakerId)
+ .setAccessibilityDetectionData(mAccessibilityDetectionData);
}
@@ -98,11 +117,13 @@ public final class VisualQueryDetectedResult implements Parcelable {
@DataClass.Generated.Member
/* package-private */ VisualQueryDetectedResult(
@NonNull String partialQuery,
- int speakerId) {
+ int speakerId,
+ @Nullable byte[] accessibilityDetectionData) {
this.mPartialQuery = partialQuery;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mPartialQuery);
this.mSpeakerId = speakerId;
+ this.mAccessibilityDetectionData = accessibilityDetectionData;
onConstructed();
}
@@ -125,6 +146,16 @@ public final class VisualQueryDetectedResult implements Parcelable {
return mSpeakerId;
}
+ /**
+ * Detected signal representing the data for allowing accessibility feature. This field can
+ * only be set when the secure device settings is set to true by either settings page UI or
+ * {@link VisualQueryDetector@setAccessibilityDetectionEnabled(boolean)}
+ */
+ @DataClass.Generated.Member
+ public @Nullable byte[] getAccessibilityDetectionData() {
+ return mAccessibilityDetectionData;
+ }
+
@Override
@DataClass.Generated.Member
public String toString() {
@@ -133,7 +164,8 @@ public final class VisualQueryDetectedResult implements Parcelable {
return "VisualQueryDetectedResult { " +
"partialQuery = " + mPartialQuery + ", " +
- "speakerId = " + mSpeakerId +
+ "speakerId = " + mSpeakerId + ", " +
+ "accessibilityDetectionData = " + java.util.Arrays.toString(mAccessibilityDetectionData) +
" }";
}
@@ -151,7 +183,8 @@ public final class VisualQueryDetectedResult implements Parcelable {
//noinspection PointlessBooleanExpression
return true
&& Objects.equals(mPartialQuery, that.mPartialQuery)
- && mSpeakerId == that.mSpeakerId;
+ && mSpeakerId == that.mSpeakerId
+ && java.util.Arrays.equals(mAccessibilityDetectionData, that.mAccessibilityDetectionData);
}
@Override
@@ -163,6 +196,7 @@ public final class VisualQueryDetectedResult implements Parcelable {
int _hash = 1;
_hash = 31 * _hash + Objects.hashCode(mPartialQuery);
_hash = 31 * _hash + mSpeakerId;
+ _hash = 31 * _hash + java.util.Arrays.hashCode(mAccessibilityDetectionData);
return _hash;
}
@@ -174,6 +208,7 @@ public final class VisualQueryDetectedResult implements Parcelable {
dest.writeString(mPartialQuery);
dest.writeInt(mSpeakerId);
+ dest.writeByteArray(mAccessibilityDetectionData);
}
@Override
@@ -189,11 +224,13 @@ public final class VisualQueryDetectedResult implements Parcelable {
String partialQuery = in.readString();
int speakerId = in.readInt();
+ byte[] accessibilityDetectionData = in.createByteArray();
this.mPartialQuery = partialQuery;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mPartialQuery);
this.mSpeakerId = speakerId;
+ this.mAccessibilityDetectionData = accessibilityDetectionData;
onConstructed();
}
@@ -221,6 +258,7 @@ public final class VisualQueryDetectedResult implements Parcelable {
private @NonNull String mPartialQuery;
private int mSpeakerId;
+ private @Nullable byte[] mAccessibilityDetectionData;
private long mBuilderFieldsSet = 0L;
@@ -251,10 +289,23 @@ public final class VisualQueryDetectedResult implements Parcelable {
return this;
}
+ /**
+ * Detected signal representing the data for allowing accessibility feature. This field can
+ * only be set when the secure device settings is set to true by either settings page UI or
+ * {@link VisualQueryDetector@setAccessibilityDetectionEnabled(boolean)}
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setAccessibilityDetectionData(@NonNull byte... value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4;
+ mAccessibilityDetectionData = value;
+ return this;
+ }
+
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull VisualQueryDetectedResult build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x4; // Mark builder used
+ mBuilderFieldsSet |= 0x8; // Mark builder used
if ((mBuilderFieldsSet & 0x1) == 0) {
mPartialQuery = defaultPartialQuery();
@@ -262,14 +313,18 @@ public final class VisualQueryDetectedResult implements Parcelable {
if ((mBuilderFieldsSet & 0x2) == 0) {
mSpeakerId = defaultSpeakerId();
}
+ if ((mBuilderFieldsSet & 0x4) == 0) {
+ mAccessibilityDetectionData = defaultAccessibilityDetectionData();
+ }
VisualQueryDetectedResult o = new VisualQueryDetectedResult(
mPartialQuery,
- mSpeakerId);
+ mSpeakerId,
+ mAccessibilityDetectionData);
return o;
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x4) != 0) {
+ if ((mBuilderFieldsSet & 0x8) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -277,10 +332,10 @@ public final class VisualQueryDetectedResult implements Parcelable {
}
@DataClass.Generated(
- time = 1704949386772L,
+ time = 1707429290528L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/service/voice/VisualQueryDetectedResult.java",
- inputSignatures = "private final @android.annotation.NonNull java.lang.String mPartialQuery\nprivate final int mSpeakerId\nprivate static java.lang.String defaultPartialQuery()\nprivate static int defaultSpeakerId()\npublic static int getMaxSpeakerId()\nprivate void onConstructed()\npublic android.service.voice.VisualQueryDetectedResult.Builder buildUpon()\nclass VisualQueryDetectedResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
+ inputSignatures = "private final @android.annotation.NonNull java.lang.String mPartialQuery\nprivate final int mSpeakerId\nprivate final @android.annotation.Nullable byte[] mAccessibilityDetectionData\nprivate static java.lang.String defaultPartialQuery()\nprivate static int defaultSpeakerId()\npublic static int getMaxSpeakerId()\nprivate static byte[] defaultAccessibilityDetectionData()\nprivate void onConstructed()\npublic android.service.voice.VisualQueryDetectedResult.Builder buildUpon()\nclass VisualQueryDetectedResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/service/voice/VisualQueryDetector.java b/core/java/android/service/voice/VisualQueryDetector.java
index 23847fe76ecc..bf8de06fd244 100644
--- a/core/java/android/service/voice/VisualQueryDetector.java
+++ b/core/java/android/service/voice/VisualQueryDetector.java
@@ -39,6 +39,7 @@ import android.text.TextUtils;
import android.util.Slog;
import com.android.internal.app.IHotwordRecognitionStatusCallback;
+import com.android.internal.app.IVoiceInteractionAccessibilitySettingsListener;
import com.android.internal.app.IVoiceInteractionManagerService;
import com.android.internal.infra.AndroidFuture;
@@ -61,6 +62,8 @@ import java.util.function.Consumer;
public class VisualQueryDetector {
private static final String TAG = VisualQueryDetector.class.getSimpleName();
private static final boolean DEBUG = false;
+ private static final int SETTINGS_DISABLE_BIT = 0;
+ private static final int SETTINGS_ENABLE_BIT = 1;
private final Callback mCallback;
private final Executor mExecutor;
@@ -68,6 +71,8 @@ public class VisualQueryDetector {
private final IVoiceInteractionManagerService mManagerService;
private final VisualQueryDetectorInitializationDelegate mInitializationDelegate;
private final String mAttributionTag;
+ // Used to manage the internal mapping of exposed listener API and internal aidl impl
+ private AccessibilityDetectionEnabledListenerWrapper mActiveAccessibilityListenerWrapper = null;
VisualQueryDetector(
IVoiceInteractionManagerService managerService,
@@ -174,6 +179,108 @@ public class VisualQueryDetector {
}
}
+ /**
+ * Gets the binary value that controls the egress of accessibility data from
+ * {@link VisualQueryDetectedResult#setAccessibilityDetectionData(byte[])} is enabled.
+ *
+ * @return boolean value denoting if the setting is on. Default is {@code false}.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ALLOW_COMPLEX_RESULTS_EGRESS_FROM_VQDS)
+ public boolean isAccessibilityDetectionEnabled() {
+ Slog.d(TAG, "Fetching accessibility setting");
+ synchronized (mInitializationDelegate.getLock()) {
+ try {
+ return mManagerService.getAccessibilityDetectionEnabled();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Sets a listener subscribing to the value of the system setting that controls the egress of
+ * accessibility data from
+ * {@link VisualQueryDetectedResult#setAccessibilityDetectionData(byte[])} is enabled.
+ *
+ * Only one listener can be set at a time. The listener set must be unset with
+ * {@link clearAccessibilityDetectionEnabledListener(Consumer<Boolean>)}
+ * in order to set a new listener. Otherwise, this method will throw a
+ * {@link IllegalStateException}.
+ *
+ * @param listener Listener of type {@code Consumer<Boolean>} to subscribe to the value update.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ALLOW_COMPLEX_RESULTS_EGRESS_FROM_VQDS)
+ public void setAccessibilityDetectionEnabledListener(@NonNull Consumer<Boolean> listener) {
+ Slog.d(TAG, "Registering Accessibility settings listener.");
+ synchronized (mInitializationDelegate.getLock()) {
+ try {
+ if (mActiveAccessibilityListenerWrapper != null) {
+ Slog.e(TAG, "Fail to register accessibility setting listener: "
+ + "already registered and not unregistered.");
+ throw new IllegalStateException(
+ "Cannot register listener with listeners already set.");
+ }
+ mActiveAccessibilityListenerWrapper =
+ new AccessibilityDetectionEnabledListenerWrapper(listener);
+ mManagerService.registerAccessibilityDetectionSettingsListener(
+ mActiveAccessibilityListenerWrapper);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Clear the listener that has been set with
+ * {@link setAccessibilityDetectionEnabledListener(Consumer<Boolean>)} such that when the value
+ * of the setting that controls the egress of accessibility data is changed the listener gets
+ * notified.
+ *
+ * If there is not listener that has been registered, the call to this method will lead to a
+ * {@link IllegalStateException}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ALLOW_COMPLEX_RESULTS_EGRESS_FROM_VQDS)
+ public void clearAccessibilityDetectionEnabledListener() {
+ Slog.d(TAG, "Unregistering Accessibility settings listener.");
+ synchronized (mInitializationDelegate.getLock()) {
+ try {
+ if (mActiveAccessibilityListenerWrapper == null) {
+ Slog.e(TAG, "Not able to remove the listener: listener does not exist.");
+ throw new IllegalStateException("Cannot clear listener since it is not set.");
+ }
+ mManagerService.unregisterAccessibilityDetectionSettingsListener(
+ mActiveAccessibilityListenerWrapper);
+ mActiveAccessibilityListenerWrapper = null;
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+
+ private final class AccessibilityDetectionEnabledListenerWrapper
+ extends IVoiceInteractionAccessibilitySettingsListener.Stub {
+
+ private Consumer<Boolean> mListener;
+
+ AccessibilityDetectionEnabledListenerWrapper(Consumer<Boolean> listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void onAccessibilityDetectionChanged(boolean enabled) {
+ mListener.accept(enabled);
+ }
+ }
+
/** @hide */
public void dump(String prefix, PrintWriter pw) {
synchronized (mInitializationDelegate.getLock()) {
@@ -414,6 +521,13 @@ public class VisualQueryDetector {
}
@Override
+ public void onKeyphraseDetectedFromExternalSource(HotwordDetectedResult result) {
+ if (DEBUG) {
+ Slog.i(TAG, "Ignored #onKeyphraseDetectedFromExternalSource event");
+ }
+ }
+
+ @Override
public void onGenericSoundTriggerDetected(
SoundTrigger.GenericRecognitionEvent recognitionEvent) throws RemoteException {
if (DEBUG) {
diff --git a/core/java/android/service/voice/VoiceInteractionManagerInternal.java b/core/java/android/service/voice/VoiceInteractionManagerInternal.java
index 270f848598d7..7d2773387c1a 100644
--- a/core/java/android/service/voice/VoiceInteractionManagerInternal.java
+++ b/core/java/android/service/voice/VoiceInteractionManagerInternal.java
@@ -19,14 +19,17 @@ package android.service.voice;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.content.ComponentName;
+import android.media.AudioFormat;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
import com.android.internal.annotations.Immutable;
/**
- * @hide
- * Private interface to the VoiceInteractionManagerService for use by ActivityManagerService.
+ * @hide Private interface to the VoiceInteractionManagerService for use within system_server.
*/
public abstract class VoiceInteractionManagerInternal {
@@ -77,6 +80,25 @@ public abstract class VoiceInteractionManagerInternal {
public abstract void onPreCreatedUserConversion(@UserIdInt int userId);
/**
+ * Called by {@link com.android.server.wearable.WearableSensingManagerPerUserService} when a
+ * wearable starts sending audio data for hotword detection.
+ *
+ * @param audioStream The audio data.
+ * @param audioFormat The format of the audio data.
+ * @param options Options supporting hotword detection.
+ * @param targetVisComponentName The target VoiceInteractionService ComponentName
+ * @param userId The user ID of the calling wearable service
+ * @param callback The callback to notify the caller of the hotword detection result.
+ */
+ public abstract void startListeningFromWearable(
+ ParcelFileDescriptor audioStream,
+ AudioFormat audioFormat,
+ PersistableBundle options,
+ ComponentName targetVisComponentName,
+ int userId,
+ WearableHotwordDetectionCallback callback);
+
+ /**
* Provides the uids of the currently active
* {@link android.service.voice.HotwordDetectionService} and its owning package. The
* HotwordDetectionService is an isolated service, so it has a separate uid.
@@ -101,4 +123,20 @@ public abstract class VoiceInteractionManagerInternal {
return mOwnerUid;
}
}
+
+ /**
+ * Callback for returning the detected hotword result to the wearable.
+ *
+ * @hide
+ */
+ public interface WearableHotwordDetectionCallback {
+ /** Called when hotword is detected. */
+ void onDetected();
+
+ /** Called when hotword is not detected. */
+ void onRejected();
+
+ /** Called when an unexpected error occurs. */
+ void onError(String errorMessage);
+ }
}
diff --git a/core/java/android/service/voice/flags/flags.aconfig b/core/java/android/service/voice/flags/flags.aconfig
index f870db5f95f0..22e8cddbfdb8 100644
--- a/core/java/android/service/voice/flags/flags.aconfig
+++ b/core/java/android/service/voice/flags/flags.aconfig
@@ -16,7 +16,7 @@ flag {
flag {
name: "allow_foreground_activities_in_on_show"
- namespace: "voice_interaction_session"
+ namespace: "machine_learning"
description: "This flag allows providing foreground app component along with onShow args."
bug: "319409708"
}
diff --git a/core/java/android/service/wallpaper/Android.bp b/core/java/android/service/wallpaper/Android.bp
index a527d3d10fdf..552a07d97bf5 100644
--- a/core/java/android/service/wallpaper/Android.bp
+++ b/core/java/android/service/wallpaper/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_",
// 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"
diff --git a/core/java/android/service/wearable/IWearableSensingService.aidl b/core/java/android/service/wearable/IWearableSensingService.aidl
index f67dcff3ebfe..dffadf0cda70 100644
--- a/core/java/android/service/wearable/IWearableSensingService.aidl
+++ b/core/java/android/service/wearable/IWearableSensingService.aidl
@@ -28,11 +28,15 @@ import android.os.SharedMemory;
* @hide
*/
oneway interface IWearableSensingService {
- void provideSecureWearableConnection(in ParcelFileDescriptor parcelFileDescriptor, in RemoteCallback callback);
+ void provideSecureConnection(in ParcelFileDescriptor parcelFileDescriptor, in RemoteCallback callback);
void provideDataStream(in ParcelFileDescriptor parcelFileDescriptor, in RemoteCallback callback);
void provideData(in PersistableBundle data, in SharedMemory sharedMemory, in RemoteCallback callback);
void registerDataRequestObserver(int dataType, in RemoteCallback dataRequestCallback, int dataRequestObserverId, in String packageName, in RemoteCallback statusCallback);
void unregisterDataRequestObserver(int dataType, int dataRequestObserverId, in String packageName, in RemoteCallback statusCallback);
+ void startHotwordRecognition(in RemoteCallback wearableHotwordCallback, in RemoteCallback statusCallback);
+ void stopHotwordRecognition(in RemoteCallback statusCallback);
+ void onValidatedByHotwordDetectionService();
+ void stopActiveHotwordAudio();
void startDetection(in AmbientContextEventRequest request, in String packageName,
in RemoteCallback detectionResultCallback, in RemoteCallback statusCallback);
void stopDetection(in String packageName);
diff --git a/core/java/android/service/wearable/WearableSensingService.java b/core/java/android/service/wearable/WearableSensingService.java
index bb6e030456a7..a2770172d3ca 100644
--- a/core/java/android/service/wearable/WearableSensingService.java
+++ b/core/java/android/service/wearable/WearableSensingService.java
@@ -37,6 +37,7 @@ import android.os.RemoteCallback;
import android.os.SharedMemory;
import android.service.ambientcontext.AmbientContextDetectionResult;
import android.service.ambientcontext.AmbientContextDetectionServiceStatus;
+import android.service.voice.HotwordAudioStream;
import android.util.Slog;
import android.util.SparseArray;
@@ -85,6 +86,14 @@ public abstract class WearableSensingService extends Service {
"android.app.wearable.WearableSensingStatusBundleKey";
/**
+ * The bundle key for hotword audio stream, used in {@code RemoteCallback#sendResult}.
+ *
+ * @hide
+ */
+ public static final String HOTWORD_AUDIO_STREAM_BUNDLE_KEY =
+ "android.app.wearable.HotwordAudioStreamBundleKey";
+
+ /**
* The {@link Intent} that must be declared as handled by the service. To be supported, the
* service must also require the
* {@link android.Manifest.permission#BIND_WEARABLE_SENSING_SERVICE}
@@ -103,11 +112,11 @@ public abstract class WearableSensingService extends Service {
return new IWearableSensingService.Stub() {
/** {@inheritDoc} */
@Override
- public void provideSecureWearableConnection(
+ public void provideSecureConnection(
ParcelFileDescriptor secureWearableConnection, RemoteCallback callback) {
Objects.requireNonNull(secureWearableConnection);
Consumer<Integer> consumer = createWearableStatusConsumer(callback);
- WearableSensingService.this.onSecureWearableConnectionProvided(
+ WearableSensingService.this.onSecureConnectionProvided(
secureWearableConnection, consumer);
}
@@ -181,6 +190,50 @@ public abstract class WearableSensingService extends Service {
dataType, packageName, dataRequester, statusConsumer);
}
+ @Override
+ public void startHotwordRecognition(
+ RemoteCallback wearableHotwordCallback, RemoteCallback statusCallback) {
+ Consumer<HotwordAudioStream> hotwordAudioConsumer =
+ (hotwordAudioStream) -> {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(
+ HOTWORD_AUDIO_STREAM_BUNDLE_KEY, hotwordAudioStream);
+ wearableHotwordCallback.sendResult(bundle);
+ };
+ Consumer<Integer> statusConsumer =
+ response -> {
+ Bundle bundle = new Bundle();
+ bundle.putInt(STATUS_RESPONSE_BUNDLE_KEY, response);
+ statusCallback.sendResult(bundle);
+ };
+ WearableSensingService.this.onStartHotwordRecognition(
+ hotwordAudioConsumer, statusConsumer);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void stopHotwordRecognition(RemoteCallback statusCallback) {
+ Consumer<Integer> statusConsumer =
+ response -> {
+ Bundle bundle = new Bundle();
+ bundle.putInt(STATUS_RESPONSE_BUNDLE_KEY, response);
+ statusCallback.sendResult(bundle);
+ };
+ WearableSensingService.this.onStopHotwordRecognition(statusConsumer);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onValidatedByHotwordDetectionService() {
+ WearableSensingService.this.onValidatedByHotwordDetectionService();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void stopActiveHotwordAudio() {
+ WearableSensingService.this.onStopHotwordAudioStream();
+ }
+
/** {@inheritDoc} */
@Override
public void startDetection(
@@ -258,12 +311,12 @@ public abstract class WearableSensingService extends Service {
/**
* Called when a secure connection to the wearable is available. See {@link
- * WearableSensingManager#provideWearableConnection(ParcelFileDescriptor, Executor, Consumer)}
+ * WearableSensingManager#provideConnection(ParcelFileDescriptor, Executor, Consumer)}
* for details about the secure connection.
*
* <p>When the {@code secureWearableConnection} is closed, the system will send a {@link
* WearableSensingManager#STATUS_CHANNEL_ERROR} status code to the status consumer provided by
- * the caller of {@link WearableSensingManager#provideWearableConnection(ParcelFileDescriptor,
+ * the caller of {@link WearableSensingManager#provideConnection(ParcelFileDescriptor,
* Executor, Consumer)}.
*
* <p>The implementing class should override this method. It should return an appropriate status
@@ -274,7 +327,7 @@ public abstract class WearableSensingService extends Service {
*/
@FlaggedApi(Flags.FLAG_ENABLE_PROVIDE_WEARABLE_CONNECTION_API)
@BinderThread
- public void onSecureWearableConnectionProvided(
+ public void onSecureConnectionProvided(
@NonNull ParcelFileDescriptor secureWearableConnection,
@NonNull Consumer<Integer> statusConsumer) {
statusConsumer.accept(WearableSensingManager.STATUS_UNSUPPORTED_OPERATION);
@@ -377,6 +430,100 @@ public abstract class WearableSensingService extends Service {
}
/**
+ * Called when the wearable is requested to start hotword recognition.
+ *
+ * <p>This method is expected to be overridden by a derived class. The implementation should
+ * store the {@code hotwordAudioConsumer} and send it the audio data when first-stage hotword is
+ * detected from the wearable. It should also send a {@link
+ * WearableSensingManager#STATUS_SUCCESS} status code to the {@code statusConsumer} unless it
+ * encounters an error condition described by a status code listed in {@link
+ * WearableSensingManager}, such as {@link WearableSensingManager#STATUS_WEARABLE_UNAVAILABLE},
+ * in which case it should return the corresponding status code.
+ *
+ * <p>The implementation should also store the {@code statusConsumer}. If the wearable stops
+ * listening for hotword for any reason other than {@link #onStopListeningForHotword(Consumer)}
+ * being invoked, it should send an appropriate status code listed in {@link
+ * WearableSensingManager} to {@code statusConsumer}. If the error condition cannot be described
+ * by any of those status codes, it should send a {@link WearableSensingManager#STATUS_UNKNOWN}.
+ *
+ * <p>If this method is called again, the implementation should use the new {@code
+ * hotwordAudioConsumer} and discard any previous ones it received.
+ *
+ * <p>At this time, the {@code timestamp} field in the {@link HotwordAudioStream} is not used
+ * and will be discarded by the system.
+ *
+ * @param hotwordAudioConsumer The consumer for the wearable hotword audio data.
+ * @param statusConsumer The consumer for the service status.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_HOTWORD_WEARABLE_SENSING_API)
+ @BinderThread
+ public void onStartHotwordRecognition(
+ @NonNull Consumer<HotwordAudioStream> hotwordAudioConsumer,
+ @NonNull Consumer<Integer> statusConsumer) {
+ if (Flags.enableUnsupportedOperationStatusCode()) {
+ statusConsumer.accept(WearableSensingManager.STATUS_UNSUPPORTED_OPERATION);
+ }
+ }
+
+ /**
+ * Called when the wearable is requested to stop hotword recognition.
+ *
+ * <p>This method is expected to be overridden by a derived class. It should send a {@link
+ * WearableSensingManager#STATUS_SUCCESS} status code to the {@code statusConsumer} unless it
+ * encounters an error condition described by a status code listed in {@link
+ * WearableSensingManager}, such as {@link WearableSensingManager#STATUS_WEARABLE_UNAVAILABLE},
+ * in which case it should return the corresponding status code.
+ *
+ * @param statusConsumer The consumer for the service status.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_HOTWORD_WEARABLE_SENSING_API)
+ @BinderThread
+ public void onStopHotwordRecognition(@NonNull Consumer<Integer> statusConsumer) {
+ if (Flags.enableUnsupportedOperationStatusCode()) {
+ statusConsumer.accept(WearableSensingManager.STATUS_UNSUPPORTED_OPERATION);
+ }
+ }
+
+ /**
+ * Called when hotword audio data sent to the {@code hotwordAudioConsumer} in {@link
+ * #onStartListeningForHotword(Consumer, Consumer)} is accepted by the
+ * {@link android.service.voice.HotwordDetectionService} as valid hotword.
+ *
+ * <p>After the implementation of this class sends the hotword audio data to the {@code
+ * hotwordAudioConsumer} in {@link #onStartListeningForHotword(Consumer,
+ * Consumer)}, the system will forward the data into {@link
+ * android.service.voice.HotwordDetectionService} (which runs in an isolated process) for
+ * second-stage hotword detection. If accepted as valid hotword there, this method will be
+ * called, and then the system will send the data to the currently active {@link
+ * android.service.voice.AlwaysOnHotwordDetector} (which may not run in an isolated process).
+ *
+ * <p>This method is expected to be overridden by a derived class. The implementation must
+ * request the wearable to turn on the microphone indicator to notify the user that audio data
+ * is being used outside of the isolated environment.
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_HOTWORD_WEARABLE_SENSING_API)
+ @BinderThread
+ public void onValidatedByHotwordDetectionService() {}
+
+ /**
+ * Called when the currently active hotword audio stream is no longer needed.
+ *
+ * <p>This method can be called as a result of hotword rejection by {@link
+ * android.service.voice.HotwordDetectionService}, or the {@link
+ * android.service.voice.AlwaysOnHotwordDetector} closing the data stream it received, or a
+ * non-recoverable error occurred before the data reaches the {@link
+ * android.service.voice.HotwordDetectionService} or the {@link
+ * android.service.voice.AlwaysOnHotwordDetector}.
+ *
+ * <p>This method is expected to be overridden by a derived class. The implementation should
+ * stop sending hotword audio data to the {@code hotwordAudioConsumer} in {@link
+ * #onStartListeningForHotword(Consumer, Consumer)}
+ */
+ @FlaggedApi(Flags.FLAG_ENABLE_HOTWORD_WEARABLE_SENSING_API)
+ @BinderThread
+ public void onStopHotwordAudioStream() {}
+
+ /**
* Called when a client app requests starting detection of the events in the request. The
* implementation should keep track of whether the user has explicitly consented to detecting
* the events using on-going ambient sensor (e.g. microphone), and agreed to share the
@@ -460,4 +607,6 @@ public abstract class WearableSensingService extends Service {
statusCallback.sendResult(bundle);
};
}
+
+
}
diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java
index 2028c4057c01..9db8aa167498 100644
--- a/core/java/android/text/BoringLayout.java
+++ b/core/java/android/text/BoringLayout.java
@@ -273,7 +273,7 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
outerwidth /* ellipsizedWidth */, null /* ellipsize */, 1 /* maxLines */,
BREAK_STRATEGY_SIMPLE, HYPHENATION_FREQUENCY_NONE, null /* leftIndents */,
null /* rightIndents */, JUSTIFICATION_MODE_NONE, LineBreakConfig.NONE, false,
- null);
+ false /* shiftDrawingOffsetForStartOverhang */, null);
mEllipsizedWidth = outerwidth;
mEllipsizedStart = 0;
@@ -346,7 +346,8 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
ellipsizedWidth, ellipsize, 1 /* maxLines */,
BREAK_STRATEGY_SIMPLE, HYPHENATION_FREQUENCY_NONE, null /* leftIndents */,
null /* rightIndents */, JUSTIFICATION_MODE_NONE,
- LineBreakConfig.NONE, metrics, false /* useBoundsForWidth */, null);
+ LineBreakConfig.NONE, metrics, false /* useBoundsForWidth */,
+ false /* shiftDrawingOffsetForStartOverhang */, null);
}
/** @hide */
@@ -363,12 +364,14 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
TextUtils.TruncateAt ellipsize,
Metrics metrics,
boolean useBoundsForWidth,
+ boolean shiftDrawingOffsetForStartOverhang,
@Nullable Paint.FontMetrics minimumFontMetrics) {
this(text, paint, width, align, TextDirectionHeuristics.LTR,
spacingMult, spacingAdd, includePad, fallbackLineSpacing, ellipsizedWidth,
ellipsize, 1 /* maxLines */, Layout.BREAK_STRATEGY_SIMPLE,
Layout.HYPHENATION_FREQUENCY_NONE, null, null, Layout.JUSTIFICATION_MODE_NONE,
- LineBreakConfig.NONE, metrics, useBoundsForWidth, minimumFontMetrics);
+ LineBreakConfig.NONE, metrics, useBoundsForWidth,
+ shiftDrawingOffsetForStartOverhang, minimumFontMetrics);
}
/* package */ BoringLayout(
@@ -392,12 +395,14 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
LineBreakConfig lineBreakConfig,
Metrics metrics,
boolean useBoundsForWidth,
+ boolean shiftDrawingOffsetForStartOverhang,
@Nullable Paint.FontMetrics minimumFontMetrics) {
super(text, paint, width, align, textDir, spacingMult, spacingAdd, includePad,
fallbackLineSpacing, ellipsizedWidth, ellipsize, maxLines, breakStrategy,
hyphenationFrequency, leftIndents, rightIndents, justificationMode,
- lineBreakConfig, useBoundsForWidth, minimumFontMetrics);
+ lineBreakConfig, useBoundsForWidth, shiftDrawingOffsetForStartOverhang,
+ minimumFontMetrics);
boolean trust;
@@ -712,7 +717,7 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
int cursorOffset) {
if (mDirect != null && highlight == null) {
float leftShift = 0;
- if (getUseBoundsForWidth()) {
+ if (getUseBoundsForWidth() && getShiftDrawingOffsetForStartOverhang()) {
RectF drawingRect = computeDrawingBoundingBox();
if (drawingRect.left < 0) {
leftShift = -drawingRect.left;
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 928604983b70..cce4f7bf7b1f 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -25,6 +25,7 @@ import android.annotation.FloatRange;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Paint;
import android.graphics.Rect;
@@ -317,6 +318,35 @@ public class DynamicLayout extends Layout {
}
/**
+ * Set true for shifting the drawing x offset for showing overhang at the start position.
+ *
+ * This flag is ignored if the {@link #getUseBoundsForWidth()} is false.
+ *
+ * If this value is false, the Layout draws text from the zero even if there is a glyph
+ * stroke in a region where the x coordinate is negative.
+ *
+ * If this value is true, the Layout draws text with shifting the x coordinate of the
+ * drawing bounding box.
+ *
+ * This value is false by default.
+ *
+ * @param shiftDrawingOffsetForStartOverhang true for shifting the drawing offset for
+ * showing the stroke that is in the region where
+ * the x coordinate is negative.
+ * @see #setUseBoundsForWidth(boolean)
+ * @see #getUseBoundsForWidth()
+ */
+ @NonNull
+ // The corresponding getter is getShiftDrawingOffsetForStartOverhang()
+ @SuppressLint("MissingGetterMatchingBuilder")
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
+ public Builder setShiftDrawingOffsetForStartOverhang(
+ boolean shiftDrawingOffsetForStartOverhang) {
+ mShiftDrawingOffsetForStartOverhang = shiftDrawingOffsetForStartOverhang;
+ return this;
+ }
+
+ /**
* Set the minimum font metrics used for line spacing.
*
* <p>
@@ -386,6 +416,7 @@ public class DynamicLayout extends Layout {
private int mEllipsizedWidth;
private LineBreakConfig mLineBreakConfig = LineBreakConfig.NONE;
private boolean mUseBoundsForWidth;
+ private boolean mShiftDrawingOffsetForStartOverhang;
private @Nullable Paint.FontMetrics mMinimumFontMetrics;
private final Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt();
@@ -462,7 +493,8 @@ public class DynamicLayout extends Layout {
false /* fallbackLineSpacing */, ellipsizedWidth, ellipsize,
Integer.MAX_VALUE /* maxLines */, breakStrategy, hyphenationFrequency,
null /* leftIndents */, null /* rightIndents */, justificationMode,
- lineBreakConfig, false /* useBoundsForWidth */, null /* minimumFontMetrics */);
+ lineBreakConfig, false /* useBoundsForWidth */, false,
+ null /* minimumFontMetrics */);
final Builder b = Builder.obtain(base, paint, width)
.setAlignment(align)
@@ -488,7 +520,8 @@ public class DynamicLayout extends Layout {
b.mIncludePad, b.mFallbackLineSpacing, b.mEllipsizedWidth, b.mEllipsize,
Integer.MAX_VALUE /* maxLines */, b.mBreakStrategy, b.mHyphenationFrequency,
null /* leftIndents */, null /* rightIndents */, b.mJustificationMode,
- b.mLineBreakConfig, b.mUseBoundsForWidth, b.mMinimumFontMetrics);
+ b.mLineBreakConfig, b.mUseBoundsForWidth, b.mShiftDrawingOffsetForStartOverhang,
+ b.mMinimumFontMetrics);
mDisplay = b.mDisplay;
mIncludePad = b.mIncludePad;
@@ -516,6 +549,7 @@ public class DynamicLayout extends Layout {
mBase = b.mBase;
mFallbackLineSpacing = b.mFallbackLineSpacing;
mUseBoundsForWidth = b.mUseBoundsForWidth;
+ mShiftDrawingOffsetForStartOverhang = b.mShiftDrawingOffsetForStartOverhang;
mMinimumFontMetrics = b.mMinimumFontMetrics;
if (b.mEllipsize != null) {
mInts = new PackedIntVector(COLUMNS_ELLIPSIZE);
@@ -713,6 +747,7 @@ public class DynamicLayout extends Layout {
.setAddLastLineLineSpacing(!islast)
.setIncludePad(false)
.setUseBoundsForWidth(mUseBoundsForWidth)
+ .setShiftDrawingOffsetForStartOverhang(mShiftDrawingOffsetForStartOverhang)
.setMinimumFontMetrics(mMinimumFontMetrics)
.setCalculateBounds(true);
@@ -1392,6 +1427,7 @@ public class DynamicLayout extends Layout {
private Rect mTempRect = new Rect();
private boolean mUseBoundsForWidth;
+ private boolean mShiftDrawingOffsetForStartOverhang;
@Nullable Paint.FontMetrics mMinimumFontMetrics;
@UnsupportedAppUsage
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index e5d199ad8e46..8dee4b19c6d3 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -16,6 +16,7 @@
package android.text;
+import static com.android.graphics.hwui.flags.Flags.highContrastTextLuminance;
import static com.android.text.flags.Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE;
import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH;
import static com.android.text.flags.Flags.FLAG_LETTER_SPACING_JUSTIFICATION;
@@ -28,7 +29,9 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.compat.annotation.UnsupportedAppUsage;
+import android.graphics.BlendMode;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
@@ -44,8 +47,11 @@ import android.text.style.LineBackgroundSpan;
import android.text.style.ParagraphStyle;
import android.text.style.ReplacementSpan;
import android.text.style.TabStopSpan;
+import android.widget.TextView;
+import com.android.graphics.hwui.flags.Flags;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.ColorUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
@@ -299,7 +305,7 @@ public abstract class Layout {
this(text, paint, width, align, TextDirectionHeuristics.FIRSTSTRONG_LTR,
spacingMult, spacingAdd, false, false, 0, null, Integer.MAX_VALUE,
BREAK_STRATEGY_SIMPLE, HYPHENATION_FREQUENCY_NONE, null, null,
- JUSTIFICATION_MODE_NONE, LineBreakConfig.NONE, false, null);
+ JUSTIFICATION_MODE_NONE, LineBreakConfig.NONE, false, false, null);
}
/**
@@ -349,6 +355,7 @@ public abstract class Layout {
int justificationMode,
LineBreakConfig lineBreakConfig,
boolean useBoundsForWidth,
+ boolean shiftDrawingOffsetForStartOverhang,
Paint.FontMetrics minimumFontMetrics
) {
@@ -384,6 +391,7 @@ public abstract class Layout {
mJustificationMode = justificationMode;
mLineBreakConfig = lineBreakConfig;
mUseBoundsForWidth = useBoundsForWidth;
+ mShiftDrawingOffsetForStartOverhang = shiftDrawingOffsetForStartOverhang;
mMinimumFontMetrics = minimumFontMetrics;
}
@@ -465,7 +473,7 @@ public abstract class Layout {
@Nullable Paint selectionPaint,
int cursorOffsetVertical) {
float leftShift = 0;
- if (mUseBoundsForWidth) {
+ if (mUseBoundsForWidth && mShiftDrawingOffsetForStartOverhang) {
RectF drawingRect = computeDrawingBoundingBox();
if (drawingRect.left < 0) {
leftShift = -drawingRect.left;
@@ -477,9 +485,23 @@ public abstract class Layout {
int lastLine = TextUtils.unpackRangeEndFromLong(lineRange);
if (lastLine < 0) return;
- drawWithoutText(canvas, highlightPaths, highlightPaints, selectionPath, selectionPaint,
- cursorOffsetVertical, firstLine, lastLine);
+ if (shouldDrawHighlightsOnTop(canvas)) {
+ drawBackground(canvas, firstLine, lastLine);
+ } else {
+ drawWithoutText(canvas, highlightPaths, highlightPaints, selectionPath, selectionPaint,
+ cursorOffsetVertical, firstLine, lastLine);
+ }
+
drawText(canvas, firstLine, lastLine);
+
+ // Since high contrast text draws a solid rectangle background behind the text, it covers up
+ // the highlights and selections. In this case we draw over the top of the text with a
+ // blend mode that ensures the text stays high-contrast.
+ if (shouldDrawHighlightsOnTop(canvas)) {
+ drawHighlights(canvas, highlightPaths, highlightPaints, selectionPath, selectionPaint,
+ cursorOffsetVertical, firstLine, lastLine);
+ }
+
if (leftShift != 0) {
// Manually translate back to the original position because of b/324498002, using
// save/restore disappears the toggle switch drawables.
@@ -487,6 +509,19 @@ public abstract class Layout {
}
}
+ private static boolean shouldDrawHighlightsOnTop(Canvas canvas) {
+ return Flags.highContrastTextSmallTextRect() && canvas.isHighContrastTextEnabled();
+ }
+
+ private static Paint setToHighlightPaint(Paint p, BlendMode blendMode, Paint outPaint) {
+ if (p == null) return null;
+ outPaint.set(p);
+ outPaint.setBlendMode(blendMode);
+ // Yellow for maximum contrast
+ outPaint.setColor(Color.YELLOW);
+ return outPaint;
+ }
+
/**
* Draw text part of this layout.
*
@@ -539,11 +574,28 @@ public abstract class Layout {
int firstLine,
int lastLine) {
drawBackground(canvas, firstLine, lastLine);
+ drawHighlights(canvas, highlightPaths, highlightPaints, selectionPath, selectionPaint,
+ cursorOffsetVertical, firstLine, lastLine);
+ }
+
+ /**
+ * @hide public for Editor.java
+ */
+ public void drawHighlights(
+ @NonNull Canvas canvas,
+ @Nullable List<Path> highlightPaths,
+ @Nullable List<Paint> highlightPaints,
+ @Nullable Path selectionPath,
+ @Nullable Paint selectionPaint,
+ int cursorOffsetVertical,
+ int firstLine,
+ int lastLine) {
if (highlightPaths == null && highlightPaints == null) {
return;
}
if (cursorOffsetVertical != 0) canvas.translate(0, cursorOffsetVertical);
try {
+ BlendMode blendMode = determineHighContrastHighlightBlendMode(canvas);
if (highlightPaths != null) {
if (highlightPaints == null) {
throw new IllegalArgumentException(
@@ -556,7 +608,12 @@ public abstract class Layout {
}
for (int i = 0; i < highlightPaths.size(); ++i) {
final Path highlight = highlightPaths.get(i);
- final Paint highlightPaint = highlightPaints.get(i);
+ Paint highlightPaint = highlightPaints.get(i);
+ if (shouldDrawHighlightsOnTop(canvas)) {
+ highlightPaint = setToHighlightPaint(highlightPaint, blendMode,
+ mWorkPlainPaint);
+ }
+
if (highlight != null) {
canvas.drawPath(highlight, highlightPaint);
}
@@ -564,6 +621,10 @@ public abstract class Layout {
}
if (selectionPath != null) {
+ if (shouldDrawHighlightsOnTop(canvas)) {
+ selectionPaint = setToHighlightPaint(selectionPaint, blendMode,
+ mWorkPlainPaint);
+ }
canvas.drawPath(selectionPath, selectionPaint);
}
} finally {
@@ -571,6 +632,31 @@ public abstract class Layout {
}
}
+ @Nullable
+ private BlendMode determineHighContrastHighlightBlendMode(Canvas canvas) {
+ if (!shouldDrawHighlightsOnTop(canvas)) {
+ return null;
+ }
+
+ return isHighContrastTextDark() ? BlendMode.MULTIPLY : BlendMode.DIFFERENCE;
+ }
+
+ private boolean isHighContrastTextDark() {
+ // High-contrast text mode
+ // Determine if the text is black-on-white or white-on-black, so we know what blendmode will
+ // give the highest contrast and most realistic text color.
+ // This equation should match the one in libs/hwui/hwui/DrawTextFunctor.h
+ if (highContrastTextLuminance()) {
+ var lab = new double[3];
+ ColorUtils.colorToLAB(mPaint.getColor(), lab);
+ return lab[0] < 0.5;
+ } else {
+ var color = mPaint.getColor();
+ int channelSum = Color.red(color) + Color.green(color) + Color.blue(color);
+ return channelSum < (128 * 3);
+ }
+ }
+
private boolean isJustificationRequired(int lineNum) {
if (mJustificationMode == JUSTIFICATION_MODE_NONE) return false;
final int lineEnd = getLineEnd(lineNum);
@@ -3393,7 +3479,8 @@ public abstract class Layout {
private CharSequence mText;
@UnsupportedAppUsage
private TextPaint mPaint;
- private TextPaint mWorkPaint = new TextPaint();
+ private final TextPaint mWorkPaint = new TextPaint();
+ private final Paint mWorkPlainPaint = new Paint();
private int mWidth;
private Alignment mAlignment = Alignment.ALIGN_NORMAL;
private float mSpacingMult;
@@ -3414,6 +3501,7 @@ public abstract class Layout {
private int mJustificationMode;
private LineBreakConfig mLineBreakConfig;
private boolean mUseBoundsForWidth;
+ private boolean mShiftDrawingOffsetForStartOverhang;
private @Nullable Paint.FontMetrics mMinimumFontMetrics;
private TextLine.LineInfo mLineInfo = null;
@@ -3873,6 +3961,35 @@ public abstract class Layout {
}
/**
+ * Set true for shifting the drawing x offset for showing overhang at the start position.
+ *
+ * This flag is ignored if the {@link #getUseBoundsForWidth()} is false.
+ *
+ * If this value is false, the Layout draws text from the zero even if there is a glyph
+ * stroke in a region where the x coordinate is negative.
+ *
+ * If this value is true, the Layout draws text with shifting the x coordinate of the
+ * drawing bounding box.
+ *
+ * This value is false by default.
+ *
+ * @param shiftDrawingOffsetForStartOverhang true for shifting the drawing offset for
+ * showing the stroke that is in the region where
+ * the x coordinate is negative.
+ * @see #setUseBoundsForWidth(boolean)
+ * @see #getUseBoundsForWidth()
+ */
+ @NonNull
+ // The corresponding getter is getShiftDrawingOffsetForStartOverhang()
+ @SuppressLint("MissingGetterMatchingBuilder")
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
+ public Builder setShiftDrawingOffsetForStartOverhang(
+ boolean shiftDrawingOffsetForStartOverhang) {
+ mShiftDrawingOffsetForStartOverhang = shiftDrawingOffsetForStartOverhang;
+ return this;
+ }
+
+ /**
* Set the minimum font metrics used for line spacing.
*
* <p>
@@ -3948,6 +4065,7 @@ public abstract class Layout {
.setJustificationMode(mJustificationMode)
.setLineBreakConfig(mLineBreakConfig)
.setUseBoundsForWidth(mUseBoundsForWidth)
+ .setShiftDrawingOffsetForStartOverhang(mShiftDrawingOffsetForStartOverhang)
.build();
} else {
return new BoringLayout(
@@ -3955,7 +4073,7 @@ public abstract class Layout {
mIncludePad, mFallbackLineSpacing, mEllipsizedWidth, mEllipsize, mMaxLines,
mBreakStrategy, mHyphenationFrequency, mLeftIndents, mRightIndents,
mJustificationMode, mLineBreakConfig, metrics, mUseBoundsForWidth,
- mMinimumFontMetrics);
+ mShiftDrawingOffsetForStartOverhang, mMinimumFontMetrics);
}
}
@@ -3980,6 +4098,7 @@ public abstract class Layout {
private int mJustificationMode = JUSTIFICATION_MODE_NONE;
private LineBreakConfig mLineBreakConfig = LineBreakConfig.NONE;
private boolean mUseBoundsForWidth;
+ private boolean mShiftDrawingOffsetForStartOverhang;
private Paint.FontMetrics mMinimumFontMetrics;
}
@@ -4294,6 +4413,20 @@ public abstract class Layout {
}
/**
+ * Returns true if shifting drawing offset for start overhang.
+ *
+ * @return True if shifting drawing offset for start overhang.
+ * @see android.widget.TextView#setShiftDrawingOffsetForStartOverhang(boolean)
+ * @see TextView#getShiftDrawingOffsetForStartOverhang()
+ * @see StaticLayout.Builder#setShiftDrawingOffsetForStartOverhang(boolean)
+ * @see DynamicLayout.Builder#setShiftDrawingOffsetForStartOverhang(boolean)
+ */
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
+ public boolean getShiftDrawingOffsetForStartOverhang() {
+ return mShiftDrawingOffsetForStartOverhang;
+ }
+
+ /**
* Get the minimum font metrics used for line spacing.
*
* @see android.widget.TextView#setMinimumFontMetrics(Paint.FontMetrics)
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index 95d197433397..2b6684ea8f1d 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -237,10 +237,13 @@ public class MeasuredParagraph {
// Easy case: If the line instance only contains single directionality run, no need
// to reorder visually.
if (bidi.getRunCount() == 1) {
- if ((bidi.getParaLevel() & 0x01) == 1) {
+ if (bidi.getRunLevel(0) == 1) {
return Layout.DIRS_ALL_RIGHT_TO_LEFT;
- } else {
+ } else if (bidi.getRunLevel(0) == 0) {
return Layout.DIRS_ALL_LEFT_TO_RIGHT;
+ } else {
+ return new Directions(new int[] {
+ 0, bidi.getRunLevel(0) << Layout.RUN_LEVEL_SHIFT | (end - start)});
}
}
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 5986238d3035..3dd3a9ea8baf 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -24,6 +24,7 @@ import android.annotation.FloatRange;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Paint;
import android.graphics.RectF;
@@ -454,6 +455,35 @@ public class StaticLayout extends Layout {
}
/**
+ * Set true for shifting the drawing x offset for showing overhang at the start position.
+ *
+ * This flag is ignored if the {@link #getUseBoundsForWidth()} is false.
+ *
+ * If this value is false, the Layout draws text from the zero even if there is a glyph
+ * stroke in a region where the x coordinate is negative.
+ *
+ * If this value is true, the Layout draws text with shifting the x coordinate of the
+ * drawing bounding box.
+ *
+ * This value is false by default.
+ *
+ * @param shiftDrawingOffsetForStartOverhang true for shifting the drawing offset for
+ * showing the stroke that is in the region where
+ * the x coordinate is negative.
+ * @see #setUseBoundsForWidth(boolean)
+ * @see #getUseBoundsForWidth()
+ */
+ @NonNull
+ // The corresponding getter is getShiftDrawingOffsetForStartOverhang()
+ @SuppressLint("MissingGetterMatchingBuilder")
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
+ public Builder setShiftDrawingOffsetForStartOverhang(
+ boolean shiftDrawingOffsetForStartOverhang) {
+ mShiftDrawingOffsetForStartOverhang = shiftDrawingOffsetForStartOverhang;
+ return this;
+ }
+
+ /**
* Internal API that tells underlying line breaker that calculating bounding boxes even if
* the line break is performed with advances. This is useful for DynamicLayout internal
* implementation because it uses bounding box as well as advances.
@@ -566,6 +596,7 @@ public class StaticLayout extends Layout {
private boolean mAddLastLineLineSpacing;
private LineBreakConfig mLineBreakConfig = LineBreakConfig.NONE;
private boolean mUseBoundsForWidth;
+ private boolean mShiftDrawingOffsetForStartOverhang;
private boolean mCalculateBounds;
@Nullable private Paint.FontMetrics mMinimumFontMetrics;
@@ -599,6 +630,7 @@ public class StaticLayout extends Layout {
JUSTIFICATION_MODE_NONE,
null, // lineBreakConfig,
false, // useBoundsForWidth
+ false, // shiftDrawingOffsetForStartOverhang
null // minimumFontMetrics
);
@@ -677,7 +709,7 @@ public class StaticLayout extends Layout {
b.mIncludePad, b.mFallbackLineSpacing, b.mEllipsizedWidth, b.mEllipsize,
b.mMaxLines, b.mBreakStrategy, b.mHyphenationFrequency, b.mLeftIndents,
b.mRightIndents, b.mJustificationMode, b.mLineBreakConfig, b.mUseBoundsForWidth,
- b.mMinimumFontMetrics);
+ b.mShiftDrawingOffsetForStartOverhang, b.mMinimumFontMetrics);
mColumns = columnSize;
if (b.mEllipsize != null) {
diff --git a/core/java/android/tracing/flags.aconfig b/core/java/android/tracing/flags.aconfig
index c6e8844bc47a..b1bca96c5c09 100644
--- a/core/java/android/tracing/flags.aconfig
+++ b/core/java/android/tracing/flags.aconfig
@@ -12,4 +12,5 @@ flag {
namespace: "windowing_tools"
description: "Migrate protolog to Perfetto"
bug: "276432490"
+ is_fixed_read_only: true
}
diff --git a/core/java/android/tracing/perfetto/DataSource.java b/core/java/android/tracing/perfetto/DataSource.java
index 4e08aeef88e6..d0c719b86ac9 100644
--- a/core/java/android/tracing/perfetto/DataSource.java
+++ b/core/java/android/tracing/perfetto/DataSource.java
@@ -18,6 +18,8 @@ package android.tracing.perfetto;
import android.util.proto.ProtoInputStream;
+import com.android.internal.annotations.VisibleForTesting;
+
/**
* Templated base class meant to be derived by embedders to create a custom data
* source.
@@ -87,7 +89,8 @@ public abstract class DataSource<DataSourceInstanceType extends DataSourceInstan
*
* NOTE: Should only be called from native side.
*/
- protected TlsStateType createTlsState(CreateTlsStateArgs<DataSourceInstanceType> args) {
+ @VisibleForTesting
+ public TlsStateType createTlsState(CreateTlsStateArgs<DataSourceInstanceType> args) {
return null;
}
diff --git a/core/java/android/tracing/perfetto/DataSourceInstance.java b/core/java/android/tracing/perfetto/DataSourceInstance.java
index 3710b4df33e8..904cf55e014a 100644
--- a/core/java/android/tracing/perfetto/DataSourceInstance.java
+++ b/core/java/android/tracing/perfetto/DataSourceInstance.java
@@ -16,6 +16,8 @@
package android.tracing.perfetto;
+import com.android.internal.annotations.VisibleForTesting;
+
/**
* @hide
*/
@@ -66,7 +68,8 @@ public abstract class DataSourceInstance implements AutoCloseable {
* Only required to be called when instance was retrieved with
* `DataSource#getDataSourceInstanceLocked`.
*/
- public final void release() {
+ @VisibleForTesting
+ public void release() {
mDataSource.releaseDataSourceInstance(mInstanceIndex);
}
diff --git a/core/java/android/util/EventLog.java b/core/java/android/util/EventLog.java
index d2c5975ea356..0a73fd1689c3 100644
--- a/core/java/android/util/EventLog.java
+++ b/core/java/android/util/EventLog.java
@@ -50,7 +50,7 @@ import java.util.regex.Pattern;
*/
@android.ravenwood.annotation.RavenwoodKeepWholeClass
@android.ravenwood.annotation.RavenwoodNativeSubstitutionClass(
- "com.android.hoststubgen.nativesubstitution.EventLog_host")
+ "com.android.platform.test.ravenwood.nativesubstitution.EventLog_host")
public class EventLog {
/** @hide */ public EventLog() {}
diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java
index 31576c5c74fc..b33214dffb7e 100644
--- a/core/java/android/util/Log.java
+++ b/core/java/android/util/Log.java
@@ -73,7 +73,7 @@ import java.net.UnknownHostException;
*/
@android.ravenwood.annotation.RavenwoodKeepWholeClass
@android.ravenwood.annotation.RavenwoodNativeSubstitutionClass(
- "com.android.hoststubgen.nativesubstitution.Log_host")
+ "com.android.platform.test.ravenwood.nativesubstitution.Log_host")
public final class Log {
/** @hide */
@IntDef({ASSERT, ERROR, WARN, INFO, DEBUG, VERBOSE})
diff --git a/core/java/android/util/MemoryIntArray.java b/core/java/android/util/MemoryIntArray.java
index 5cbbbef2cf88..2226881c82cf 100644
--- a/core/java/android/util/MemoryIntArray.java
+++ b/core/java/android/util/MemoryIntArray.java
@@ -58,6 +58,7 @@ public final class MemoryIntArray implements Parcelable, Closeable {
private final boolean mIsOwner;
private final long mMemoryAddr;
+ private final int mSize;
private int mFd = -1;
/**
@@ -75,6 +76,9 @@ public final class MemoryIntArray implements Parcelable, Closeable {
final String name = UUID.randomUUID().toString();
mFd = nativeCreate(name, size);
mMemoryAddr = nativeOpen(mFd, mIsOwner);
+ // Note that we use the effective size after allocation, rather than the provided size,
+ // preserving compat with the original behavior. In practice these should be equivalent.
+ mSize = nativeSize(mFd);
mCloseGuard.open("MemoryIntArray.close");
}
@@ -86,6 +90,7 @@ public final class MemoryIntArray implements Parcelable, Closeable {
}
mFd = pfd.detachFd();
mMemoryAddr = nativeOpen(mFd, mIsOwner);
+ mSize = nativeSize(mFd);
mCloseGuard.open("MemoryIntArray.close");
}
@@ -127,13 +132,11 @@ public final class MemoryIntArray implements Parcelable, Closeable {
}
/**
- * Gets the array size.
- *
- * @throws IOException If an error occurs while accessing the shared memory.
+ * @return Gets the array size.
*/
- public int size() throws IOException {
+ public int size() {
enforceNotClosed();
- return nativeSize(mFd);
+ return mSize;
}
/**
@@ -210,11 +213,10 @@ public final class MemoryIntArray implements Parcelable, Closeable {
}
}
- private void enforceValidIndex(int index) throws IOException {
- final int size = size();
- if (index < 0 || index > size - 1) {
+ private void enforceValidIndex(int index) {
+ if (index < 0 || index > mSize - 1) {
throw new IndexOutOfBoundsException(
- index + " not between 0 and " + (size - 1));
+ index + " not between 0 and " + (mSize - 1));
}
}
diff --git a/core/java/android/util/TimingsTraceLog.java b/core/java/android/util/TimingsTraceLog.java
index 48a5ceae1aef..b4f4729c82b2 100644
--- a/core/java/android/util/TimingsTraceLog.java
+++ b/core/java/android/util/TimingsTraceLog.java
@@ -34,6 +34,7 @@ import java.util.List;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class TimingsTraceLog {
// Debug boot time for every step if it's non-user build.
private static final boolean DEBUG_BOOT_TIME = !Build.IS_USER;
diff --git a/core/java/android/view/AttachedSurfaceControl.java b/core/java/android/view/AttachedSurfaceControl.java
index ffe0c716905d..5406cf557410 100644
--- a/core/java/android/view/AttachedSurfaceControl.java
+++ b/core/java/android/view/AttachedSurfaceControl.java
@@ -19,10 +19,11 @@ import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UiThread;
+import android.content.Context;
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.HardwareBuffer;
-import android.os.IBinder;
+import android.os.Looper;
import android.window.InputTransferToken;
import android.window.SurfaceSyncGroup;
@@ -180,66 +181,25 @@ public interface AttachedSurfaceControl {
}
/**
- * Gets the token used for associating this {@link AttachedSurfaceControl} with
- * {@link SurfaceControlViewHost} instances.
- *
- * <p>This token should be passed to {@link SurfaceControlViewHost}'s constructor.
- * This token will be {@code null} if the window does not have an input channel.
- *
- * @return The SurfaceControlViewHost link token.
- */
- @Nullable
- @FlaggedApi(Flags.FLAG_GET_HOST_TOKEN_API)
- default IBinder getHostToken() {
- throw new UnsupportedOperationException("The getHostToken needs to be "
- + "implemented before making this call.");
- }
-
- /**
* Gets the token used for associating this {@link AttachedSurfaceControl} with an embedded
* {@link SurfaceControlViewHost} or {@link SurfaceControl}
*
- * @return The SurfaceControlViewHost link token. This can return {@code null} if the
- * {@link AttachedSurfaceControl} was created with no registered input
- * @hide
+ * <p>This token should be passed to
+ * {@link SurfaceControlViewHost#SurfaceControlViewHost(Context, Display, InputTransferToken)}
+ * or
+ * {@link WindowManager#registerBatchedSurfaceControlInputReceiver(int, InputTransferToken,
+ * SurfaceControl, Choreographer, SurfaceControlInputReceiver)} or
+ * {@link WindowManager#registerUnbatchedSurfaceControlInputReceiver(int, InputTransferToken,
+ * SurfaceControl, Looper, SurfaceControlInputReceiver)}
+ *
+ * @return The {@link InputTransferToken} for the {@link AttachedSurfaceControl}
+ * @throws IllegalStateException if the {@link AttachedSurfaceControl} was created with no
+ * registered input
*/
- @Nullable
+ @NonNull
+ @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER)
default InputTransferToken getInputTransferToken() {
- throw new UnsupportedOperationException("The getHostToken needs to be "
+ throw new UnsupportedOperationException("The getInputTransferToken needs to be "
+ "implemented before making this call.");
}
-
- /**
- * Transfer the currently in progress touch gesture from the host to the requested
- * {@link SurfaceControlViewHost.SurfacePackage}. This requires that the
- * SurfaceControlViewHost was created with the current host's inputToken.
- * <p>
- * When the touch is transferred, the window currently receiving touch gets an ACTION_CANCEL
- * and does not receive any further input events for this gesture.
- * <p>
- * The transferred-to window receives an ACTION_DOWN event and then the remainder of the
- * input events for this gesture. It does not receive any of the previous events of this gesture
- * that the originating window received.
- * <p>
- * The "transferTouch" API only works for the current gesture. When a new gesture arrives,
- * input dispatcher will do a new round of hit testing. So, if the "host" window is still the
- * first thing that's being touched, then it will receive the new gesture again. It will
- * again be up to the host to transfer this new gesture to the embedded.
- * <p>
- * Once the transferred-to window receives the gesture, it can choose to give up this gesture
- * and send it to another window that it's linked to (it can't be an arbitrary window for
- * security reasons) using the same transferTouch API. Only the window currently receiving
- * touch is allowed to transfer the gesture.
- *
- * @param surfacePackage The SurfacePackage to transfer the gesture to.
- * @return Whether the touch stream was transferred.
- * @see SurfaceControlViewHost#transferTouchGestureToHost() for the reverse to transfer touch
- * gesture from the embedded to the host.
- */
- @FlaggedApi(Flags.FLAG_TRANSFER_GESTURE_TO_EMBEDDED)
- default boolean transferHostTouchGestureToEmbedded(
- @NonNull SurfaceControlViewHost.SurfacePackage surfacePackage) {
- throw new UnsupportedOperationException(
- "transferHostTouchGestureToEmbedded is unimplemented");
- }
}
diff --git a/core/java/android/view/BatchedInputEventReceiver.java b/core/java/android/view/BatchedInputEventReceiver.java
index e679f2998ca1..ca2e56d383e5 100644
--- a/core/java/android/view/BatchedInputEventReceiver.java
+++ b/core/java/android/view/BatchedInputEventReceiver.java
@@ -19,6 +19,7 @@ package android.view;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Handler;
import android.os.Looper;
+import android.os.Trace;
/**
* Similar to {@link InputEventReceiver}, but batches events to vsync boundaries when possible.
@@ -42,6 +43,8 @@ public class BatchedInputEventReceiver extends InputEventReceiver {
super(inputChannel, looper);
mChoreographer = choreographer;
mBatchingEnabled = true;
+ traceBoolVariable("mBatchingEnabled", mBatchingEnabled);
+ traceBoolVariable("mBatchedInputScheduled", mBatchedInputScheduled);
mHandler = new Handler(looper);
}
@@ -71,6 +74,7 @@ public class BatchedInputEventReceiver extends InputEventReceiver {
}
mBatchingEnabled = batchingEnabled;
+ traceBoolVariable("mBatchingEnabled", mBatchingEnabled);
mHandler.removeCallbacks(mConsumeBatchedInputEvents);
if (!batchingEnabled) {
unscheduleBatchedInput();
@@ -81,6 +85,7 @@ public class BatchedInputEventReceiver extends InputEventReceiver {
protected void doConsumeBatchedInput(long frameTimeNanos) {
if (mBatchedInputScheduled) {
mBatchedInputScheduled = false;
+ traceBoolVariable("mBatchedInputScheduled", mBatchedInputScheduled);
if (consumeBatchedInputEvents(frameTimeNanos) && frameTimeNanos != -1) {
// If we consumed a batch here, we want to go ahead and schedule the
// consumption of batched input events on the next frame. Otherwise, we would
@@ -95,6 +100,7 @@ public class BatchedInputEventReceiver extends InputEventReceiver {
private void scheduleBatchedInput() {
if (!mBatchedInputScheduled) {
mBatchedInputScheduled = true;
+ traceBoolVariable("mBatchedInputScheduled", mBatchedInputScheduled);
mChoreographer.postCallback(Choreographer.CALLBACK_INPUT, mBatchedInputRunnable, null);
}
}
@@ -102,11 +108,18 @@ public class BatchedInputEventReceiver extends InputEventReceiver {
private void unscheduleBatchedInput() {
if (mBatchedInputScheduled) {
mBatchedInputScheduled = false;
+ traceBoolVariable("mBatchedInputScheduled", mBatchedInputScheduled);
mChoreographer.removeCallbacks(
Choreographer.CALLBACK_INPUT, mBatchedInputRunnable, null);
}
}
+ // @TODO(b/311142655): Delete this temporary tracing. It's only used here to debug a very
+ // specific issue.
+ private void traceBoolVariable(String name, boolean value) {
+ Trace.traceCounter(Trace.TRACE_TAG_INPUT, name, value ? 1 : 0);
+ }
+
private final class BatchedInputRunnable implements Runnable {
@Override
public void run() {
diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java
index eb289204e481..00657ea35e02 100644
--- a/core/java/android/view/HandwritingInitiator.java
+++ b/core/java/android/view/HandwritingInitiator.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -23,6 +24,9 @@ import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
+import android.text.TextUtils;
+import android.view.inputmethod.ConnectionlessHandwritingCallback;
+import android.view.inputmethod.CursorAnchorInfo;
import android.view.inputmethod.Flags;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
@@ -35,6 +39,7 @@ import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
+import java.util.function.Consumer;
/**
* Initiates handwriting mode once it detects stylus movement in handwritable areas.
@@ -225,15 +230,7 @@ public class HandwritingInitiator {
}
startHandwriting(candidateView);
} else if (candidateView.getHandwritingDelegatorCallback() != null) {
- String delegatePackageName =
- candidateView.getAllowedHandwritingDelegatePackageName();
- if (delegatePackageName == null) {
- delegatePackageName = candidateView.getContext().getOpPackageName();
- }
- mImm.prepareStylusHandwritingDelegation(
- candidateView, delegatePackageName);
- candidateView.getHandwritingDelegatorCallback().run();
- mState.mHasPreparedHandwritingDelegation = true;
+ prepareDelegation(candidateView);
} else {
if (!mInitiateWithoutConnection) {
mState.mPendingConnectedView = new WeakReference<>(candidateView);
@@ -375,7 +372,7 @@ public class HandwritingInitiator {
// A new view just gain focus. By default, we should show hover icon for it.
mShowHoverIconForConnectedView = true;
}
- if (!fromTouchEvent) {
+ if (!fromTouchEvent && view.isHandwritingDelegate()) {
tryAcceptStylusHandwritingDelegation(view);
}
return true;
@@ -393,36 +390,82 @@ public class HandwritingInitiator {
}
}
+ private void prepareDelegation(View view) {
+ String delegatePackageName = view.getAllowedHandwritingDelegatePackageName();
+ if (delegatePackageName == null) {
+ delegatePackageName = view.getContext().getOpPackageName();
+ }
+ if (mImm.isConnectionlessStylusHandwritingAvailable()) {
+ // No other view should have focus during the connectionless handwriting session, as
+ // this could cause user confusion about the input target for the session.
+ view.getViewRootImpl().getView().clearFocus();
+ mImm.startConnectionlessStylusHandwritingForDelegation(
+ view, getCursorAnchorInfoForConnectionless(view), delegatePackageName,
+ view::post, new DelegationCallback(view, delegatePackageName));
+ mState.mHasInitiatedHandwriting = true;
+ mState.mShouldInitHandwriting = false;
+ } else {
+ mImm.prepareStylusHandwritingDelegation(view, delegatePackageName);
+ view.getHandwritingDelegatorCallback().run();
+ mState.mHasPreparedHandwritingDelegation = true;
+ }
+ }
+
/**
* Starts a stylus handwriting session for the delegate view, if {@link
* InputMethodManager#prepareStylusHandwritingDelegation} was previously called.
*/
@VisibleForTesting
public boolean tryAcceptStylusHandwritingDelegation(@NonNull View view) {
- if (!view.isHandwritingDelegate() || (mState != null && mState.mHasInitiatedHandwriting)) {
- return false;
+ if (Flags.useZeroJankProxy()) {
+ tryAcceptStylusHandwritingDelegationAsync(view);
+ } else {
+ return tryAcceptStylusHandwritingDelegationInternal(view);
}
+ return false;
+ }
+
+ private boolean tryAcceptStylusHandwritingDelegationInternal(@NonNull View view) {
String delegatorPackageName =
view.getAllowedHandwritingDelegatorPackageName();
if (delegatorPackageName == null) {
delegatorPackageName = view.getContext().getOpPackageName();
}
if (mImm.acceptStylusHandwritingDelegation(view, delegatorPackageName)) {
- if (mState != null) {
- mState.mHasInitiatedHandwriting = true;
- mState.mShouldInitHandwriting = false;
- }
- if (view instanceof TextView) {
- ((TextView) view).hideHint();
- }
- // A handwriting delegate view is accepted and handwriting starts; hide the
- // hover icon.
- mShowHoverIconForConnectedView = false;
+ onDelegationAccepted(view);
return true;
}
return false;
}
+ @FlaggedApi(Flags.FLAG_USE_ZERO_JANK_PROXY)
+ private void tryAcceptStylusHandwritingDelegationAsync(@NonNull View view) {
+ String delegatorPackageName =
+ view.getAllowedHandwritingDelegatorPackageName();
+ if (delegatorPackageName == null) {
+ delegatorPackageName = view.getContext().getOpPackageName();
+ }
+ Consumer<Boolean> consumer = delegationAccepted -> {
+ if (delegationAccepted) {
+ onDelegationAccepted(view);
+ }
+ };
+ mImm.acceptStylusHandwritingDelegation(view, delegatorPackageName, view::post, consumer);
+ }
+
+ private void onDelegationAccepted(View view) {
+ if (mState != null) {
+ mState.mHasInitiatedHandwriting = true;
+ mState.mShouldInitHandwriting = false;
+ }
+ if (view instanceof TextView) {
+ ((TextView) view).hideHint();
+ }
+ // A handwriting delegate view is accepted and handwriting starts; hide the
+ // hover icon.
+ mShowHoverIconForConnectedView = false;
+ }
+
/**
* Notify that the handwriting area for the given view might be updated.
* @param view the view whose handwriting area might be updated.
@@ -807,6 +850,59 @@ public class HandwritingInitiator {
&& view.shouldInitiateHandwriting();
}
+ private CursorAnchorInfo getCursorAnchorInfoForConnectionless(View view) {
+ CursorAnchorInfo.Builder builder = new CursorAnchorInfo.Builder();
+ // Fake editor views will usually display hint text. The hint text view can be used to
+ // populate the CursorAnchorInfo.
+ TextView textView = findFirstTextViewDescendent(view);
+ if (textView != null) {
+ textView.getCursorAnchorInfo(0, builder, mTempMatrix);
+ if (textView.getSelectionStart() < 0) {
+ // Insertion marker location is not populated if selection start is negative, so
+ // make a best guess.
+ float bottom = textView.getHeight() - textView.getExtendedPaddingBottom();
+ builder.setInsertionMarkerLocation(
+ /* horizontalPosition= */ textView.getCompoundPaddingStart(),
+ /* lineTop= */ textView.getExtendedPaddingTop(),
+ /* lineBaseline= */ bottom,
+ /* lineBottom= */ bottom,
+ /* flags= */ 0);
+ }
+ } else {
+ // If there is no TextView descendent, just populate the insertion marker with the start
+ // edge of the view.
+ mTempMatrix.reset();
+ view.transformMatrixToGlobal(mTempMatrix);
+ builder.setMatrix(mTempMatrix);
+ builder.setInsertionMarkerLocation(
+ /* horizontalPosition= */ view.isLayoutRtl() ? view.getWidth() : 0,
+ /* lineTop= */ 0,
+ /* lineBaseline= */ view.getHeight(),
+ /* lineBottom= */ view.getHeight(),
+ /* flags= */ 0);
+ }
+ return builder.build();
+ }
+
+ @Nullable
+ private static TextView findFirstTextViewDescendent(View view) {
+ if (view instanceof ViewGroup viewGroup) {
+ TextView textView;
+ for (int i = 0; i < viewGroup.getChildCount(); ++i) {
+ View child = viewGroup.getChildAt(i);
+ textView = (child instanceof TextView tv)
+ ? tv : findFirstTextViewDescendent(viewGroup.getChildAt(i));
+ if (textView != null
+ && textView.isAggregatedVisible()
+ && (!TextUtils.isEmpty(textView.getText())
+ || !TextUtils.isEmpty(textView.getHint()))) {
+ return textView;
+ }
+ }
+ }
+ return null;
+ }
+
/**
* A class used to track the handwriting areas set by the Views.
*
@@ -931,4 +1027,35 @@ public class HandwritingInitiator {
return true;
}
}
+
+ private class DelegationCallback implements ConnectionlessHandwritingCallback {
+ private final View mView;
+ private final String mDelegatePackageName;
+
+ private DelegationCallback(View view, String delegatePackageName) {
+ mView = view;
+ mDelegatePackageName = delegatePackageName;
+ }
+
+ @Override
+ public void onResult(@NonNull CharSequence text) {
+ mView.getHandwritingDelegatorCallback().run();
+ }
+
+ @Override
+ public void onError(int errorCode) {
+ switch (errorCode) {
+ case CONNECTIONLESS_HANDWRITING_ERROR_NO_TEXT_RECOGNIZED:
+ mView.getHandwritingDelegatorCallback().run();
+ break;
+ case CONNECTIONLESS_HANDWRITING_ERROR_UNSUPPORTED:
+ // Fall back to the old delegation flow
+ mImm.prepareStylusHandwritingDelegation(mView, mDelegatePackageName);
+ mView.getHandwritingDelegatorCallback().run();
+ mState.mHasInitiatedHandwriting = false;
+ mState.mHasPreparedHandwritingDelegation = true;
+ break;
+ }
+ }
+ }
}
diff --git a/core/java/android/view/ISensitiveContentProtectionManager.aidl b/core/java/android/view/ISensitiveContentProtectionManager.aidl
new file mode 100644
index 000000000000..c135ae4b6a95
--- /dev/null
+++ b/core/java/android/view/ISensitiveContentProtectionManager.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 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.view;
+
+import android.os.IBinder;
+
+/**
+ * @hide
+ */
+oneway interface ISensitiveContentProtectionManager {
+ /**
+ * Block projection for a package's window when the window is showing sensitive content on
+ * the screen, the projection is unblocked when the window no more shows sensitive content.
+ *
+ * @param windowToken window where the content is shown.
+ * @param packageName package name.
+ * @param isShowingSensitiveContent whether the window is showing sensitive content.
+ */
+ void setSensitiveContentProtection(in IBinder windowToken, in String packageName,
+ in boolean isShowingSensitiveContent);
+}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 29cc8594deec..51d7caacd4b7 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -69,11 +69,11 @@ import android.view.SurfaceControl;
import android.view.displayhash.DisplayHash;
import android.view.displayhash.VerifiedDisplayHash;
import android.window.AddToSurfaceSyncGroupResult;
+import android.window.IGlobalDragListener;
import android.window.IScreenRecordingCallback;
import android.window.ISurfaceSyncGroupCompletedListener;
import android.window.ITaskFpsCallback;
import android.window.ITrustedPresentationListener;
-import android.window.IUnhandledDragListener;
import android.window.InputTransferToken;
import android.window.ScreenCapture;
import android.window.TrustedPresentationThresholds;
@@ -1094,8 +1094,10 @@ interface IWindowManager
void unregisterScreenRecordingCallback(IScreenRecordingCallback callback);
/**
- * Sets the listener to be called back when a cross-window drag and drop operation is unhandled
- * (ie. not handled by any window which can handle the drag).
+ * Sets the listener to be called back when a cross-window drag and drop operation happens.
*/
- void setUnhandledDragListener(IUnhandledDragListener listener);
+ void setGlobalDragListener(IGlobalDragListener listener);
+
+ boolean transferTouchGesture(in InputTransferToken transferFromToken,
+ in InputTransferToken transferToToken);
}
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 55e49f8f3ad9..d68a47c54d4b 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -370,11 +370,6 @@ interface IWindowSession {
*/
boolean cancelDraw(IWindow window);
- boolean transferEmbeddedTouchFocusToHost(IWindow embeddedWindow);
-
- boolean transferHostTouchGestureToEmbedded(IWindow hostWindow,
- in InputTransferToken transferTouchToken);
-
/**
* Moves the focus to the adjacent window if there is one in the given direction. This can only
* move the focus to the window in the same leaf task.
diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java
index 9c430cd4acb4..2cc05b0bc4b0 100644
--- a/core/java/android/view/InputEventReceiver.java
+++ b/core/java/android/view/InputEventReceiver.java
@@ -271,12 +271,29 @@ public abstract class InputEventReceiver {
return mInputChannel.getToken();
}
+ private String getShortDescription(InputEvent event) {
+ if (event instanceof MotionEvent motion) {
+ return "MotionEvent " + MotionEvent.actionToString(motion.getAction()) + " deviceId="
+ + motion.getDeviceId() + " source=0x"
+ + Integer.toHexString(motion.getSource()) + " historySize="
+ + motion.getHistorySize();
+ } else if (event instanceof KeyEvent key) {
+ return "KeyEvent " + KeyEvent.actionToString(key.getAction())
+ + " deviceId=" + key.getDeviceId();
+ } else {
+ Log.wtf(TAG, "Illegal InputEvent type: " + event);
+ return "InputEvent";
+ }
+ }
+
// Called from native code.
@SuppressWarnings("unused")
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void dispatchInputEvent(int seq, InputEvent event) {
+ Trace.traceBegin(Trace.TRACE_TAG_INPUT, "dispatchInputEvent " + getShortDescription(event));
mSeqMap.put(event.getSequenceNumber(), seq);
onInputEvent(event);
+ Trace.traceEnd(Trace.TRACE_TAG_INPUT);
}
/**
diff --git a/core/java/android/view/KeyCharacterMap.java b/core/java/android/view/KeyCharacterMap.java
index 4fe53c2410f5..a8d4e2d2c70a 100644
--- a/core/java/android/view/KeyCharacterMap.java
+++ b/core/java/android/view/KeyCharacterMap.java
@@ -579,6 +579,17 @@ public class KeyCharacterMap implements Parcelable {
}
/**
+ * Get the combining character that corresponds with the provided accent.
+ *
+ * @param accent The accent character. eg. '`'
+ * @return The combining character
+ * @hide
+ */
+ public static int getCombiningChar(int accent) {
+ return sAccentToCombining.get(accent);
+ }
+
+ /**
* Describes the character mappings associated with a key.
*
* @deprecated instead use {@link KeyCharacterMap#getDisplayLabel(int)},
diff --git a/core/java/android/view/KeyboardShortcutGroup.java b/core/java/android/view/KeyboardShortcutGroup.java
index 763ca2621e93..c4c87ef595be 100644
--- a/core/java/android/view/KeyboardShortcutGroup.java
+++ b/core/java/android/view/KeyboardShortcutGroup.java
@@ -35,6 +35,8 @@ public final class KeyboardShortcutGroup implements Parcelable {
private final List<KeyboardShortcutInfo> mItems;
// The system group looks different UI wise.
private boolean mSystemGroup;
+ // The package name for the shortcut
+ private CharSequence mPackageName;
/**
* @param label The title to be used for this group, or null if there is none.
@@ -82,6 +84,7 @@ public final class KeyboardShortcutGroup implements Parcelable {
mLabel = source.readCharSequence();
source.readTypedList(mItems, KeyboardShortcutInfo.CREATOR);
mSystemGroup = source.readInt() == 1;
+ mPackageName = source.readCharSequence();
}
/**
@@ -105,6 +108,22 @@ public final class KeyboardShortcutGroup implements Parcelable {
}
/**
+ * @param packageName the name of the package associated with this shortcut.
+ * @hide
+ */
+ public void setPackageName(CharSequence packageName) {
+ mPackageName = packageName;
+ }
+
+ /**
+ * Return the package name of the app associated with this shortcut.
+ * @hide
+ */
+ public CharSequence getPackageName() {
+ return mPackageName;
+ }
+
+ /**
* Adds an item to the existing list.
*
* @param item The item to be added.
@@ -123,6 +142,7 @@ public final class KeyboardShortcutGroup implements Parcelable {
dest.writeCharSequence(mLabel);
dest.writeTypedList(mItems);
dest.writeInt(mSystemGroup ? 1 : 0);
+ dest.writeCharSequence(mPackageName);
}
public static final @android.annotation.NonNull Creator<KeyboardShortcutGroup> CREATOR =
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java
index 715f1befb046..13b9c4518711 100644
--- a/core/java/android/view/PointerIcon.java
+++ b/core/java/android/view/PointerIcon.java
@@ -430,6 +430,11 @@ public final class PointerIcon implements Parcelable {
VectorDrawable vectorDrawable) {
Bitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(),
vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
+ // BitmapDrawables and Bitmap have a default density of DisplayMetrics.DENSITY_DEVICE,
+ // (which is deprecated in favor of DENSITY_DEVICE_STABLE/resources.densityDpi). In
+ // rare cases when device density differs from the resource density, the bitmap will
+ // scale as the BitmapDrawable is created. Avoid by explicitly setting density here.
+ bitmap.setDensity(resources.getDisplayMetrics().densityDpi);
Canvas canvas = new Canvas(bitmap);
vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
vectorDrawable.draw(canvas);
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 3c0ac06ae13f..eff35c0c0f03 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -224,6 +224,8 @@ public final class SurfaceControl implements Parcelable {
@DataSpace.NamedDataSpace int dataSpace);
private static native void nativeSetExtendedRangeBrightness(long transactionObj,
long nativeObject, float currentBufferRatio, float desiredRatio);
+ private static native void nativeSetDesiredHdrHeadroom(long transactionObj,
+ long nativeObject, float desiredRatio);
private static native void nativeSetCachingHint(long transactionObj,
long nativeObject, int cachingHint);
@@ -4148,6 +4150,50 @@ public final class SurfaceControl implements Parcelable {
}
/**
+ * Sets the desired hdr headroom for the layer.
+ *
+ * <p>Prefer using this API over {@link #setExtendedRangeBrightness} for formats that
+ *. conform to HDR video standards like HLG or HDR10 which do not communicate a HDR/SDR
+ * ratio as part of generating the buffer.
+ *
+ * @param sc The layer whose desired hdr headroom is being specified
+ *
+ * @param desiredRatio The desired hdr/sdr ratio. This can be used to communicate the max
+ * desired brightness range. This is similar to the "max luminance"
+ * value in other HDR metadata formats, but represented as a ratio of
+ * the target SDR whitepoint to the max display brightness. The system
+ * may not be able to, or may choose not to, deliver the
+ * requested range.
+ *
+ * <p>Default value is 0.0f and indicates that the system will choose
+ * the best headroom for this surface control's content. Typically,
+ * this means that HLG/PQ encoded content will be displayed with some
+ * HDR headroom greater than 1.0.
+ *
+ * <p>When called after {@link #setExtendedRangeBrightness}, the
+ * desiredHeadroom will override the desiredRatio provided by
+ * {@link #setExtendedRangeBrightness}. Conversely, when called
+ * before {@link #setExtendedRangeBrightness}, the desiredRatio provided
+ * by {@link #setExtendedRangeBrightness} will override the
+ * desiredHeadroom.
+ *
+ * <p>Must be finite && >= 1.0f or 0.0f.
+ * @return this
+ * @see #setExtendedRangeBrightness
+ **/
+ @FlaggedApi(com.android.graphics.hwui.flags.Flags.FLAG_LIMITED_HDR)
+ public @NonNull Transaction setDesiredHdrHeadroom(@NonNull SurfaceControl sc,
+ @FloatRange(from = 0.0f) float desiredRatio) {
+ checkPreconditions(sc);
+ if (!Float.isFinite(desiredRatio) || (desiredRatio != 0 && desiredRatio < 1.0f)) {
+ throw new IllegalArgumentException(
+ "desiredRatio must be finite && >= 1.0f or 0; got " + desiredRatio);
+ }
+ nativeSetDesiredHdrHeadroom(mNativeObject, sc.mNativeObject, desiredRatio);
+ return this;
+ }
+
+ /**
* Sets the caching hint for the layer. By default, the caching hint is
* {@link CACHING_ENABLED}.
*
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index 58765b46a05a..6f757df66d42 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
@@ -33,6 +34,8 @@ import android.window.ISurfaceSyncGroup;
import android.window.InputTransferToken;
import android.window.WindowTokenClient;
+import com.android.window.flags.Flags;
+
import dalvik.system.CloseGuard;
import java.util.Objects;
@@ -290,12 +293,13 @@ public class SurfaceControlViewHost {
/**
* Gets an {@link InputTransferToken} which can be used to request focus on the embedded
* surface or to transfer touch gesture to the embedded surface.
- * @return the InputTransferToken associated with {@link SurfacePackage}
- * @see AttachedSurfaceControl#transferHostTouchGestureToEmbedded(SurfacePackage)
*
- * @hide
+ * @return the InputTransferToken associated with {@link SurfacePackage} or {@code null} if
+ * the embedded hasn't set up its view or doesn't have input.
+ * @see WindowManager#transferTouchGesture(InputTransferToken, InputTransferToken)
*/
@Nullable
+ @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER)
public InputTransferToken getInputTransferToken() {
return mInputTransferToken;
}
@@ -347,6 +351,25 @@ public class SurfaceControlViewHost {
@Nullable IBinder hostToken) {
this(context, display, hostToken == null ? null : new InputTransferToken(hostToken),
"untracked");
+
+ }
+
+ /**
+ * Construct a new SurfaceControlViewHost. The root Surface will be
+ * allocated internally and is accessible via getSurfacePackage().
+ * <p>
+ * The hostInputTransferToken parameter allows the host and embedded to be associated with
+ * each other to allow transferring touch gesture and focus. This is also used for ANR
+ * reporting. It's accessible from {@link AttachedSurfaceControl#getInputTransferToken()}.
+ *
+ * @param context The Context object for your activity or application.
+ * @param display The Display the hierarchy will be placed on.
+ * @param hostInputTransferToken The host input transfer token, as discussed above.
+ */
+ @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER)
+ public SurfaceControlViewHost(@NonNull Context context, @NonNull Display display,
+ @Nullable InputTransferToken hostInputTransferToken) {
+ this(context, display, hostInputTransferToken, "untracked");
}
/**
@@ -555,9 +578,9 @@ public class SurfaceControlViewHost {
}
/**
- * Transfer the currently in progress touch gesture to the parent
- * (if any) of this SurfaceControlViewHost. This requires that the
- * SurfaceControlViewHost was created with an associated hostInputToken.
+ * Transfer the currently in progress touch gesture to the parent (if any) of this
+ * SurfaceControlViewHost. This requires that the SurfaceControlViewHost was created with an
+ * associated host {@link InputTransferToken}.
*
* @return Whether the touch stream was transferred.
*/
@@ -565,13 +588,14 @@ public class SurfaceControlViewHost {
if (mViewRoot == null) {
return false;
}
-
- final IWindowSession realWm = WindowManagerGlobal.getWindowSession();
- try {
- return realWm.transferEmbeddedTouchFocusToHost(mViewRoot.mWindow);
- } catch (RemoteException e) {
- e.rethrowAsRuntimeException();
+ final WindowManager wm =
+ (WindowManager) mViewRoot.mContext.getSystemService(Context.WINDOW_SERVICE);
+ InputTransferToken embeddedToken = getInputTransferToken();
+ InputTransferToken hostToken = mWm.mHostInputTransferToken;
+ if (embeddedToken == null || hostToken == null) {
+ Log.w(TAG, "Failed to transferTouchGestureToHost. Host or embedded token is null");
+ return false;
}
- return false;
+ return wm.transferTouchGesture(getInputTransferToken(), mWm.mHostInputTransferToken);
}
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 9caf7a61bfbc..d494e2811747 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -21,6 +21,8 @@ import static android.view.WindowManagerPolicyConstants.APPLICATION_MEDIA_OVERLA
import static android.view.WindowManagerPolicyConstants.APPLICATION_MEDIA_SUBLAYER;
import static android.view.WindowManagerPolicyConstants.APPLICATION_PANEL_SUBLAYER;
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -187,6 +189,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
final Rect mScreenRect = new Rect();
private final SurfaceSession mSurfaceSession = new SurfaceSession();
+ private final boolean mLimitedHdrEnabled = Flags.limitedHdr();
SurfaceControl mSurfaceControl;
SurfaceControl mBackgroundControl;
@@ -197,6 +200,9 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
@SurfaceLifecycleStrategy
private int mSurfaceLifecycleStrategy = SURFACE_LIFECYCLE_DEFAULT;
+ private float mRequestedHdrHeadroom = 0.f;
+ private float mHdrHeadroom = 0.f;
+
/**
* We use this lock to protect access to mSurfaceControl. Both are accessed on the UI
* thread and the render thread via RenderNode.PositionUpdateListener#positionLost.
@@ -821,6 +827,45 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
updateSurface();
}
+
+ /**
+ * Sets the desired amount of HDR headroom to be used when HDR content is presented on this
+ * SurfaceView.
+ *
+ * <p>By default the system will choose an amount of HDR headroom that is appropriate
+ * for the underlying device capabilities & bit-depth of the panel. However, for some types
+ * of content this can end up being more headroom than necessary or desired. An example
+ * would be a messaging app or gallery thumbnail view where some amount of HDR pop is desired
+ * without overly influencing the perceived brightness of the majority SDR content. This can
+ * also be used to animate in/out of an HDR range for smoother transitions.</p>
+ *
+ * <p>Note: The actual amount of HDR headroom that will be given is subject to a variety
+ * of factors such as ambient conditions, display capabilities, or bit-depth limitations.
+ * See {@link Display#getHdrSdrRatio()} for more information as well as how to query the
+ * current value.</p>
+ *
+ * @param desiredHeadroom The amount of HDR headroom that is desired. Must be >= 1.0 (no HDR)
+ * and <= 10,000.0. Passing 0.0 will reset to the default, automatically
+ * chosen value.
+ * @see Display#getHdrSdrRatio()
+ */
+ @FlaggedApi(com.android.graphics.hwui.flags.Flags.FLAG_LIMITED_HDR)
+ public void setDesiredHdrHeadroom(
+ @FloatRange(from = 0.0f, to = 10000.0) float desiredHeadroom) {
+ if (!Float.isFinite(desiredHeadroom)) {
+ throw new IllegalArgumentException("desiredHeadroom must be finite: "
+ + desiredHeadroom);
+ }
+ if (desiredHeadroom != 0 && (desiredHeadroom < 1.0f || desiredHeadroom > 10000.0f)) {
+ throw new IllegalArgumentException(
+ "desiredHeadroom must be 0.0 or in the range [1.0, 10000.0f], received: "
+ + desiredHeadroom);
+ }
+ mRequestedHdrHeadroom = desiredHeadroom;
+ updateSurface();
+ invalidate();
+ }
+
private void updateOpaqueFlag() {
if (!PixelFormat.formatHasAlpha(mRequestedFormat)) {
mSurfaceFlags |= SurfaceControl.OPAQUE;
@@ -941,6 +986,10 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
updateBackgroundVisibility(surfaceUpdateTransaction);
updateBackgroundColor(surfaceUpdateTransaction);
+ if (mLimitedHdrEnabled) {
+ surfaceUpdateTransaction.setDesiredHdrHeadroom(
+ mBlastSurfaceControl, mHdrHeadroom);
+ }
if (isAboveParent()) {
float alpha = getAlpha();
surfaceUpdateTransaction.setAlpha(mSurfaceControl, alpha);
@@ -1085,11 +1134,12 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
final boolean relativeZChanged = mSubLayer != mRequestedSubLayer;
final boolean surfaceLifecycleStrategyChanged =
mSurfaceLifecycleStrategy != mRequestedSurfaceLifecycleStrategy;
+ final boolean hdrHeadroomChanged = mHdrHeadroom != mRequestedHdrHeadroom;
if (creating || formatChanged || sizeChanged || visibleChanged
|| alphaChanged || windowVisibleChanged || positionChanged
|| layoutSizeChanged || hintChanged || relativeZChanged || !mAttachedToWindow
- || surfaceLifecycleStrategyChanged) {
+ || surfaceLifecycleStrategyChanged || hdrHeadroomChanged) {
if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ "Changes: creating=" + creating
@@ -1117,6 +1167,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
final int previousSurfaceLifecycleStrategy = mSurfaceLifecycleStrategy;
mSurfaceLifecycleStrategy = mRequestedSurfaceLifecycleStrategy;
+ mHdrHeadroom = mRequestedHdrHeadroom;
mScreenRect.left = mWindowSpaceLeft;
mScreenRect.top = mWindowSpaceTop;
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 021bbf7b9c9f..896b3f4bd5c3 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -196,8 +196,6 @@ public class TextureView extends View {
private Canvas mCanvas;
private int mSaveCount;
- @Surface.FrameRateCompatibility int mFrameRateCompatibility;
-
private final Object[] mNativeWindowLock = new Object[0];
// Set by native code, do not write!
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 254c4aed57c1..4df95bf31c3f 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -18,11 +18,13 @@ package android.view;
import static android.content.res.Resources.ID_NULL;
import static android.os.Trace.TRACE_TAG_APP;
+import static android.service.autofill.Flags.FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION;
import static android.view.ContentInfo.SOURCE_DRAG_AND_DROP;
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.Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
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;
@@ -34,6 +36,7 @@ import static android.view.flags.Flags.FLAG_SENSITIVE_CONTENT_APP_PROTECTION_API
import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY;
import static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API;
import static android.view.flags.Flags.enableUseMeasureCacheDuringForceLayout;
+import static android.view.flags.Flags.sensitiveContentAppProtection;
import static android.view.flags.Flags.toolkitMetricsForFrameRateDecision;
import static android.view.flags.Flags.toolkitSetFrameRateReadOnly;
import static android.view.flags.Flags.viewVelocityApi;
@@ -77,12 +80,14 @@ import android.content.ClipDescription;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
+import android.content.IntentSender;
import android.content.res.ColorStateList;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.credentials.CredentialManager;
+import android.credentials.CredentialOption;
import android.credentials.GetCredentialException;
import android.credentials.GetCredentialRequest;
import android.credentials.GetCredentialResponse;
@@ -127,9 +132,11 @@ import android.os.SystemClock;
import android.os.Trace;
import android.os.Vibrator;
import android.os.vibrator.Flags;
+import android.service.credentials.CredentialProviderService;
import android.sysprop.DisplayProperties;
import android.text.InputType;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.FloatProperty;
@@ -1045,7 +1052,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
@Nullable
private ViewCredentialHandler mViewCredentialHandler;
-
/** Used to avoid computing the full strings each time when layout tracing is enabled. */
@Nullable
private ViewTraversalTracingStrings mTracingStrings;
@@ -2365,6 +2371,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private static boolean sToolkitSetFrameRateReadOnlyFlagValue;
private static boolean sToolkitMetricsForFrameRateDecisionFlagValue;
+ // Used to set frame rate compatibility.
+ @Surface.FrameRateCompatibility int mFrameRateCompatibility =
+ FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
static {
EMPTY_STATE_SET = StateSet.get(0);
@@ -3694,6 +3703,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* 1 PFLAG4_ROTARY_HAPTICS_SCROLL_SINCE_LAST_ROTARY_INPUT
* 1 PFLAG4_ROTARY_HAPTICS_WAITING_FOR_SCROLL_EVENT
* 11 PFLAG4_CONTENT_SENSITIVITY_MASK
+ * 1 PFLAG4_IS_COUNTED_AS_SENSITIVE
* |-------|-------|-------|-------|
*/
@@ -3819,6 +3829,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private static final int PFLAG4_CONTENT_SENSITIVITY_MASK =
(CONTENT_SENSITIVITY_AUTO | CONTENT_SENSITIVITY_SENSITIVE
| CONTENT_SENSITIVITY_NOT_SENSITIVE) << PFLAG4_CONTENT_SENSITIVITY_SHIFT;
+
+ /**
+ * Whether this view has been counted as a sensitive view or not.
+ *
+ * @see AttachInfo#mSensitiveViewsCount
+ */
+ private static final int PFLAG4_IS_COUNTED_AS_SENSITIVE = 0x4000000;
/* End of masks for mPrivateFlags4 */
/** @hide */
@@ -5356,16 +5373,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* Flag indicating that an unhandled drag should be delegated to the system to be started if no
* visible window wishes to handle the drop. When using this flag, the caller must provide
- * ClipData with an Item that contains an immutable PendingIntent to an activity to be launched
+ * ClipData with an Item that contains an immutable IntentSender to an activity to be launched
* (not a broadcast, service, etc). See
- * {@link ClipData.Item.Builder#setPendingIntent(PendingIntent)}.
+ * {@link ClipData.Item.Builder#setIntentSender(IntentSender)}.
*
* The system can decide to launch the intent or not based on factors like the current screen
* size or windowing mode. If the system does not launch the intent, it will be canceled via the
* normal drag and drop flow.
*/
@FlaggedApi(FLAG_DELEGATE_UNHANDLED_DRAGS)
- public static final int DRAG_FLAG_START_PENDING_INTENT_ON_UNHANDLED_DRAG = 1 << 13;
+ public static final int DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG = 1 << 13;
/**
* Vertical scroll factor cached by {@link #getVerticalScrollFactor}.
@@ -5628,7 +5645,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
// The preferred frame rate of the view that is mainly used for
// touch boosting, view velocity handling, and TextureView.
- private float mPreferredFrameRate = Float.NaN;
+ private float mPreferredFrameRate = REQUESTED_FRAME_RATE_CATEGORY_DEFAULT;
private int mInfrequentUpdateCount = 0;
private long mLastUpdateTimeMillis = 0;
@@ -5637,7 +5654,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private int mLastFrameRateCategory = FRAME_RATE_CATEGORY_HIGH;
@FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
- public static final float REQUESTED_FRAME_RATE_CATEGORY_DEFAULT = 0;
+ public static final float REQUESTED_FRAME_RATE_CATEGORY_DEFAULT = Float.NaN;
@FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
public static final float REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE = -1;
@FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
@@ -6995,7 +7012,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* @see #setCredentialManagerRequest
*/
- @FlaggedApi("autofill_credman_dev_integration")
+ @FlaggedApi(FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION)
public void clearCredentialManagerRequest() {
if (Log.isLoggable(AUTOFILL_LOG_TAG, Log.VERBOSE)) {
Log.v(AUTOFILL_LOG_TAG, "clearCredentialManagerRequest called");
@@ -7027,12 +7044,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @param callback to be invoked when either a response or an exception needs to be
* propagated for the given view
*/
- @FlaggedApi("autofill_credman_dev_integration")
+ @FlaggedApi(FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION)
public void setCredentialManagerRequest(@NonNull GetCredentialRequest request,
@NonNull OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback) {
Preconditions.checkNotNull(request, "request must not be null");
Preconditions.checkNotNull(callback, "request must not be null");
+ for (CredentialOption option : request.getCredentialOptions()) {
+ ArrayList<AutofillId> ids = option.getCandidateQueryData()
+ .getParcelableArrayList(
+ CredentialProviderService.EXTRA_AUTOFILL_ID, AutofillId.class);
+ ids = ids != null ? ids : new ArrayList<>();
+ if (!ids.contains(getAutofillId())) {
+ ids.add(getAutofillId());
+ }
+ option.getCandidateQueryData()
+ .putParcelableArrayList(CredentialProviderService.EXTRA_AUTOFILL_ID, ids);
+ }
mViewCredentialHandler = new ViewCredentialHandler(request, callback);
}
@@ -9913,6 +9941,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * @hide
+ */
+ public void onGetCredentialResponse(GetCredentialResponse response) {
+ if (getCredentialManagerCallback() == null) {
+ Log.w(AUTOFILL_LOG_TAG, "onGetCredentialResponse called but no callback found");
+ return;
+ }
+ getCredentialManagerCallback().onResult(response);
+ }
+
+ /**
* Gets the unique, logical identifier of this view in the activity, for autofill purposes.
*
* <p>The autofill id is created on demand, unless it is explicitly set by
@@ -9944,7 +9983,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* @return The credential request associated with this View.
*/
- @FlaggedApi("autofill_credman_dev_integration")
+ @FlaggedApi(FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION)
@Nullable
public final GetCredentialRequest getCredentialManagerRequest() {
if (mViewCredentialHandler == null) {
@@ -9968,7 +10007,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @return The callback associated with this view that will be invoked on a response from
* {@link CredentialManager} .
*/
- @FlaggedApi("autofill_credman_dev_integration")
+ @FlaggedApi(FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION)
@Nullable
public final OutcomeReceiver<GetCredentialResponse,
GetCredentialException> getCredentialManagerCallback() {
@@ -10358,6 +10397,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mPrivateFlags4 &= ~PFLAG4_CONTENT_SENSITIVITY_MASK;
mPrivateFlags4 |= ((mode << PFLAG4_CONTENT_SENSITIVITY_SHIFT)
& PFLAG4_CONTENT_SENSITIVITY_MASK);
+ if (sensitiveContentAppProtection()) {
+ updateSensitiveViewsCountIfNeeded(isAggregatedVisible());
+ }
}
/**
@@ -10389,13 +10431,44 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
@FlaggedApi(FLAG_SENSITIVE_CONTENT_APP_PROTECTION_API)
public final boolean isContentSensitive() {
- if (getContentSensitivity() == CONTENT_SENSITIVITY_SENSITIVE) {
+ final int contentSensitivity = getContentSensitivity();
+ if (contentSensitivity == CONTENT_SENSITIVITY_SENSITIVE) {
return true;
+ } else if (contentSensitivity == CONTENT_SENSITIVITY_NOT_SENSITIVE) {
+ return false;
+ } else if (sensitiveContentAppProtection()) {
+ return SensitiveAutofillHintsHelper
+ .containsSensitiveAutofillHint(getAutofillHints());
}
return false;
}
/**
+ * Helper used to track sensitive views when they are added or removed from the window
+ * based on whether it's laid out and visible.
+ *
+ * <p>This method is called from many places (visibility changed, view laid out, view attached
+ * or detached to/from window, etc...)
+ */
+ private void updateSensitiveViewsCountIfNeeded(boolean appeared) {
+ if (!sensitiveContentAppProtection() || mAttachInfo == null) {
+ return;
+ }
+
+ if (appeared && isContentSensitive()) {
+ if ((mPrivateFlags4 & PFLAG4_IS_COUNTED_AS_SENSITIVE) == 0) {
+ mPrivateFlags4 |= PFLAG4_IS_COUNTED_AS_SENSITIVE;
+ mAttachInfo.increaseSensitiveViewsCount();
+ }
+ } else {
+ if ((mPrivateFlags4 & PFLAG4_IS_COUNTED_AS_SENSITIVE) != 0) {
+ mPrivateFlags4 &= ~PFLAG4_IS_COUNTED_AS_SENSITIVE;
+ mAttachInfo.decreaseSensitiveViewsCount();
+ }
+ }
+ }
+
+ /**
* Gets the mode for determining whether this view is important for content capture.
*
* <p>See {@link #setImportantForContentCapture(int)} and
@@ -10864,8 +10937,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
structure.setAutofillId(new AutofillId(getAutofillId(),
AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId())));
}
- structure.setCredentialManagerRequest(getCredentialManagerRequest(),
- getCredentialManagerCallback());
+ if (getViewCredentialHandler() != null) {
+ structure.setCredentialManagerRequest(
+ getViewCredentialHandler().getRequest(),
+ getViewCredentialHandler().getCallback());
+ }
CharSequence cname = info.getClassName();
structure.setClassName(cname != null ? cname.toString() : null);
structure.setContentDescription(info.getContentDescription());
@@ -13425,6 +13501,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
} else {
mAutofillHints = autofillHints;
}
+ if (sensitiveContentAppProtection()) {
+ if (getContentSensitivity() == CONTENT_SENSITIVITY_AUTO) {
+ updateSensitiveViewsCountIfNeeded(isAggregatedVisible());
+ }
+ }
}
/**
@@ -16649,6 +16730,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
notifyAppearedOrDisappearedForContentCaptureIfNeeded(isVisible);
+ updateSensitiveViewsCountIfNeeded(isVisible);
if (!getSystemGestureExclusionRects().isEmpty()) {
postUpdate(this::updateSystemGestureExclusionRects);
@@ -22638,6 +22720,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
notifyAppearedOrDisappearedForContentCaptureIfNeeded(false);
+ updateSensitiveViewsCountIfNeeded(false);
mAttachInfo = null;
if (mOverlay != null) {
@@ -28664,10 +28747,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (com.android.window.flags.Flags.delegateUnhandledDrags()) {
data.prepareToLeaveProcess(
(flags & (DRAG_FLAG_GLOBAL_SAME_APPLICATION | DRAG_FLAG_GLOBAL)) != 0);
- if ((flags & DRAG_FLAG_START_PENDING_INTENT_ON_UNHANDLED_DRAG) != 0) {
- if (!data.hasActivityPendingIntents()) {
+ if ((flags & DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG) != 0) {
+ if (!hasActivityPendingIntents(data)) {
// Reset the flag if there is no launchable activity intent
- flags &= ~DRAG_FLAG_START_PENDING_INTENT_ON_UNHANDLED_DRAG;
+ flags &= ~DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG;
Log.w(VIEW_LOG_TAG, "startDragAndDrop called with "
+ "DRAG_FLAG_START_INTENT_ON_UNHANDLED_DRAG but the clip data "
+ "contains non-activity PendingIntents");
@@ -28780,7 +28863,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mAttachInfo.mDragSurface.release();
}
if (mAttachInfo.mDragData != null) {
- mAttachInfo.mDragData.cleanUpPendingIntents();
+ View.cleanUpPendingIntents(mAttachInfo.mDragData);
}
mAttachInfo.mDragSurface = surface;
mAttachInfo.mDragToken = token;
@@ -28805,6 +28888,39 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
}
+ /**
+ * Checks if this clip data has a pending intent that is an activity type.
+ * @hide
+ */
+ static boolean hasActivityPendingIntents(ClipData data) {
+ final int size = data.getItemCount();
+ for (int i = 0; i < size; i++) {
+ final ClipData.Item item = data.getItemAt(i);
+ if (item.getIntentSender() != null) {
+ final PendingIntent pi = new PendingIntent(item.getIntentSender().getTarget());
+ if (pi.isActivity()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Cleans up all pending intents in the ClipData.
+ * @hide
+ */
+ static void cleanUpPendingIntents(ClipData data) {
+ final int size = data.getItemCount();
+ for (int i = 0; i < size; i++) {
+ final ClipData.Item item = data.getItemAt(i);
+ if (item.getIntentSender() != null) {
+ final PendingIntent pi = new PendingIntent(item.getIntentSender().getTarget());
+ pi.cancel();
+ }
+ }
+ }
+
void setAccessibilityDragStarted(boolean started) {
int pflags4 = mPrivateFlags4;
if (started) {
@@ -31752,6 +31868,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
ScrollCaptureInternal mScrollCaptureInternal;
/**
+ * sensitive views attached to the window
+ */
+ int mSensitiveViewsCount;
+
+ /**
* Creates a new set of attachment information with the specified
* events handler and thread.
*
@@ -31770,6 +31891,24 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mTreeObserver = new ViewTreeObserver(context);
}
+ void increaseSensitiveViewsCount() {
+ if (mSensitiveViewsCount == 0) {
+ mViewRootImpl.notifySensitiveContentAppProtection(true);
+ }
+ mSensitiveViewsCount++;
+ }
+
+ void decreaseSensitiveViewsCount() {
+ mSensitiveViewsCount--;
+ if (mSensitiveViewsCount == 0) {
+ mViewRootImpl.notifySensitiveContentAppProtection(false);
+ }
+ if (mSensitiveViewsCount < 0) {
+ Log.wtf(VIEW_LOG_TAG, "mSensitiveViewsCount is negative" + mSensitiveViewsCount);
+ mSensitiveViewsCount = 0;
+ }
+ }
+
@Nullable
ContentCaptureManager getContentCaptureManager(@NonNull Context context) {
if (mContentCaptureManager != null) {
@@ -32383,6 +32522,36 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
}
+ private static class SensitiveAutofillHintsHelper {
+ /**
+ * List of autofill hints deemed sensitive for screen protection during screen share.
+ */
+ private static final ArraySet<String> SENSITIVE_CONTENT_AUTOFILL_HINTS = new ArraySet<>();
+ static {
+ SENSITIVE_CONTENT_AUTOFILL_HINTS.add(View.AUTOFILL_HINT_USERNAME);
+ SENSITIVE_CONTENT_AUTOFILL_HINTS.add(View.AUTOFILL_HINT_PASSWORD_AUTO);
+ SENSITIVE_CONTENT_AUTOFILL_HINTS.add(View.AUTOFILL_HINT_PASSWORD);
+ }
+
+ /**
+ * Whether View's autofill hints contains a sensitive autofill hint.
+ *
+ * @see #SENSITIVE_CONTENT_AUTOFILL_HINTS
+ */
+ static boolean containsSensitiveAutofillHint(@Nullable String[] autofillHints) {
+ if (autofillHints == null) {
+ return false;
+ }
+
+ int size = autofillHints.length;
+ for (int i = 0; i < size; i++) {
+ if (SENSITIVE_CONTENT_AUTOFILL_HINTS.contains(autofillHints[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
/**
* Returns the current scroll capture hint for this view.
@@ -33456,7 +33625,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (mInfrequentUpdateCount == INFREQUENT_UPDATE_COUNTS) {
return FRAME_RATE_CATEGORY_NORMAL;
}
-
return mLastFrameRateCategory;
}
@@ -33481,7 +33649,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
frameRateCateogry = FRAME_RATE_CATEGORY_HIGH;
}
} else {
- viewRootImpl.votePreferredFrameRate(mPreferredFrameRate);
+ viewRootImpl.votePreferredFrameRate(mPreferredFrameRate,
+ mFrameRateCompatibility);
return;
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 28a73344b731..23a7017b6125 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -27,7 +27,10 @@ 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_HIGH_HINT;
+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.Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
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;
@@ -55,6 +58,7 @@ import static android.view.ViewRootImplProto.WIDTH;
import static android.view.ViewRootImplProto.WINDOW_ATTRIBUTES;
import static android.view.ViewRootImplProto.WIN_FRAME;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
+import static android.view.flags.Flags.sensitiveContentAppProtection;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
@@ -103,7 +107,6 @@ import android.accessibilityservice.AccessibilityService;
import android.animation.AnimationHandler;
import android.animation.LayoutTransition;
import android.annotation.AnyThread;
-import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
@@ -164,6 +167,7 @@ import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.Trace;
@@ -240,7 +244,6 @@ import com.android.internal.view.BaseSurfaceHolder;
import com.android.internal.view.RootViewSurfaceTaker;
import com.android.internal.view.SurfaceCallbackHelper;
import com.android.modules.expresslog.Counter;
-import com.android.window.flags.Flags;
import java.io.IOException;
import java.io.OutputStream;
@@ -923,6 +926,8 @@ public final class ViewRootImpl implements ViewParent,
private IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection;
+ private final ISensitiveContentProtectionManager mSensitiveContentProtectionService;
+
static final class SystemUiVisibilityInfo {
int globalVisibility;
int localValue;
@@ -1022,6 +1027,18 @@ public final class ViewRootImpl implements ViewParent,
// Used to check if there is a message in the message queue
// for idleness handling.
private boolean mHasIdledMessage = false;
+ // Used to allow developers to opt out Toolkit dVRR feature.
+ // This feature allows device to adjust refresh rate
+ // as needed and can be useful for power saving.
+ // Should not enable the dVRR feature if the value is false.
+ private boolean mIsFrameRatePowerSavingsBalanced = true;
+ // Used to check if there is a conflict between different frame rate voting.
+ // Take 24 and 30 as an example, 24 is not a divisor of 30.
+ // We consider there is a conflict.
+ private boolean mIsFrameRateConflicted = false;
+ // Used to set frame rate compatibility.
+ @Surface.FrameRateCompatibility int mFrameRateCompatibility =
+ FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
// time for touch boost period.
private static final int FRAME_RATE_TOUCH_BOOST_TIME = 3000;
// time for checking idle status periodically.
@@ -1033,6 +1050,15 @@ public final class ViewRootImpl implements ViewParent,
private static final int FRAME_RATE_SETTING_REEVALUATE_TIME = 100;
/*
+ * The variables below are used to update frame rate category
+ */
+ private static final int FRAME_RATE_CATEGORY_COUNT = 5;
+ private int mFrameRateCategoryHighCount = 0;
+ private int mFrameRateCategoryHighHintCount = 0;
+ private int mFrameRateCategoryNormalCount = 0;
+ private int mFrameRateCategoryLowCount = 0;
+
+ /*
* the variables below are used to determine whther a dVRR feature should be enabled
*/
@@ -1181,6 +1207,13 @@ public final class ViewRootImpl implements ViewParent,
mScrollCaptureRequestTimeout = SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS;
mOnBackInvokedDispatcher = new WindowOnBackInvokedDispatcher(context);
+ if (sensitiveContentAppProtection()) {
+ mSensitiveContentProtectionService =
+ ISensitiveContentProtectionManager.Stub.asInterface(
+ ServiceManager.getService(Context.SENSITIVE_CONTENT_PROTECTION_SERVICE));
+ } else {
+ mSensitiveContentProtectionService = null;
+ }
}
public static void addFirstDrawHandler(Runnable callback) {
@@ -2524,8 +2557,10 @@ public final class ViewRootImpl implements ViewParent,
// Set the frame rate selection strategy to FRAME_RATE_SELECTION_STRATEGY_SELF
// This strategy ensures that the frame rate specifications do not cascade down to
// the descendant layers. This is particularly important for applications like Chrome,
- // where child surfaces should adhere to default behavior instead of no preference
- if (sToolkitSetFrameRateReadOnlyFlagValue) {
+ // where child surfaces should adhere to default behavior instead of no preference.
+ // This issue only happens when ViewRootImpl calls setFrameRateCategory. This is
+ // no longer needed if the dVRR feature is disabled.
+ if (shouldEnableDvrr()) {
try {
mFrameRateTransaction.setFrameRateSelectionStrategy(sc,
sc.FRAME_RATE_SELECTION_STRATEGY_SELF).applyAsyncUnsafe();
@@ -3249,7 +3284,7 @@ public final class ViewRootImpl implements ViewParent,
destroyHardwareResources();
}
- if (sToolkitSetFrameRateReadOnlyFlagValue && viewVisibility == View.VISIBLE) {
+ if (shouldEnableDvrr() && viewVisibility == View.VISIBLE) {
// Boost frame rate when the viewVisibility becomes true.
// This is mainly for lanuchers that lanuch new windows.
boostFrameRate(FRAME_RATE_TOUCH_BOOST_TIME);
@@ -3964,7 +3999,7 @@ public final class ViewRootImpl implements ViewParent,
}
}
- if (sToolkitSetFrameRateReadOnlyFlagValue) {
+ if (shouldEnableDvrr()) {
// Boost the frame rate when the ViewRootImpl first becomes available.
boostFrameRate(FRAME_RATE_TOUCH_BOOST_TIME);
}
@@ -4086,7 +4121,15 @@ public final class ViewRootImpl implements ViewParent,
// when the values are applicable.
setPreferredFrameRate(mPreferredFrameRate);
setPreferredFrameRateCategory(mPreferredFrameRateCategory);
+ mFrameRateCategoryHighCount = mFrameRateCategoryHighCount > 0
+ ? mFrameRateCategoryHighCount - 1 : mFrameRateCategoryHighCount;
+ mFrameRateCategoryNormalCount = mFrameRateCategoryNormalCount > 0
+ ? mFrameRateCategoryNormalCount - 1 : mFrameRateCategoryNormalCount;
+ mFrameRateCategoryLowCount = mFrameRateCategoryLowCount > 0
+ ? mFrameRateCategoryLowCount - 1 : mFrameRateCategoryLowCount;
mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
+ mPreferredFrameRate = -1;
+ mIsFrameRateConflicted = false;
}
private void createSyncIfNeeded() {
@@ -4122,6 +4165,29 @@ public final class ViewRootImpl implements ViewParent,
mWmsRequestSyncGroup.add(this, null /* runnable */);
}
+ /**
+ * Helper used to notify the service to block projection when a sensitive
+ * view (the view displays sensitive content) is attached to the window.
+ * The window manager service is also notified to unblock projection when
+ * no attached view (to the window) displays sensitive content.
+ *
+ * <ol>
+ * <li>It should only notify service to block projection when first sensitive view is
+ * attached to the window.
+ * <li>It should only notify service to unblock projection when all sensitive view are
+ * removed from the window.
+ * </ol>
+ */
+ void notifySensitiveContentAppProtection(boolean showSensitiveContent) {
+ try {
+ // The window would be blocked during screen share if it shows sensitive content.
+ mSensitiveContentProtectionService.setSensitiveContentProtection(
+ getWindowToken(), mContext.getPackageName(), showSensitiveContent);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Unable to protect sensitive content during screen share", ex);
+ }
+ }
+
private void notifyContentCaptureEvents() {
if (!isContentCaptureEnabled()) {
if (DEBUG_CONTENT_CAPTURE) {
@@ -5171,7 +5237,10 @@ public final class ViewRootImpl implements ViewParent,
// Force recalculation of transparent regions
if (accessibilityFocusDirty) {
- requestLayout();
+ final Rect bounds = mAttachInfo.mTmpInvalRect;
+ if (getAccessibilityFocusedRect(bounds)) {
+ requestLayout();
+ }
}
mAttachInfo.mDrawingTime =
@@ -6482,6 +6551,7 @@ public final class ViewRootImpl implements ViewParent,
break;
case MSG_FRAME_RATE_SETTING:
mPreferredFrameRate = 0;
+ mFrameRateCompatibility = FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
setPreferredFrameRate(mPreferredFrameRate);
break;
}
@@ -8600,7 +8670,7 @@ public final class ViewRootImpl implements ViewParent,
mAttachInfo.mDragSurface = null;
}
if (mAttachInfo.mDragData != null) {
- mAttachInfo.mDragData.cleanUpPendingIntents();
+ View.cleanUpPendingIntents(mAttachInfo.mDragData);
mAttachInfo.mDragData = null;
}
}
@@ -8626,6 +8696,12 @@ public final class ViewRootImpl implements ViewParent,
if (mView != null) {
mView.requestKeyboardShortcuts(list, deviceId);
}
+ int numGroups = list.size();
+ for (int i = 0; i < numGroups; ++i) {
+ final KeyboardShortcutGroup group = list.get(i);
+ group.setPackageName(mBasePackageName);
+
+ }
data.putParcelableArrayList(WindowManager.PARCEL_KEY_SHORTCUTS_ARRAY, list);
try {
receiver.send(0, data);
@@ -11220,25 +11296,15 @@ public final class ViewRootImpl implements ViewParent,
}
/**
- * @return Returns a token used for associating the root surface
- * to {@link SurfaceControlViewHost}.
- */
- @Nullable
- @Override
- @FlaggedApi(Flags.FLAG_GET_HOST_TOKEN_API)
- public IBinder getHostToken() {
- return getInputToken();
- }
-
- /**
* {@inheritDoc}
*/
- @Nullable
+ @NonNull
@Override
public InputTransferToken getInputTransferToken() {
IBinder inputToken = getInputToken();
if (inputToken == null) {
- return null;
+ throw new IllegalStateException(
+ "Called getInputTransferToken for Window with no input channel");
}
return new InputTransferToken(inputToken);
}
@@ -12264,9 +12330,15 @@ public final class ViewRootImpl implements ViewParent,
if (!shouldSetFrameRateCategory()) {
return;
}
+ int categoryFromConflictedFrameRates = FRAME_RATE_CATEGORY_NO_PREFERENCE;
+ if (mIsFrameRateConflicted) {
+ categoryFromConflictedFrameRates = mPreferredFrameRate > 60
+ ? FRAME_RATE_CATEGORY_HIGH : FRAME_RATE_CATEGORY_NORMAL;
+ }
int frameRateCategory = mIsTouchBoosting
- ? FRAME_RATE_CATEGORY_HIGH_HINT : preferredFrameRateCategory;
+ ? FRAME_RATE_CATEGORY_HIGH_HINT
+ : Math.max(preferredFrameRateCategory, categoryFromConflictedFrameRates);
// FRAME_RATE_CATEGORY_HIGH has a higher precedence than FRAME_RATE_CATEGORY_HIGH_HINT
// For now, FRAME_RATE_CATEGORY_HIGH_HINT is used for boosting with user interaction.
@@ -12308,14 +12380,15 @@ public final class ViewRootImpl implements ViewParent,
}
try {
- if (mLastPreferredFrameRate != preferredFrameRate) {
+ if (mLastPreferredFrameRate != preferredFrameRate
+ && preferredFrameRate >= 0) {
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.traceBegin(
Trace.TRACE_TAG_VIEW, "ViewRootImpl#setFrameRate "
+ preferredFrameRate);
}
mFrameRateTransaction.setFrameRate(mSurfaceControl, preferredFrameRate,
- Surface.FRAME_RATE_COMPATIBILITY_DEFAULT).applyAsyncUnsafe();
+ mFrameRateCompatibility).applyAsyncUnsafe();
mLastPreferredFrameRate = preferredFrameRate;
}
} catch (Exception e) {
@@ -12333,12 +12406,13 @@ public final class ViewRootImpl implements ViewParent,
private boolean shouldSetFrameRateCategory() {
// use toolkitSetFrameRate flag to gate the change
- return mSurface.isValid() && sToolkitSetFrameRateReadOnlyFlagValue;
+ return mSurface.isValid() && shouldEnableDvrr();
}
private boolean shouldSetFrameRate() {
// use toolkitSetFrameRate flag to gate the change
- return sToolkitSetFrameRateReadOnlyFlagValue;
+ return mSurface.isValid() && mPreferredFrameRate > 0
+ && shouldEnableDvrr() && !mIsFrameRateConflicted;
}
private boolean shouldTouchBoost(int motionEventAction, int windowType) {
@@ -12347,7 +12421,7 @@ public final class ViewRootImpl implements ViewParent,
|| motionEventAction == MotionEvent.ACTION_UP;
boolean undesiredType = windowType == TYPE_INPUT_METHOD && mShouldSuppressBoostOnTyping;
// use toolkitSetFrameRate flag to gate the change
- return desiredAction && !undesiredType && sToolkitSetFrameRateReadOnlyFlagValue
+ return desiredAction && !undesiredType && shouldEnableDvrr()
&& getFrameRateBoostOnTouchEnabled();
}
@@ -12358,40 +12432,68 @@ public final class ViewRootImpl implements ViewParent,
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
public void votePreferredFrameRateCategory(int frameRateCategory) {
- mPreferredFrameRateCategory = Math.max(mPreferredFrameRateCategory, frameRateCategory);
+ if (frameRateCategory == FRAME_RATE_CATEGORY_HIGH) {
+ mFrameRateCategoryHighCount = FRAME_RATE_CATEGORY_COUNT;
+ } else if (frameRateCategory == FRAME_RATE_CATEGORY_HIGH_HINT) {
+ mFrameRateCategoryHighHintCount = FRAME_RATE_CATEGORY_COUNT;
+ } else if (frameRateCategory == FRAME_RATE_CATEGORY_NORMAL) {
+ mFrameRateCategoryNormalCount = FRAME_RATE_CATEGORY_COUNT;
+ } else if (frameRateCategory == FRAME_RATE_CATEGORY_LOW) {
+ mFrameRateCategoryLowCount = FRAME_RATE_CATEGORY_COUNT;
+ }
+
+ if (mFrameRateCategoryHighCount > 0) {
+ mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH;
+ } else if (mFrameRateCategoryHighHintCount > 0) {
+ mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH_HINT;
+ } else if (mFrameRateCategoryNormalCount > 0) {
+ mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NORMAL;
+ } else if (mFrameRateCategoryLowCount > 0) {
+ mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_LOW;
+ }
mHasInvalidation = true;
}
/**
- * Allow Views to vote for the preferred frame rate
+ * Allow Views to vote for the preferred frame rate and compatibility.
* When determining the preferred frame rate value,
* we follow this logic: If no preferred frame rate has been set yet,
* we assign the value of frameRate as the preferred frame rate.
- * If either the current or the new preferred frame rate exceeds 60 Hz,
- * we select the higher value between them.
- * However, if both values are 60 Hz or lower, we set the preferred frame rate
- * to 60 Hz to maintain optimal performance.
+ * IF there are multiple frame rates are voted:
+ * 1. There is a frame rate is a multiple of all other frame rates.
+ * We choose this frmae rate to be the one to be set.
+ * 2. There is no frame rate can be a multiple of others
+ * We set category to HIGH if the maximum frame rate is greater than 60.
+ * Otherwise, we set category to NORMAL.
+ *
+ * Use FRAME_RATE_COMPATIBILITY_GTE for velocity and FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
+ * for TextureView video play and user requested frame rate.
*
* @param frameRate the preferred frame rate of a View
+ * @param frameRateCompatibility the preferred frame rate compatibility of a View
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
- public void votePreferredFrameRate(float frameRate) {
+ public void votePreferredFrameRate(float frameRate, int frameRateCompatibility) {
if (frameRate <= 0) {
return;
}
-
- if (mPreferredFrameRate == 0) {
- mPreferredFrameRate = frameRate;
- } else if (frameRate > 60 || mPreferredFrameRate > 60) {
- mPreferredFrameRate = Math.max(mPreferredFrameRate, frameRate);
- } else if (mPreferredFrameRate != frameRate) {
- mPreferredFrameRate = 60;
+ if (mPreferredFrameRate > 0 && mPreferredFrameRate % frameRate != 0
+ && frameRate % mPreferredFrameRate != 0) {
+ mIsFrameRateConflicted = true;
+ mFrameRateCompatibility = FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
+ }
+ if (frameRate > mPreferredFrameRate) {
+ mFrameRateCompatibility = frameRateCompatibility;
}
+ mPreferredFrameRate = Math.max(mPreferredFrameRate, frameRate);
mHasInvalidation = true;
- mHandler.removeMessages(MSG_FRAME_RATE_SETTING);
- mHandler.sendEmptyMessageDelayed(MSG_FRAME_RATE_SETTING,
- FRAME_RATE_SETTING_REEVALUATE_TIME);
+
+ if (!mIsFrameRateConflicted) {
+ mHandler.removeMessages(MSG_FRAME_RATE_SETTING);
+ mHandler.sendEmptyMessageDelayed(MSG_FRAME_RATE_SETTING,
+ FRAME_RATE_SETTING_REEVALUATE_TIME);
+ }
}
/**
@@ -12415,7 +12517,15 @@ public final class ViewRootImpl implements ViewParent,
*/
@VisibleForTesting
public float getPreferredFrameRate() {
- return mPreferredFrameRate;
+ return mPreferredFrameRate >= 0 ? mPreferredFrameRate : mLastPreferredFrameRate;
+ }
+
+ /**
+ * Get the value of mFrameRateCompatibility
+ */
+ @VisibleForTesting
+ public int getFrameRateCompatibility() {
+ return mFrameRateCompatibility;
}
/**
@@ -12443,19 +12553,6 @@ public final class ViewRootImpl implements ViewParent,
boostTimeOut);
}
- @Override
- public boolean transferHostTouchGestureToEmbedded(
- @NonNull SurfaceControlViewHost.SurfacePackage surfacePackage) {
- final IWindowSession realWm = WindowManagerGlobal.getWindowSession();
- try {
- return realWm.transferHostTouchGestureToEmbedded(mWindow,
- surfacePackage.getInputTransferToken());
- } catch (RemoteException e) {
- e.rethrowAsRuntimeException();
- }
- return false;
- }
-
/**
* Set the default back key callback for windowless window, to forward the back key event
* to host app.
@@ -12470,4 +12567,36 @@ public final class ViewRootImpl implements ViewParent,
// Record the largest view of percentage to the display size.
mLargestChildPercentage = Math.max(percentage, mLargestChildPercentage);
}
+
+ /**
+ * Get the value of mIsFrameRatePowerSavingsBalanced
+ * Can be used to checked if toolkit dVRR feature is enabled. The default value is true.
+ */
+ @VisibleForTesting
+ public boolean isFrameRatePowerSavingsBalanced() {
+ return mIsFrameRatePowerSavingsBalanced;
+ }
+
+ /**
+ * Get the value of mIsFrameRateConflicted
+ * Can be used to checked if there is a conflict of frame rate votes.
+ */
+ @VisibleForTesting
+ public boolean isFrameRateConflicted() {
+ return mIsFrameRateConflicted;
+ }
+
+ /**
+ * Set the value of mIsFrameRatePowerSavingsBalanced
+ * Can be used to checked if toolkit dVRR feature is enabled.
+ */
+ public void setFrameRatePowerSavingsBalanced(boolean enabled) {
+ if (sToolkitSetFrameRateReadOnlyFlagValue) {
+ mIsFrameRatePowerSavingsBalanced = enabled;
+ }
+ }
+
+ private boolean shouldEnableDvrr() {
+ return sToolkitSetFrameRateReadOnlyFlagValue && mIsFrameRatePowerSavingsBalanced;
+ }
}
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index d86cc4ac781d..131fca7d923a 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -16,6 +16,8 @@
package android.view;
+import static android.service.autofill.Flags.FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION;
+
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -361,7 +363,7 @@ public abstract class ViewStructure {
* {@link ViewStructure#setCredentialManagerRequest(GetCredentialRequest, OutcomeReceiver)}
*/
@Nullable
- @FlaggedApi("autofill_credman_dev_integration")
+ @FlaggedApi(FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION)
public GetCredentialRequest getCredentialManagerRequest() {
return null;
}
@@ -376,7 +378,7 @@ public abstract class ViewStructure {
* {@link ViewStructure#setCredentialManagerRequest(GetCredentialRequest, OutcomeReceiver)}
*/
@Nullable
- @FlaggedApi("autofill_credman_dev_integration")
+ @FlaggedApi(FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION)
public OutcomeReceiver<
GetCredentialResponse, GetCredentialException> getCredentialManagerCallback() {
return null;
@@ -551,7 +553,7 @@ public abstract class ViewStructure {
* @param request the request to be fired
* @param callback the callback where the response or exception, is returned
*/
- @FlaggedApi("autofill_credman_dev_integration")
+ @FlaggedApi(FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION)
public void setCredentialManagerRequest(@NonNull GetCredentialRequest request,
@NonNull OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback) {}
@@ -559,7 +561,7 @@ public abstract class ViewStructure {
* Clears the credential request previously set through
* {@link ViewStructure#setCredentialManagerRequest(GetCredentialRequest, OutcomeReceiver)}
*/
- @FlaggedApi("autofill_credman_dev_integration")
+ @FlaggedApi(FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION)
public void clearCredentialManagerRequest() {}
/**
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 4ba4ee3ffff7..6b427fc00766 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -1409,6 +1409,42 @@ public abstract class Window {
}
/**
+ * Set whether frameratepowersavingsbalance is enabled for this Window.
+ * This allows device to adjust refresh rate
+ * as needed and can be useful for power saving.
+ *
+ * @param enabled whether the frameratepowersavingsbalance is enabled.
+ * @see #isFrameRatePowerSavingsBalanced()
+ * @see WindowManager.LayoutParams#setFrameRatePowerSavingsBalanced(boolean)
+ */
+ @FlaggedApi(android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+ public void setFrameRatePowerSavingsBalanced(boolean enabled) {
+ if (sToolkitSetFrameRateReadOnlyFlagValue) {
+ final WindowManager.LayoutParams attrs = getAttributes();
+ attrs.setFrameRatePowerSavingsBalanced(enabled);
+ dispatchWindowAttributesChanged(attrs);
+ }
+ }
+
+ /**
+ * Get whether frameratepowersavingsbalance is enabled for this Window.
+ * This allows device to adjust refresh rate
+ * as needed and can be useful for power saving.
+ * {@link #setFrameRateBoostOnTouchEnabled(boolean)}
+ *
+ * @return whether the frameratepowersavingsbalance is enabled.
+ * @see #setFrameRatePowerSavingsBalanced(boolean)
+ * @see WindowManager.LayoutParams#isFrameRatePowerSavingsBalanced()
+ */
+ @FlaggedApi(android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+ public boolean isFrameRatePowerSavingsBalanced() {
+ if (sToolkitSetFrameRateReadOnlyFlagValue) {
+ return getAttributes().isFrameRatePowerSavingsBalanced();
+ }
+ return false;
+ }
+
+ /**
* If {@code isPreferred} is true, this method requests that the connected display does minimal
* post processing when this window is visible on the screen. Otherwise, it requests that the
* display switches back to standard image processing.
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 58fb273212f7..64846d082601 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -123,6 +123,7 @@ import android.view.WindowInsets.Side.InsetsSide;
import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type.InsetsType;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.window.InputTransferToken;
import android.window.TaskFpsCallback;
import android.window.TrustedPresentationThresholds;
@@ -1547,6 +1548,48 @@ public interface WindowManager extends ViewManager {
"android.window.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI";
/**
+ * Application or Activity level
+ * {@link android.content.pm.PackageManager.Property PackageManager.Property} to provide any
+ * preferences for showing all or specific Activities on small cover displays of foldable
+ * style devices.
+ *
+ * <p>The only supported value for the property is {@link #COMPAT_SMALL_COVER_SCREEN_OPT_IN}.
+ *
+ * <p><b>Syntax:</b>
+ * <pre>
+ * &lt;application&gt;
+ * &lt;property
+ * android:name="android.window.PROPERTY_COMPAT_ALLOW_SMALL_COVER_SCREEN"
+ * android:value=1 <!-- COMPAT_COVER_SCREEN_OPT_IN -->/&gt;
+ * &lt;/application&gt;
+ * </pre>
+ */
+ @FlaggedApi(Flags.FLAG_COVER_DISPLAY_OPT_IN)
+ String PROPERTY_COMPAT_ALLOW_SMALL_COVER_SCREEN =
+ "android.window.PROPERTY_COMPAT_ALLOW_SMALL_COVER_SCREEN";
+
+ /**
+ * Value applicable for the {@link #PROPERTY_COMPAT_ALLOW_SMALL_COVER_SCREEN} property to
+ * provide a signal to the system that an application or its specific activities explicitly
+ * opt into being displayed on small foldable device cover screens that measure at least 1.5
+ * inches for the shorter dimension and at least 2.4 inches for the longer dimension.
+ */
+ @CompatSmallScreenPolicy
+ @FlaggedApi(Flags.FLAG_COVER_DISPLAY_OPT_IN)
+ int COMPAT_SMALL_COVER_SCREEN_OPT_IN = 1;
+
+ /**
+ * @hide
+ */
+ @IntDef({
+ COMPAT_SMALL_COVER_SCREEN_OPT_IN,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface CompatSmallScreenPolicy {}
+
+
+
+ /**
* Request for app's keyboard shortcuts to be retrieved asynchronously.
*
* @param receiver The callback to be triggered when the result is ready.
@@ -4396,6 +4439,7 @@ public interface WindowManager extends ViewManager {
* For variable refresh rate project.
*/
private boolean mFrameRateBoostOnTouch = true;
+ private boolean mIsFrameRatePowerSavingsBalanced = true;
private static boolean sToolkitSetFrameRateReadOnlyFlagValue =
android.view.flags.Flags.toolkitSetFrameRateReadOnly();
@@ -4860,6 +4904,36 @@ public interface WindowManager extends ViewManager {
}
/**
+ * Set the value whether frameratepowersavingsbalance is enabled for this Window.
+ * This allows device to adjust refresh rate
+ * as needed and can be useful for power saving.
+ *
+ * @param enabled Whether we should enable frameratepowersavingsbalance.
+ */
+ @FlaggedApi(android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+ public void setFrameRatePowerSavingsBalanced(boolean enabled) {
+ if (sToolkitSetFrameRateReadOnlyFlagValue) {
+ mIsFrameRatePowerSavingsBalanced = enabled;
+ }
+ }
+
+ /**
+ * Get the value whether frameratepowersavingsbalance is enabled for this Window.
+ * This allows device to adjust refresh rate
+ * as needed and can be useful for power saving.
+ * by {@link #setFrameRatePowerSavingsBalanced(boolean)}
+ *
+ * @return Whether we should enable frameratepowersavingsbalance.
+ */
+ @FlaggedApi(android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+ public boolean isFrameRatePowerSavingsBalanced() {
+ if (sToolkitSetFrameRateReadOnlyFlagValue) {
+ return mIsFrameRatePowerSavingsBalanced;
+ }
+ return true;
+ }
+
+ /**
* <p>
* Blurs the screen behind the window. The effect is similar to that of {@link #dimAmount},
* but instead of dimmed, the content behind the window will be blurred (or combined with
@@ -5012,6 +5086,7 @@ public interface WindowManager extends ViewManager {
out.writeFloat(mDesiredHdrHeadroom);
if (sToolkitSetFrameRateReadOnlyFlagValue) {
out.writeBoolean(mFrameRateBoostOnTouch);
+ out.writeBoolean(mIsFrameRatePowerSavingsBalanced);
}
}
@@ -5087,6 +5162,7 @@ public interface WindowManager extends ViewManager {
mDesiredHdrHeadroom = in.readFloat();
if (sToolkitSetFrameRateReadOnlyFlagValue) {
mFrameRateBoostOnTouch = in.readBoolean();
+ mIsFrameRatePowerSavingsBalanced = in.readBoolean();
}
}
@@ -5430,6 +5506,12 @@ public interface WindowManager extends ViewManager {
changes |= LAYOUT_CHANGED;
}
+ if (sToolkitSetFrameRateReadOnlyFlagValue
+ && mIsFrameRatePowerSavingsBalanced != o.mIsFrameRatePowerSavingsBalanced) {
+ mIsFrameRatePowerSavingsBalanced = o.mIsFrameRatePowerSavingsBalanced;
+ changes |= LAYOUT_CHANGED;
+ }
+
return changes;
}
@@ -5657,6 +5739,11 @@ public interface WindowManager extends ViewManager {
sb.append(prefix).append(" frameRateBoostOnTouch=");
sb.append(mFrameRateBoostOnTouch);
}
+ if (sToolkitSetFrameRateReadOnlyFlagValue && mIsFrameRatePowerSavingsBalanced) {
+ sb.append(System.lineSeparator());
+ sb.append(prefix).append(" dvrrWindowFrameRateHint=");
+ sb.append(mIsFrameRatePowerSavingsBalanced);
+ }
if (paramsForRotation != null && paramsForRotation.length != 0) {
sb.append(System.lineSeparator());
sb.append(prefix).append(" paramsForRotation:");
@@ -6097,52 +6184,65 @@ public interface WindowManager extends ViewManager {
* receive batched input event. For those events that are batched, the invocation will happen
* once per {@link Choreographer} frame, and other input events will be delivered immediately.
* This is different from
- * {@link #registerUnbatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Looper,
- * SurfaceControlInputReceiver)} in that the input events are received batched. The caller must
- * invoke {@link #unregisterSurfaceControlInputReceiver(SurfaceControl)} to clean up the
- * resources when no longer needing to use the {@link SurfaceControlInputReceiver}
- *
- * @param displayId The display that the SurfaceControl will be placed on. Input will
- * only work
- * if SurfaceControl is on that display and that display was touched.
- * @param surfaceControl The SurfaceControl to register the InputChannel for
- * @param hostToken The host token to link the InputChannel for. This is primarily for ANRs
- * to ensure the host receives the ANR if any issues with touch on the
- * InputChannel
- * @param choreographer The Choreographer used for batching. This should match the rendering
- * Choreographer.
- * @param receiver The SurfaceControlInputReceiver that will receive the input events
+ * {@link #registerUnbatchedSurfaceControlInputReceiver(int, InputTransferToken, SurfaceControl,
+ * Looper, SurfaceControlInputReceiver)} in that the input events are received batched. The
+ * caller must invoke {@link #unregisterSurfaceControlInputReceiver(SurfaceControl)} to clean up
+ * the resources when no longer needing to use the {@link SurfaceControlInputReceiver}
+ *
+ * @param displayId The display that the SurfaceControl will be placed on. Input
+ * will only work if SurfaceControl is on that display and that
+ * display was touched.
+ * @param surfaceControl The SurfaceControl to register the InputChannel for
+ * @param hostInputTransferToken The host token to link the embedded. This is used to handle
+ * transferring touch gesture from host to embedded and for ANRs
+ * to ensure the host receives the ANR if any issues with
+ * touch on the embedded.
+ * @param choreographer The Choreographer used for batching. This should match the
+ * rendering Choreographer.
+ * @param receiver The SurfaceControlInputReceiver that will receive the input
+ * events
+ * @return Returns the {@link InputTransferToken} that can be used to transfer touch gesture
+ * to or from other windows.
*/
@FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER)
- default void registerBatchedSurfaceControlInputReceiver(int displayId,
- @NonNull IBinder hostToken, @NonNull SurfaceControl surfaceControl,
- @NonNull Choreographer choreographer, @NonNull SurfaceControlInputReceiver receiver) {
+ @NonNull
+ default InputTransferToken registerBatchedSurfaceControlInputReceiver(int displayId,
+ @NonNull InputTransferToken hostInputTransferToken,
+ @NonNull SurfaceControl surfaceControl, @NonNull Choreographer choreographer,
+ @NonNull SurfaceControlInputReceiver receiver) {
throw new UnsupportedOperationException(
"registerBatchedSurfaceControlInputReceiver is not implemented");
}
/**
* Registers a {@link SurfaceControlInputReceiver} for a {@link SurfaceControl} that will
- * receive every input event. This is different than calling @link
- * #registerBatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Choreographer,
- * SurfaceControlInputReceiver)} in that the input events are received unbatched. The caller
- * must invoke {@link #unregisterSurfaceControlInputReceiver(SurfaceControl)} to clean up the
- * resources when no longer needing to use the {@link SurfaceControlInputReceiver}
- *
- * @param displayId The display that the SurfaceControl will be placed on. Input will only
- * work if SurfaceControl is on that display and that display was
- * touched.
- * @param hostToken The host token to link the InputChannel for. This is primarily for ANRs
- * to ensure the host receives the ANR if any issues with touch on the
- * InputChannel
- * @param surfaceControl The SurfaceControl to register the InputChannel for
- * @param looper The looper to use when invoking callbacks.
- * @param receiver The SurfaceControlInputReceiver that will receive the input events
- **/
+ * receive every input event. This is different than calling
+ * {@link #registerBatchedSurfaceControlInputReceiver(int, InputTransferToken, SurfaceControl,
+ * Choreographer, SurfaceControlInputReceiver)} in that the input events are received
+ * unbatched.
+ * The caller must invoke {@link #unregisterSurfaceControlInputReceiver(SurfaceControl)} to
+ * clean up the resources when no longer needing to use the {@link SurfaceControlInputReceiver}
+ *
+ * @param displayId The display that the SurfaceControl will be placed on. Input
+ * will only work if SurfaceControl is on that display and that
+ * display was touched.
+ * @param surfaceControl The SurfaceControl to register the InputChannel for
+ * @param hostInputTransferToken The host token to link the embedded. This is used to handle
+ * transferring touch gesture from host to embedded and for ANRs
+ * to ensure the host receives the ANR if any issues with
+ * touch on the embedded.
+ * @param looper The looper to use when invoking callbacks.
+ * @param receiver The SurfaceControlInputReceiver that will receive the input
+ * events.
+ * @return Returns the {@link InputTransferToken} that can be used to transfer touch gesture
+ * to or from other windows.
+ */
@FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER)
- default void registerUnbatchedSurfaceControlInputReceiver(int displayId,
- @NonNull IBinder hostToken, @NonNull SurfaceControl surfaceControl,
- @NonNull Looper looper, @NonNull SurfaceControlInputReceiver receiver) {
+ @NonNull
+ default InputTransferToken registerUnbatchedSurfaceControlInputReceiver(int displayId,
+ @NonNull InputTransferToken hostInputTransferToken,
+ @NonNull SurfaceControl surfaceControl, @NonNull Looper looper,
+ @NonNull SurfaceControlInputReceiver receiver) {
throw new UnsupportedOperationException(
"registerUnbatchedSurfaceControlInputReceiver is not implemented");
}
@@ -6152,11 +6252,10 @@ public interface WindowManager extends ViewManager {
* specified token.
* <p>
* Must be called on the same {@link Looper} thread to which was passed to the
- * {@link #registerBatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl,
- * Choreographer,
- * SurfaceControlInputReceiver)} or
- * {@link #registerUnbatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Looper,
- * SurfaceControlInputReceiver)}
+ * {@link #registerBatchedSurfaceControlInputReceiver(int, InputTransferToken, SurfaceControl,
+ * Choreographer, SurfaceControlInputReceiver)} or
+ * {@link #registerUnbatchedSurfaceControlInputReceiver(int, InputTransferToken, SurfaceControl,
+ * Looper, SurfaceControlInputReceiver)}
*
* @param surfaceControl The SurfaceControl to remove and unregister the input channel for.
*/
@@ -6167,12 +6266,12 @@ public interface WindowManager extends ViewManager {
}
/**
- * Returns the input client token for the {@link SurfaceControl}. This will only return non null
- * if the SurfaceControl was registered for input via
- * { #registerBatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Choreographer,
- * SurfaceControlInputReceiver)} or
- * {@link #registerUnbatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Looper,
- * SurfaceControlInputReceiver)}.
+ * Returns the input client token for the {@link SurfaceControl}. This will only return non
+ * null if the SurfaceControl was registered for input via
+ * {@link #registerBatchedSurfaceControlInputReceiver(int, InputTransferToken, SurfaceControl,
+ * Choreographer, SurfaceControlInputReceiver)} or
+ * {@link #registerUnbatchedSurfaceControlInputReceiver(int, InputTransferToken,
+ * SurfaceControl, Looper, SurfaceControlInputReceiver)}.
* <p>
* This is helpful for testing to ensure the test waits for the layer to be registered with
* SurfaceFlinger and Input before proceeding with the test.
@@ -6188,6 +6287,70 @@ public interface WindowManager extends ViewManager {
}
/**
+ * Transfer the currently in progress touch gesture from the transferFromToken to the
+ * transferToToken.
+ * <p><br>
+ * This requires that the fromToken and toToken are associated with each other. The association
+ * can be done different ways, depending on how the embedded window is created.
+ * <ul>
+ * <li>
+ * Creating a {@link SurfaceControlViewHost} and passing the host's
+ * {@link InputTransferToken} for
+ * {@link SurfaceControlViewHost#SurfaceControlViewHost(Context, Display, InputTransferToken)}.
+ * </li>
+ * <li>
+ * Registering a SurfaceControl for input and passing the host's token to either
+ * {@link #registerBatchedSurfaceControlInputReceiver(int, InputTransferToken, SurfaceControl,
+ * Choreographer, SurfaceControlInputReceiver)} or
+ * {@link #registerUnbatchedSurfaceControlInputReceiver(int, InputTransferToken,
+ * SurfaceControl, Looper, SurfaceControlInputReceiver)}.
+ * </li>
+ * </ul>
+ * <p>
+ * The host is likely to be an {@link AttachedSurfaceControl} so the host token can be
+ * retrieved via {@link AttachedSurfaceControl#getInputTransferToken()}.
+ * <p><br>
+ * Only the window currently receiving touch is allowed to transfer the gesture so if the caller
+ * attempts to transfer touch gesture from a token that doesn't have touch, it will fail the
+ * transfer.
+ * <p><br>
+ * When the host wants to transfer touch gesture to the embedded, it can retrieve the embedded
+ * token via {@link SurfaceControlViewHost.SurfacePackage#getInputTransferToken()} or use the
+ * value returned from either
+ * {@link #registerBatchedSurfaceControlInputReceiver(int, InputTransferToken, SurfaceControl,
+ * Choreographer, SurfaceControlInputReceiver)} or
+ * {@link #registerUnbatchedSurfaceControlInputReceiver(int, InputTransferToken, SurfaceControl,
+ * Looper, SurfaceControlInputReceiver)} and pass its own token as the transferFromToken.
+ * <p>
+ * When the embedded wants to transfer touch gesture to the host, it can pass in its own
+ * token as the transferFromToken and use the associated host's {@link InputTransferToken} as
+ * the transferToToken
+ * <p><br>
+ * When the touch is transferred, the window currently receiving touch gets an ACTION_CANCEL
+ * and does not receive any further input events for this gesture.
+ * <p>
+ * The transferred-to window receives an ACTION_DOWN event and then the remainder of the input
+ * events for this gesture. It does not receive any of the previous events of this gesture that
+ * the originating window received.
+ * <p>
+ * The transferTouchGesture API only works for the current gesture. When a new gesture
+ * arrives, input dispatcher will do a new round of hit testing. So, if the host window is
+ * still the first thing that's being touched, then it will receive the new gesture again. It
+ * will again be up to the host to transfer this new gesture to the embedded.
+ *
+ * @param transferFromToken the InputTransferToken for the currently active gesture
+ * @param transferToToken the InputTransferToken to transfer the gesture to.
+ * @return Whether the touch stream was transferred.
+ * @see android.view.SurfaceControlViewHost.SurfacePackage#getInputTransferToken()
+ * @see AttachedSurfaceControl#getInputTransferToken()
+ */
+ @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER)
+ default boolean transferTouchGesture(@NonNull InputTransferToken transferFromToken,
+ @NonNull InputTransferToken transferToToken) {
+ throw new UnsupportedOperationException("transferTouchGesture is not implemented");
+ }
+
+ /**
* @hide
*/
default @NonNull IBinder getDefaultToken() {
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 142896346bde..c6d0454fbcfd 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -839,14 +839,15 @@ public final class WindowManagerGlobal {
mTrustedPresentationListener.removeListener(listener);
}
- void registerBatchedSurfaceControlInputReceiver(int displayId,
+ InputTransferToken registerBatchedSurfaceControlInputReceiver(int displayId,
@NonNull InputTransferToken hostToken, @NonNull SurfaceControl surfaceControl,
@NonNull Choreographer choreographer, @NonNull SurfaceControlInputReceiver receiver) {
IBinder clientToken = new Binder();
+ InputTransferToken inputTransferToken = new InputTransferToken();
InputChannel inputChannel = new InputChannel();
try {
WindowManagerGlobal.getWindowSession().grantInputChannel(displayId, surfaceControl,
- clientToken, hostToken, 0, 0, TYPE_APPLICATION, 0, null, null,
+ clientToken, hostToken, 0, 0, TYPE_APPLICATION, 0, null, inputTransferToken,
surfaceControl.getName(), inputChannel);
} catch (RemoteException e) {
Log.e(TAG, "Failed to create input channel", e);
@@ -865,16 +866,18 @@ public final class WindowManagerGlobal {
}
}));
}
+ return inputTransferToken;
}
- void registerUnbatchedSurfaceControlInputReceiver(int displayId,
+ InputTransferToken registerUnbatchedSurfaceControlInputReceiver(int displayId,
@NonNull InputTransferToken hostToken, @NonNull SurfaceControl surfaceControl,
@NonNull Looper looper, @NonNull SurfaceControlInputReceiver receiver) {
IBinder clientToken = new Binder();
+ InputTransferToken inputTransferToken = new InputTransferToken();
InputChannel inputChannel = new InputChannel();
try {
WindowManagerGlobal.getWindowSession().grantInputChannel(displayId, surfaceControl,
- clientToken, hostToken, 0, 0, TYPE_APPLICATION, 0, null, null,
+ clientToken, hostToken, 0, 0, TYPE_APPLICATION, 0, null, inputTransferToken,
surfaceControl.getName(), inputChannel);
} catch (RemoteException e) {
Log.e(TAG, "Failed to create input channel", e);
@@ -892,9 +895,10 @@ public final class WindowManagerGlobal {
}
}));
}
+ return inputTransferToken;
}
- void unregisterSurfaceControlInputReceiver(SurfaceControl surfaceControl) {
+ void unregisterSurfaceControlInputReceiver(@NonNull SurfaceControl surfaceControl) {
SurfaceControlInputReceiverInfo surfaceControlInputReceiverInfo;
synchronized (mSurfaceControlInputReceivers) {
surfaceControlInputReceiverInfo = mSurfaceControlInputReceivers.removeReturnOld(
@@ -916,7 +920,7 @@ public final class WindowManagerGlobal {
surfaceControlInputReceiverInfo.mInputEventReceiver.dispose();
}
- IBinder getSurfaceControlInputClientToken(SurfaceControl surfaceControl) {
+ IBinder getSurfaceControlInputClientToken(@NonNull SurfaceControl surfaceControl) {
SurfaceControlInputReceiverInfo surfaceControlInputReceiverInfo;
synchronized (mSurfaceControlInputReceivers) {
surfaceControlInputReceiverInfo = mSurfaceControlInputReceivers.get(
@@ -930,6 +934,17 @@ public final class WindowManagerGlobal {
return surfaceControlInputReceiverInfo.mClientToken;
}
+ boolean transferTouchGesture(@NonNull InputTransferToken transferFromToken,
+ @NonNull InputTransferToken transferToToken) {
+ try {
+ return getWindowManagerService().transferTouchGesture(transferFromToken,
+ transferToToken);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ return false;
+ }
+
private final class TrustedPresentationListener extends
ITrustedPresentationListener.Stub {
private static int sId = 0;
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 2fb5213279a6..df4fed6a45f1 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -533,36 +533,56 @@ public final class WindowManagerImpl implements WindowManager {
mGlobal.unregisterTrustedPresentationListener(listener);
}
+ @NonNull
@Override
- public void registerBatchedSurfaceControlInputReceiver(int displayId,
- @NonNull IBinder hostToken, @NonNull SurfaceControl surfaceControl,
- @NonNull Choreographer choreographer, @NonNull SurfaceControlInputReceiver receiver) {
- mGlobal.registerBatchedSurfaceControlInputReceiver(displayId,
- new InputTransferToken(hostToken),
+ public InputTransferToken registerBatchedSurfaceControlInputReceiver(int displayId,
+ @NonNull InputTransferToken hostInputTransferToken,
+ @NonNull SurfaceControl surfaceControl, @NonNull Choreographer choreographer,
+ @NonNull SurfaceControlInputReceiver receiver) {
+ Objects.requireNonNull(hostInputTransferToken);
+ Objects.requireNonNull(surfaceControl);
+ Objects.requireNonNull(choreographer);
+ Objects.requireNonNull(receiver);
+ return mGlobal.registerBatchedSurfaceControlInputReceiver(displayId, hostInputTransferToken,
surfaceControl, choreographer, receiver);
}
+ @NonNull
@Override
- public void registerUnbatchedSurfaceControlInputReceiver(
- int displayId, @NonNull IBinder hostToken, @NonNull SurfaceControl surfaceControl,
- @NonNull Looper looper, @NonNull SurfaceControlInputReceiver receiver) {
- mGlobal.registerUnbatchedSurfaceControlInputReceiver(displayId,
- new InputTransferToken(hostToken),
- surfaceControl, looper, receiver);
+ public InputTransferToken registerUnbatchedSurfaceControlInputReceiver(int displayId,
+ @NonNull InputTransferToken hostInputTransferToken,
+ @NonNull SurfaceControl surfaceControl, @NonNull Looper looper,
+ @NonNull SurfaceControlInputReceiver receiver) {
+ Objects.requireNonNull(hostInputTransferToken);
+ Objects.requireNonNull(surfaceControl);
+ Objects.requireNonNull(looper);
+ Objects.requireNonNull(receiver);
+ return mGlobal.registerUnbatchedSurfaceControlInputReceiver(displayId,
+ hostInputTransferToken, surfaceControl, looper, receiver);
}
@Override
public void unregisterSurfaceControlInputReceiver(@NonNull SurfaceControl surfaceControl) {
+ Objects.requireNonNull(surfaceControl);
mGlobal.unregisterSurfaceControlInputReceiver(surfaceControl);
}
@Override
@Nullable
public IBinder getSurfaceControlInputClientToken(@NonNull SurfaceControl surfaceControl) {
+ Objects.requireNonNull(surfaceControl);
return mGlobal.getSurfaceControlInputClientToken(surfaceControl);
}
@Override
+ public boolean transferTouchGesture(@NonNull InputTransferToken transferFromToken,
+ @NonNull InputTransferToken transferToToken) {
+ Objects.requireNonNull(transferFromToken);
+ Objects.requireNonNull(transferToToken);
+ return mGlobal.transferTouchGesture(transferFromToken, transferToToken);
+ }
+
+ @Override
public @ScreenRecordingState int addScreenRecordingCallback(
@NonNull @CallbackExecutor Executor executor,
@NonNull Consumer<@ScreenRecordingState Integer> callback) {
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 3f1ae51ef25e..2b2c50725749 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -651,21 +651,6 @@ public class WindowlessWindowManager implements IWindowSession {
}
@Override
- public boolean transferEmbeddedTouchFocusToHost(IWindow window) {
- Log.e(TAG, "Received request to transferEmbeddedTouch focus on WindowlessWindowManager" +
- " we shouldn't get here!");
- return false;
- }
-
- @Override
- public boolean transferHostTouchGestureToEmbedded(IWindow hostWindow,
- InputTransferToken embeddedInputToken) {
- Log.e(TAG, "Received request to transferHostTouchGestureToEmbedded on"
- + " WindowlessWindowManager. We shouldn't get here!");
- return false;
- }
-
- @Override
public boolean moveFocusToAdjacentWindow(IWindow fromWindow, @FocusDirection int direction) {
Log.e(TAG, "Received request to moveFocusToAdjacentWindow on"
+ " WindowlessWindowManager. We shouldn't get here!");
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index fa0052cf664a..749f977f5e50 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -16,6 +16,7 @@
package android.view.accessibility;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
@@ -93,6 +94,12 @@ public final class AccessibilityWindowInfo implements Parcelable {
*/
public static final int TYPE_MAGNIFICATION_OVERLAY = 6;
+ /**
+ * Window type: A system window that has the function to control an associated window.
+ */
+ @FlaggedApi(Flags.FLAG_ADD_TYPE_WINDOW_CONTROL)
+ public static final int TYPE_WINDOW_CONTROL = 7;
+
/* Special values for window IDs */
/** @hide */
public static final int ACTIVE_WINDOW_ID = Integer.MAX_VALUE;
@@ -873,6 +880,10 @@ public final class AccessibilityWindowInfo implements Parcelable {
* @hide
*/
public static String typeToString(int type) {
+ if (Flags.addTypeWindowControl() && type == TYPE_WINDOW_CONTROL) {
+ return "TYPE_WINDOW_CONTROL";
+ }
+
switch (type) {
case TYPE_APPLICATION: {
return "TYPE_APPLICATION";
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index a11ac7cb48ad..5b99c71f3a8b 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -102,6 +102,13 @@ flag {
flag {
namespace: "accessibility"
+ name: "add_type_window_control"
+ description: "adds new TYPE_WINDOW_CONTROL to AccessibilityWindowInfo for detecting Window Decorations"
+ bug: "320445550"
+}
+
+flag {
+ namespace: "accessibility"
name: "update_always_on_a11y_service"
description: "Updates the Always-On A11yService state when the user changes the enablement of the shortcut."
bug: "298869916"
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index 57a3b765641d..8a407c3252f0 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -185,7 +185,7 @@ public class AnimationUtils {
@FlaggedApi(FLAG_EXPECTED_PRESENTATION_TIME_READ_ONLY)
public static long getExpectedPresentationTimeNanos() {
if (!sExpectedPresentationTimeFlagValue) {
- return SystemClock.uptimeMillis();
+ return SystemClock.uptimeMillis() * TimeUtils.NANOS_PER_MS;
}
AnimationState state = sAnimationState.get();
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index bd9f5049321d..364c94f7f92f 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -50,6 +50,7 @@ import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.credentials.GetCredentialResponse;
import android.graphics.Rect;
import android.metrics.LogMaker;
import android.os.Build;
@@ -2364,6 +2365,7 @@ public final class AutofillManager {
synchronized (mLock) {
if (!isActiveLocked()) {
+ Log.w(TAG, "onAuthenticationResult(): sessionId=" + mSessionId + " not active");
return;
}
mState = STATE_ACTIVE;
@@ -2380,6 +2382,7 @@ public final class AutofillManager {
}
if (data == null) {
// data is set to null when result is not RESULT_OK
+ Log.i(TAG, "onAuthenticationResult(): empty intent");
return;
}
@@ -2923,6 +2926,65 @@ public final class AutofillManager {
}
}
+ private void onGetCredentialResponse(int sessionId, AutofillId id,
+ GetCredentialResponse response) {
+ synchronized (mLock) {
+ if (sessionId != mSessionId) {
+ Log.w(TAG, "onGetCredentialResponse afm sessionIds don't match");
+ return;
+ }
+
+ final AutofillClient client = getClient();
+ if (client == null) {
+ Log.w(TAG, "onGetCredentialResponse afm client id null");
+ return;
+ }
+ ArrayList<AutofillId> failedIds = new ArrayList<>();
+ final View[] views = client.autofillClientFindViewsByAutofillIdTraversal(
+ Helper.toArray(new ArrayList<>(Collections.singleton(id))));
+ if (views == null || views.length == 0) {
+ Log.w(TAG, "onGetCredentialResponse afm client view not found");
+ return;
+ }
+
+ final View view = views[0];
+ if (view == null) {
+ Log.i(TAG, "onGetCredentialResponse View is null");
+
+ // Most likely view has been removed after the initial request was sent to the
+ // the service; this is fine, but we need to update the view status in the
+ // server side so it can be triggered again.
+ Log.d(TAG, "onGetCredentialResponse(): no View with id " + id);
+ failedIds.add(id);
+ }
+ if (id.isVirtualInt()) {
+ Log.i(TAG, "onGetCredentialResponse afm client id is virtual");
+ // TODO(b/326314286): Handle virtual views
+ } else {
+ Log.i(TAG, "onGetCredentialResponse afm client id is NOT virtual");
+ view.onGetCredentialResponse(response);
+ }
+ handleFailedIdsLocked(failedIds);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void handleFailedIdsLocked(ArrayList<AutofillId> failedIds) {
+ if (failedIds != null && !failedIds.isEmpty()) {
+ if (sVerbose) {
+ Log.v(TAG, "autofill(): total failed views: " + failedIds);
+ }
+ try {
+ mService.setAutofillFailure(mSessionId, failedIds, mContext.getUserId());
+ } catch (RemoteException e) {
+ // In theory, we could ignore this error since it's not a big deal, but
+ // in reality, we rather crash the app anyways, as the failure could be
+ // a consequence of something going wrong on the server side...
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
private void autofill(int sessionId, List<AutofillId> ids, List<AutofillValue> values,
boolean hideHighlight) {
synchronized (mLock) {
@@ -2989,19 +3051,7 @@ public final class AutofillManager {
}
}
- if (failedIds != null) {
- if (sVerbose) {
- Log.v(TAG, "autofill(): total failed views: " + failedIds);
- }
- try {
- mService.setAutofillFailure(mSessionId, failedIds, mContext.getUserId());
- } catch (RemoteException e) {
- // In theory, we could ignore this error since it's not a big deal, but
- // in reality, we rather crash the app anyways, as the failure could be
- // a consequence of something going wrong on the server side...
- throw e.rethrowFromSystemServer();
- }
- }
+ handleFailedIdsLocked(failedIds);
if (virtualValues != null) {
for (int i = 0; i < virtualValues.size(); i++) {
@@ -3429,6 +3479,10 @@ public final class AutofillManager {
if (view == null) {
return false;
}
+ if (view.getViewCredentialHandler() != null) {
+ return true;
+ }
+
String[] hints = view.getAutofillHints();
if (hints == null) {
return false;
@@ -4319,6 +4373,15 @@ public final class AutofillManager {
}
@Override
+ public void onGetCredentialResponse(int sessionId, AutofillId id,
+ GetCredentialResponse response) {
+ final AutofillManager afm = mAfm.get();
+ if (afm != null) {
+ afm.post(() -> afm.onGetCredentialResponse(sessionId, id, response));
+ }
+ }
+
+ @Override
public void autofillContent(int sessionId, AutofillId id, ClipData content) {
final AutofillManager afm = mAfm.get();
if (afm != null) {
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index 917a974f992d..e838027a9ae1 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -22,6 +22,7 @@ import android.content.ClipData;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentSender;
+import android.credentials.GetCredentialResponse;
import android.graphics.Rect;
import android.os.IBinder;
import android.view.autofill.AutofillId;
@@ -48,6 +49,9 @@ oneway interface IAutoFillManagerClient {
void autofill(int sessionId, in List<AutofillId> ids, in List<AutofillValue> values,
boolean hideHighlight);
+ void onGetCredentialResponse(int sessionId, in AutofillId id,
+ in GetCredentialResponse response);
+
/**
* Autofills the activity with rich content data (e.g. an image) from a dataset.
*/
diff --git a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
index dc5e0e5c62aa..491b0e349cde 100644
--- a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
+++ b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
@@ -34,6 +34,7 @@ import android.view.WindowManager;
import android.window.ImeOnBackInvokedDispatcher;
import com.android.internal.inputmethod.DirectBootAwareness;
+import com.android.internal.inputmethod.IBooleanListener;
import com.android.internal.inputmethod.IConnectionlessHandwritingCallback;
import com.android.internal.inputmethod.IImeTracker;
import com.android.internal.inputmethod.IInputMethodClient;
@@ -66,6 +67,7 @@ final class IInputMethodManagerGlobalInvoker {
@Nullable
private static volatile IImeTracker sTrackerServiceCache = null;
+ private static int sCurStartInputSeq = 0;
/**
* @return {@code true} if {@link IInputMethodManager} is available.
@@ -327,6 +329,7 @@ final class IInputMethodManagerGlobalInvoker {
}
}
+ // TODO(b/293640003): Remove method once Flags.useZeroJankProxy() is enabled.
@AnyThread
@NonNull
@RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
@@ -353,6 +356,41 @@ final class IInputMethodManagerGlobalInvoker {
}
}
+ /**
+ * Returns a sequence number for startInput.
+ */
+ @AnyThread
+ @NonNull
+ @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
+ static int startInputOrWindowGainedFocusAsync(@StartInputReason int startInputReason,
+ @NonNull IInputMethodClient client, @Nullable IBinder windowToken,
+ @StartInputFlags int startInputFlags,
+ @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode,
+ @WindowManager.LayoutParams.Flags int windowFlags, @Nullable EditorInfo editorInfo,
+ @Nullable IRemoteInputConnection remoteInputConnection,
+ @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
+ int unverifiedTargetSdkVersion, @UserIdInt int userId,
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
+ final IInputMethodManager service = getService();
+ if (service == null) {
+ return -1;
+ }
+ try {
+ service.startInputOrWindowGainedFocusAsync(startInputReason, client, windowToken,
+ startInputFlags, softInputMode, windowFlags, editorInfo, remoteInputConnection,
+ remoteAccessibilityInputConnection, unverifiedTargetSdkVersion, userId,
+ imeDispatcher, advanceAngGetStartInputSequenceNumber());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ return sCurStartInputSeq;
+ }
+
+ private static int advanceAngGetStartInputSequenceNumber() {
+ return ++sCurStartInputSeq;
+ }
+
+
@AnyThread
static void showInputMethodPickerFromClient(@NonNull IInputMethodClient client,
int auxiliarySubtypeMode) {
@@ -550,6 +588,28 @@ final class IInputMethodManagerGlobalInvoker {
}
}
+ /** Returns {@code true} if method is invoked */
+ @AnyThread
+ static boolean acceptStylusHandwritingDelegationAsync(
+ @NonNull IInputMethodClient client,
+ @UserIdInt int userId,
+ @NonNull String delegatePackageName,
+ @NonNull String delegatorPackageName,
+ @InputMethodManager.HandwritingDelegateFlags int flags,
+ @NonNull IBooleanListener callback) {
+ final IInputMethodManager service = getService();
+ if (service == null) {
+ return false;
+ }
+ try {
+ service.acceptStylusHandwritingDelegationAsync(
+ client, userId, delegatePackageName, delegatorPackageName, flags, callback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ return true;
+ }
+
@AnyThread
@RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
static boolean isStylusHandwritingAvailableAsUser(
diff --git a/core/java/android/view/inputmethod/InputBinding.java b/core/java/android/view/inputmethod/InputBinding.java
index 2bfeb5abb395..fedee9de1372 100644
--- a/core/java/android/view/inputmethod/InputBinding.java
+++ b/core/java/android/view/inputmethod/InputBinding.java
@@ -19,11 +19,13 @@ package android.view.inputmethod;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
/**
* Information given to an {@link InputMethod} about a client connecting
* to it.
*/
+@RavenwoodKeepWholeClass
public final class InputBinding implements Parcelable {
static final String TAG = "InputBinding";
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index f4b09df35705..3bce155049c8 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -109,6 +109,7 @@ import android.window.WindowOnBackInvokedDispatcher;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.DirectBootAwareness;
+import com.android.internal.inputmethod.IBooleanListener;
import com.android.internal.inputmethod.IConnectionlessHandwritingCallback;
import com.android.internal.inputmethod.IInputMethodClient;
import com.android.internal.inputmethod.IInputMethodSession;
@@ -321,6 +322,22 @@ public final class InputMethodManager {
};
/**
+ * A runnable that reports {@link InputConnection} opened event for calls to
+ * {@link IInputMethodManagerGlobalInvoker#startInputOrWindowGainedFocusAsync}.
+ */
+ private abstract static class ReportInputConnectionOpenedRunner implements Runnable {
+ /**
+ * Sequence number to track startInput requests to
+ * {@link IInputMethodManagerGlobalInvoker#startInputOrWindowGainedFocusAsync}
+ */
+ int mSequenceNum;
+ ReportInputConnectionOpenedRunner(int sequenceNum) {
+ this.mSequenceNum = sequenceNum;
+ }
+ }
+ private ReportInputConnectionOpenedRunner mReportInputConnectionOpenedRunner;
+
+ /**
* Ensures that {@link #sInstance} becomes non-{@code null} for application that have directly
* or indirectly relied on {@link #sInstance} via reflection or something like that.
*
@@ -445,8 +462,8 @@ public final class InputMethodManager {
* Flag indicating that views from the default home screen ({@link Intent#CATEGORY_HOME}) may
* act as a handwriting delegator for the delegate editor view. If set, views from the home
* screen package will be trusted for handwriting delegation, in addition to views in the {@code
- * delegatorPackageName} passed to {@link #acceptStylusHandwritingDelegation(View, String,
- * int)}.
+ * delegatorPackageName} passed to
+ * {@link #acceptStylusHandwritingDelegation(View, String, int, Executor, Consumer)} .
*/
@FlaggedApi(FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR)
public static final int HANDWRITING_DELEGATE_FLAG_HOME_DELEGATOR_ALLOWED = 0x0001;
@@ -691,6 +708,7 @@ public final class InputMethodManager {
private static final int MSG_UNBIND_ACCESSIBILITY_SERVICE = 12;
private static final int MSG_SET_INTERACTIVE = 13;
private static final int MSG_ON_SHOW_REQUESTED = 31;
+ private static final int MSG_START_INPUT_RESULT = 40;
/**
* Calling this will invalidate Local stylus handwriting availability Cache which
@@ -1045,7 +1063,7 @@ public final class InputMethodManager {
return;
}
case MSG_BIND: {
- final InputBindResult res = (InputBindResult)msg.obj;
+ final InputBindResult res = (InputBindResult) msg.obj;
if (DEBUG) {
Log.i(TAG, "handleMessage: MSG_BIND " + res.sequence + "," + res.id);
}
@@ -1071,6 +1089,60 @@ public final class InputMethodManager {
startInputInner(StartInputReason.BOUND_TO_IMMS, null, 0, 0, 0);
return;
}
+
+ case MSG_START_INPUT_RESULT: {
+ final InputBindResult res = (InputBindResult) msg.obj;
+ final int startInputSeq = msg.arg1;
+ if (res == null) {
+ // IMMS logs .wtf already.
+ return;
+ }
+ if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
+ synchronized (mH) {
+ if (res.id != null) {
+ updateInputChannelLocked(res.channel);
+ mCurMethod = res.method; // for @UnsupportedAppUsage
+ mCurBindState = new BindState(res);
+ mAccessibilityInputMethodSession.clear();
+ if (res.accessibilitySessions != null) {
+ for (int i = 0; i < res.accessibilitySessions.size(); i++) {
+ IAccessibilityInputMethodSessionInvoker wrapper =
+ IAccessibilityInputMethodSessionInvoker.createOrNull(
+ res.accessibilitySessions.valueAt(i));
+ if (wrapper != null) {
+ mAccessibilityInputMethodSession.append(
+ res.accessibilitySessions.keyAt(i), wrapper);
+ }
+ }
+ }
+ mCurId = res.id; // for @UnsupportedAppUsage
+ } else if (res.channel != null && res.channel != mCurChannel) {
+ res.channel.dispose();
+ }
+ switch (res.result) {
+ case InputBindResult.ResultCode.ERROR_NOT_IME_TARGET_WINDOW:
+ mRestartOnNextWindowFocus = true;
+ mServedView = null;
+ break;
+ }
+ if (mCompletions != null) {
+ if (isImeSessionAvailableLocked()) {
+ mCurBindState.mImeSession.displayCompletions(mCompletions);
+ }
+ }
+
+ if (res != null
+ && res.method != null
+ && mServedView != null
+ && mReportInputConnectionOpenedRunner != null
+ && mReportInputConnectionOpenedRunner.mSequenceNum
+ == startInputSeq) {
+ mReportInputConnectionOpenedRunner.run();
+ }
+ mReportInputConnectionOpenedRunner = null;
+ }
+ return;
+ }
case MSG_UNBIND: {
final int sequence = msg.arg1;
@UnbindReason
@@ -1322,6 +1394,12 @@ public final class InputMethodManager {
}
@Override
+ public void onStartInputResult(InputBindResult res, int startInputSeq) {
+ mH.obtainMessage(MSG_START_INPUT_RESULT, startInputSeq, -1 /* unused */, res)
+ .sendToTarget();
+ }
+
+ @Override
public void onBindAccessibilityService(InputBindResult res, int id) {
mH.obtainMessage(MSG_BIND_ACCESSIBILITY_SERVICE, id, 0, res).sendToTarget();
}
@@ -2010,6 +2088,7 @@ public final class InputMethodManager {
mServedConnecting = false;
clearConnectionLocked();
}
+ mReportInputConnectionOpenedRunner = null;
// Clear the back callbacks held by the ime dispatcher to avoid memory leaks.
mImeDispatcher.clear();
}
@@ -2440,16 +2519,46 @@ public final class InputMethodManager {
view, /* delegatorPackageName= */ null, /* handwritingDelegateFlags= */ 0);
}
+ private void startStylusHandwritingInternalAsync(
+ @NonNull View view, @Nullable String delegatorPackageName,
+ @HandwritingDelegateFlags int handwritingDelegateFlags,
+ @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
+ Objects.requireNonNull(view);
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+
+ startStylusHandwritingInternal(
+ view, delegatorPackageName, handwritingDelegateFlags, executor, callback);
+ }
+
+ private void sendFailureCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<Boolean> callback) {
+ if (executor == null || callback == null) {
+ return;
+ }
+ executor.execute(() -> callback.accept(false));
+ }
+
private boolean startStylusHandwritingInternal(
@NonNull View view, @Nullable String delegatorPackageName,
@HandwritingDelegateFlags int handwritingDelegateFlags) {
+ return startStylusHandwritingInternal(
+ view, delegatorPackageName, handwritingDelegateFlags,
+ null /* executor */, null /* callback */);
+ }
+
+ private boolean startStylusHandwritingInternal(
+ @NonNull View view, @Nullable String delegatorPackageName,
+ @HandwritingDelegateFlags int handwritingDelegateFlags, Executor executor,
+ Consumer<Boolean> callback) {
Objects.requireNonNull(view);
+ boolean useCallback = callback != null;
// Re-dispatch if there is a context mismatch.
final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
if (fallbackImm != null) {
fallbackImm.startStylusHandwritingInternal(
- view, delegatorPackageName, handwritingDelegateFlags);
+ view, delegatorPackageName, handwritingDelegateFlags, executor, callback);
}
boolean useDelegation = !TextUtils.isEmpty(delegatorPackageName);
@@ -2459,21 +2568,40 @@ public final class InputMethodManager {
if (!hasServedByInputMethodLocked(view)) {
Log.w(TAG,
"Ignoring startStylusHandwriting as view=" + view + " is not served.");
+ sendFailureCallback(executor, callback);
return false;
}
if (view.getViewRootImpl() != mCurRootView) {
Log.w(TAG,
"Ignoring startStylusHandwriting: View's window does not have focus.");
+ sendFailureCallback(executor, callback);
return false;
}
if (useDelegation) {
- return IInputMethodManagerGlobalInvoker.acceptStylusHandwritingDelegation(
- mClient, UserHandle.myUserId(), view.getContext().getOpPackageName(),
- delegatorPackageName, handwritingDelegateFlags);
+ if (useCallback) {
+ IBooleanListener listener = new IBooleanListener.Stub() {
+ @Override
+ public void onResult(boolean value) {
+ executor.execute(() -> {
+ callback.accept(value);
+ });
+ }
+ };
+ if (!IInputMethodManagerGlobalInvoker.acceptStylusHandwritingDelegationAsync(
+ mClient, UserHandle.myUserId(), view.getContext().getOpPackageName(),
+ delegatorPackageName, handwritingDelegateFlags, listener)) {
+ sendFailureCallback(executor, callback);
+ }
+ return true;
+ } else {
+ return IInputMethodManagerGlobalInvoker.acceptStylusHandwritingDelegation(
+ mClient, UserHandle.myUserId(), view.getContext().getOpPackageName(),
+ delegatorPackageName, handwritingDelegateFlags);
+ }
} else {
IInputMethodManagerGlobalInvoker.startStylusHandwriting(mClient);
+ return false;
}
- return false;
}
}
@@ -2710,6 +2838,7 @@ public final class InputMethodManager {
* #prepareStylusHandwritingDelegation(View, String)} and delegation is accepted
* @see #prepareStylusHandwritingDelegation(View, String)
* @see #acceptStylusHandwritingDelegation(View)
+ * TODO (b/293640003): deprecate this method once flag is enabled.
*/
// TODO(b/300979854): Once connectionless APIs are finalised, update documentation to add:
// <p>Otherwise, if the delegator view previously started delegation using {@link
@@ -2727,6 +2856,36 @@ public final class InputMethodManager {
/**
* Accepts and starts a stylus handwriting session on the delegate view, if handwriting
+ * initiation delegation was previously requested using
+ * {@link #prepareStylusHandwritingDelegation(View, String)} from the delegator and the view
+ * belongs to a specified delegate package.
+ *
+ * @param delegateView delegate view capable of receiving input via {@link InputConnection}
+ * on which {@link #startStylusHandwriting(View)} will be called.
+ * @param delegatorPackageName package name of the delegator that handled initial stylus stroke.
+ * @param executor The executor to run the callback on.
+ * @param callback Consumer callback that provides {@code true} if view belongs to allowed
+ * delegate package declared in
+ * {@link #prepareStylusHandwritingDelegation(View, String)} and handwriting
+ * session can start.
+ * @see #prepareStylusHandwritingDelegation(View, String)
+ * @see #acceptStylusHandwritingDelegation(View)
+ */
+ @FlaggedApi(Flags.FLAG_USE_ZERO_JANK_PROXY)
+ public void acceptStylusHandwritingDelegation(
+ @NonNull View delegateView, @NonNull String delegatorPackageName,
+ @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
+ Objects.requireNonNull(delegatorPackageName);
+ int flags = 0;
+ if (Flags.homeScreenHandwritingDelegator()) {
+ flags = delegateView.getHandwritingDelegateFlags();
+ }
+ startStylusHandwritingInternalAsync(
+ delegateView, delegatorPackageName, flags, executor, callback);
+ }
+
+ /**
+ * Accepts and starts a stylus handwriting session on the delegate view, if handwriting
* initiation delegation was previously requested using {@link
* #prepareStylusHandwritingDelegation(View, String)} from the delegator and the view belongs to
* a specified delegate package.
@@ -2737,6 +2896,8 @@ public final class InputMethodManager {
* @param delegateView delegate view capable of receiving input via {@link InputConnection}
* @param delegatorPackageName package name of the delegator that handled initial stylus stroke.
* @param flags {@link #HANDWRITING_DELEGATE_FLAG_HOME_DELEGATOR_ALLOWED} or {@code 0}
+ * @param executor The executor to run the callback on.
+ * @param callback {@code true>} would be received if delegation was accepted.
* @return {@code true} if view belongs to allowed delegate package declared in {@link
* #prepareStylusHandwritingDelegation(View, String)} and delegation is accepted
* @see #prepareStylusHandwritingDelegation(View, String)
@@ -2749,13 +2910,16 @@ public final class InputMethodManager {
// session to the delegate view.
// @see #startConnectionlessStylusHandwritingForDelegation(View, ResultReceiver,
// CursorAnchorInfo, String)
+ //
@FlaggedApi(FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR)
- public boolean acceptStylusHandwritingDelegation(
+ public void acceptStylusHandwritingDelegation(
@NonNull View delegateView, @NonNull String delegatorPackageName,
- @HandwritingDelegateFlags int flags) {
+ @HandwritingDelegateFlags int flags, @NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<Boolean> callback) {
Objects.requireNonNull(delegatorPackageName);
- return startStylusHandwritingInternal(delegateView, delegatorPackageName, flags);
+ startStylusHandwritingInternal(
+ delegateView, delegatorPackageName, flags, executor, callback);
}
/**
@@ -3080,14 +3244,52 @@ public final class InputMethodManager {
final int targetUserId = editorInfo.targetInputMethodUser != null
? editorInfo.targetInputMethodUser.getIdentifier() : UserHandle.myUserId();
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMM.startInputOrWindowGainedFocus");
- res = IInputMethodManagerGlobalInvoker.startInputOrWindowGainedFocus(
- startInputReason, mClient, windowGainingFocus, startInputFlags,
- softInputMode, windowFlags, editorInfo, servedInputConnection,
- servedInputConnection == null ? null
- : servedInputConnection.asIRemoteAccessibilityInputConnection(),
- view.getContext().getApplicationInfo().targetSdkVersion, targetUserId,
- mImeDispatcher);
+
+ int startInputSeq = -1;
+ if (Flags.useZeroJankProxy()) {
+ // async result delivered via MSG_START_INPUT_RESULT.
+ startInputSeq = IInputMethodManagerGlobalInvoker.startInputOrWindowGainedFocusAsync(
+ startInputReason, mClient, windowGainingFocus, startInputFlags,
+ softInputMode, windowFlags, editorInfo, servedInputConnection,
+ servedInputConnection == null ? null
+ : servedInputConnection.asIRemoteAccessibilityInputConnection(),
+ view.getContext().getApplicationInfo().targetSdkVersion, targetUserId,
+ mImeDispatcher);
+ } else {
+ res = IInputMethodManagerGlobalInvoker.startInputOrWindowGainedFocus(
+ startInputReason, mClient, windowGainingFocus, startInputFlags,
+ softInputMode, windowFlags, editorInfo, servedInputConnection,
+ servedInputConnection == null ? null
+ : servedInputConnection.asIRemoteAccessibilityInputConnection(),
+ view.getContext().getApplicationInfo().targetSdkVersion, targetUserId,
+ mImeDispatcher);
+ }
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ if (Flags.useZeroJankProxy()) {
+ // Create a runnable for delayed notification to the app that the InputConnection is
+ // initialized and ready for use.
+ if (ic != null) {
+ final int seqId = startInputSeq;
+ mReportInputConnectionOpenedRunner =
+ new ReportInputConnectionOpenedRunner(startInputSeq) {
+ @Override
+ public void run() {
+ if (DEBUG) {
+ Log.v(TAG, "Calling View.onInputConnectionOpened: view= "
+ + view
+ + ", ic=" + ic + ", editorInfo=" + editorInfo
+ + ", handler="
+ + icHandler + ", startInputSeq=" + seqId);
+ }
+ reportInputConnectionOpened(ic, editorInfo, icHandler, view);
+ }
+ };
+ } else {
+ mReportInputConnectionOpenedRunner = null;
+ }
+ return true;
+ }
+
if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
if (res == null) {
Log.wtf(TAG, "startInputOrWindowGainedFocus must not return"
@@ -3118,6 +3320,7 @@ public final class InputMethodManager {
} else if (res.channel != null && res.channel != mCurChannel) {
res.channel.dispose();
}
+
switch (res.result) {
case InputBindResult.ResultCode.ERROR_NOT_IME_TARGET_WINDOW:
mRestartOnNextWindowFocus = true;
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index 55986e7ada47..8d3920f8b1da 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -78,3 +78,11 @@ flag {
bug: "300979854"
is_fixed_read_only: true
}
+
+flag {
+ name: "predictive_back_ime"
+ namespace: "input_method"
+ description: "Predictive back animation for IMEs"
+ bug: "322836622"
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/view/textclassifier/TextClassificationConstants.java b/core/java/android/view/textclassifier/TextClassificationConstants.java
index d0ed8eef7749..7dd77191cb08 100644
--- a/core/java/android/view/textclassifier/TextClassificationConstants.java
+++ b/core/java/android/view/textclassifier/TextClassificationConstants.java
@@ -137,10 +137,6 @@ public final class TextClassificationConstants {
properties.getBoolean(
LOCAL_TEXT_CLASSIFIER_ENABLED,
LOCAL_TEXT_CLASSIFIER_ENABLED_DEFAULT);
- sSystemTextClassifierEnabled =
- properties.getBoolean(
- SYSTEM_TEXT_CLASSIFIER_ENABLED,
- SYSTEM_TEXT_CLASSIFIER_ENABLED_DEFAULT);
sModelDarkLaunchEnabled =
properties.getBoolean(
MODEL_DARK_LAUNCH_ENABLED,
@@ -199,8 +195,11 @@ public final class TextClassificationConstants {
}
public boolean isSystemTextClassifierEnabled() {
- ensureMemoizedValues();
- return sSystemTextClassifierEnabled;
+ // Don't memoize this value because we want to be able to receive config
+ // updates at runtime.
+ return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+ SYSTEM_TEXT_CLASSIFIER_ENABLED,
+ SYSTEM_TEXT_CLASSIFIER_ENABLED_DEFAULT);
}
public boolean isModelDarkLaunchEnabled() {
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 57d268ced6f4..139ebc38706e 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -19,6 +19,8 @@ package android.widget;
import static android.view.ContentInfo.SOURCE_DRAG_AND_DROP;
import static android.widget.TextView.ACCESSIBILITY_ACTION_SMART_START_ID;
+import static com.android.graphics.hwui.flags.Flags.highContrastTextSmallTextRect;
+
import android.R;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
@@ -2151,8 +2153,15 @@ public class Editor {
int lastLine = TextUtils.unpackRangeEndFromLong(lineRange);
if (lastLine < 0) return;
- layout.drawWithoutText(canvas, highlightPaths, highlightPaints, selectionHighlight,
- selectionHighlightPaint, cursorOffsetVertical, firstLine, lastLine);
+ boolean shouldDrawHighlightsOnTop = highContrastTextSmallTextRect()
+ && canvas.isHighContrastTextEnabled();
+
+ if (!shouldDrawHighlightsOnTop) {
+ layout.drawWithoutText(canvas, highlightPaths, highlightPaints, selectionHighlight,
+ selectionHighlightPaint, cursorOffsetVertical, firstLine, lastLine);
+ } else {
+ layout.drawBackground(canvas, firstLine, lastLine);
+ }
if (layout instanceof DynamicLayout) {
if (mTextRenderNodes == null) {
@@ -2226,6 +2235,11 @@ public class Editor {
// Boring layout is used for empty and hint text
layout.drawText(canvas, firstLine, lastLine);
}
+
+ if (shouldDrawHighlightsOnTop) {
+ layout.drawHighlights(canvas, highlightPaths, highlightPaints, selectionHighlight,
+ selectionHighlightPaint, cursorOffsetVertical, firstLine, lastLine);
+ }
}
private int drawHardwareAcceleratedInner(Canvas canvas, Layout layout, Path highlight,
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 5bf1b5ba6cec..13dc4efb374d 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -112,6 +112,7 @@ import android.widget.CompoundButton.OnCheckedChangeListener;
import com.android.internal.R;
import com.android.internal.util.Preconditions;
import com.android.internal.widget.IRemoteViewsFactory;
+import com.android.internal.widget.remotecompose.core.operations.Theme;
import com.android.internal.widget.remotecompose.player.RemoteComposeDocument;
import com.android.internal.widget.remotecompose.player.RemoteComposePlayer;
@@ -3902,7 +3903,6 @@ public class RemoteViews implements Parcelable, Filter {
throws ActionException {
if (drawDataParcel() && mInstructions != null
&& root instanceof RemoteComposePlayer player) {
- player.setTag(mInstructions);
final List<byte[]> bytes = mInstructions.mInstructions;
if (bytes.isEmpty()) {
return;
@@ -6082,20 +6082,24 @@ public class RemoteViews implements Parcelable, Filter {
if (applyThemeResId != 0) {
inflationContext = new ContextThemeWrapper(inflationContext, applyThemeResId);
}
+ View v;
// If the RemoteViews contains draw instructions, just use it instead.
if (rv.hasDrawInstructions()) {
- return new RemoteComposePlayer(inflationContext);
- }
- LayoutInflater inflater = LayoutInflater.from(context);
+ final RemoteComposePlayer player = new RemoteComposePlayer(inflationContext);
+ player.setDebug(Build.IS_USERDEBUG || Build.IS_ENG ? 1 : 0);
+ v = player;
+ } else {
+ LayoutInflater inflater = LayoutInflater.from(context);
- // Clone inflater so we load resources from correct context and
- // we don't add a filter to the static version returned by getSystemService.
- inflater = inflater.cloneInContext(inflationContext);
- inflater.setFilter(shouldUseStaticFilter() ? INFLATER_FILTER : this);
- if (mLayoutInflaterFactory2 != null) {
- inflater.setFactory2(mLayoutInflaterFactory2);
+ // Clone inflater so we load resources from correct context and
+ // we don't add a filter to the static version returned by getSystemService.
+ inflater = inflater.cloneInContext(inflationContext);
+ inflater.setFilter(shouldUseStaticFilter() ? INFLATER_FILTER : this);
+ if (mLayoutInflaterFactory2 != null) {
+ inflater.setFactory2(mLayoutInflaterFactory2);
+ }
+ v = inflater.inflate(rv.getLayoutId(), parent, false);
}
- View v = inflater.inflate(rv.getLayoutId(), parent, false);
if (mViewId != View.NO_ID) {
v.setId(mViewId);
v.setTagInternal(R.id.remote_views_override_id, mViewId);
@@ -6441,6 +6445,10 @@ public class RemoteViews implements Parcelable, Filter {
if (params.handler == null) {
params.handler = DEFAULT_INTERACTION_HANDLER;
}
+ if (v instanceof RemoteComposePlayer player) {
+ player.setTheme(v.getResources().getConfiguration().isNightModeActive()
+ ? Theme.DARK : Theme.LIGHT);
+ }
if (mActions != null) {
final int count = mActions.size();
for (int i = 0; i < count; i++) {
@@ -7565,6 +7573,8 @@ public class RemoteViews implements Parcelable, Filter {
@FlaggedApi(FLAG_DRAW_DATA_PARCEL)
public static final class DrawInstructions {
+ private static final long VERSION = 1L;
+
@NonNull
final List<byte[]> mInstructions;
@@ -7599,6 +7609,7 @@ public class RemoteViews implements Parcelable, Filter {
}
return new DrawInstructions(instructions);
}
+
private static void writeToParcel(@Nullable final DrawInstructions drawInstructions,
@NonNull final Parcel dest, final int flags) {
if (drawInstructions == null) {
@@ -7614,6 +7625,14 @@ public class RemoteViews implements Parcelable, Filter {
}
/**
+ * Version number of {@link DrawInstructions} currently supported.
+ */
+ @FlaggedApi(FLAG_DRAW_DATA_PARCEL)
+ public static long getSupportedVersion() {
+ return VERSION;
+ }
+
+ /**
* Builder class for {@link DrawInstructions} objects.
*/
@FlaggedApi(FLAG_DRAW_DATA_PARCEL)
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 57e4e6a2fa5b..172146215257 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -181,6 +181,7 @@ import android.view.PointerIcon;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewDebug;
+import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewHierarchyEncoder;
import android.view.ViewParent;
@@ -866,6 +867,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private final boolean mUseTextPaddingForUiTranslation;
private boolean mUseBoundsForWidth;
+ private boolean mShiftDrawingOffsetForStartOverhang;
@Nullable private Paint.FontMetrics mMinimumFontMetrics;
@Nullable private Paint.FontMetrics mLocalePreferredFontMetrics;
private boolean mUseLocalePreferredLineHeightForMinimum;
@@ -1621,6 +1623,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
hasUseBoundForWidthValue = true;
break;
case com.android.internal.R.styleable
+ .TextView_shiftDrawingOffsetForStartOverhang:
+ mShiftDrawingOffsetForStartOverhang = a.getBoolean(attr, false);
+ break;
+ case com.android.internal.R.styleable
.TextView_useLocalePreferredLineHeightForMinimum:
mUseLocalePreferredLineHeightForMinimum = a.getBoolean(attr, false);
break;
@@ -4922,6 +4928,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* @param useBoundsForWidth true for using bounding box for width. false for using advances for
* width.
* @see #getUseBoundsForWidth()
+ * @see #setShiftDrawingOffsetForStartOverhang(boolean)
+ * @see #getShiftDrawingOffsetForStartOverhang()
*/
@FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
public void setUseBoundsForWidth(boolean useBoundsForWidth) {
@@ -4939,6 +4947,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* Returns true if using bounding box as a width, false for using advance as a width.
*
* @see #setUseBoundsForWidth(boolean)
+ * @see #setShiftDrawingOffsetForStartOverhang(boolean)
+ * @see #getShiftDrawingOffsetForStartOverhang()
* @return True if using bounding box for width, false if using advance for width.
*/
@FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
@@ -4947,6 +4957,53 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
/**
+ * Set true for shifting the drawing x offset for showing overhang at the start position.
+ *
+ * This flag is ignored if the {@link #getUseBoundsForWidth()} is false.
+ *
+ * If this value is false, the TextView draws text from the zero even if there is a glyph stroke
+ * in a region where the x coordinate is negative. TextView clips the stroke in the region where
+ * the X coordinate is negative unless the parents has {@link ViewGroup#getClipChildren()} to
+ * true. This is useful for aligning multiple TextViews vertically.
+ *
+ * If this value is true, the TextView draws text with shifting the x coordinate of the drawing
+ * bounding box. This prevents the clipping even if the parents doesn't have
+ * {@link ViewGroup#getClipChildren()} to true.
+ *
+ * This value is false by default.
+ *
+ * @param shiftDrawingOffsetForStartOverhang true for shifting the drawing offset for showing
+ * the stroke that is in the region whre the x
+ * coorinate is negative.
+ * @see #setUseBoundsForWidth(boolean)
+ * @see #getUseBoundsForWidth()
+ */
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
+ public void setShiftDrawingOffsetForStartOverhang(boolean shiftDrawingOffsetForStartOverhang) {
+ if (mShiftDrawingOffsetForStartOverhang != shiftDrawingOffsetForStartOverhang) {
+ mShiftDrawingOffsetForStartOverhang = shiftDrawingOffsetForStartOverhang;
+ if (mLayout != null) {
+ nullLayouts();
+ requestLayout();
+ invalidate();
+ }
+ }
+ }
+
+ /**
+ * Returns true if shifting the drawing x offset for start overhang.
+ *
+ * @see #setShiftDrawingOffsetForStartOverhang(boolean)
+ * @see #setUseBoundsForWidth(boolean)
+ * @see #getUseBoundsForWidth()
+ * @return True if shifting the drawing x offset for start overhang.
+ */
+ @FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
+ public boolean getShiftDrawingOffsetForStartOverhang() {
+ return mShiftDrawingOffsetForStartOverhang;
+ }
+
+ /**
* Set the minimum font metrics used for line spacing.
*
* <p>
@@ -5456,6 +5513,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
*
* @attr ref android.R.styleable#TextView_fontVariationSettings
*/
+ @android.view.RemotableViewMethod
public boolean setFontVariationSettings(@Nullable String fontVariationSettings) {
final String existingSettings = mTextPaint.getFontVariationSettings();
if (fontVariationSettings == existingSettings
@@ -11001,6 +11059,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
null,
boring,
mUseBoundsForWidth,
+ mShiftDrawingOffsetForStartOverhang,
getResolvedMinimumFontMetrics());
}
@@ -11028,6 +11087,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
effectiveEllipsize,
boring,
mUseBoundsForWidth,
+ mShiftDrawingOffsetForStartOverhang,
getResolvedMinimumFontMetrics());
}
}
diff --git a/core/java/android/window/IUnhandledDragListener.aidl b/core/java/android/window/IGlobalDragListener.aidl
index 52e98952971d..8f2ca02b3c09 100644
--- a/core/java/android/window/IUnhandledDragListener.aidl
+++ b/core/java/android/window/IGlobalDragListener.aidl
@@ -16,14 +16,21 @@
package android.window;
+import android.app.ActivityManager;
import android.view.DragEvent;
import android.window.IUnhandledDragCallback;
/**
- * An interface to a handler for global drags that are not consumed (ie. not handled by any window).
+ * An interface to a handler for global drags.
* {@hide}
*/
-oneway interface IUnhandledDragListener {
+oneway interface IGlobalDragListener {
+ /**
+ * Called when a cross-window drag is handled by another window.
+ * @param taskInfo the task containing the window that consumed the drop
+ */
+ void onCrossWindowDrop(in ActivityManager.RunningTaskInfo taskInfo);
+
/**
* Called when the user finishes the drag gesture but no windows have reported handling the
* drop. The DragEvent is populated with the drag surface for the listener to animate. The
diff --git a/core/java/android/window/InputTransferToken.java b/core/java/android/window/InputTransferToken.java
index 0601b2a79268..e572853e5d5d 100644
--- a/core/java/android/window/InputTransferToken.java
+++ b/core/java/android/window/InputTransferToken.java
@@ -16,20 +16,40 @@
package android.window;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.os.Binder;
import android.os.IBinder;
+import android.os.Looper;
import android.os.Parcel;
import android.os.Parcelable;
+import android.view.Choreographer;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlInputReceiver;
import android.view.SurfaceControlViewHost;
+import com.android.window.flags.Flags;
+
import java.util.Objects;
/**
* A token that can be used to request focus on or to transfer touch gesture to a
* {@link SurfaceControlViewHost} or {@link android.view.SurfaceControl} that has an input channel.
- * @hide
+ * <p>
+ * The {@link android.view.SurfaceControl} needs to have been registered for input via
+ * {@link android.view.WindowManager#registerUnbatchedSurfaceControlInputReceiver(int,
+ * InputTransferToken, SurfaceControl, Looper, SurfaceControlInputReceiver)} or
+ * {@link android.view.WindowManager#registerBatchedSurfaceControlInputReceiver(int,
+ * InputTransferToken, SurfaceControl, Choreographer, SurfaceControlInputReceiver)} and the
+ * returned token can be used to call
+ * {@link android.view.WindowManager#transferTouchGesture(InputTransferToken, InputTransferToken)}
+ * <p>
+ * For {@link SurfaceControlViewHost}, the token can be retrieved via
+ * {@link SurfaceControlViewHost.SurfacePackage#getInputTransferToken()}
+ *
+ * @see android.view.WindowManager#transferTouchGesture(InputTransferToken, InputTransferToken)
*/
+@FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER)
public final class InputTransferToken implements Parcelable {
/**
* @hide
diff --git a/core/java/android/window/TrustedPresentationThresholds.java b/core/java/android/window/TrustedPresentationThresholds.java
index 90f8834b37d1..a30c8fa2c63c 100644
--- a/core/java/android/window/TrustedPresentationThresholds.java
+++ b/core/java/android/window/TrustedPresentationThresholds.java
@@ -19,15 +19,15 @@ package android.window;
import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.IntRange;
-import android.annotation.SuppressLint;
import android.os.Parcel;
import android.os.Parcelable;
-import android.view.SurfaceControl;
import androidx.annotation.NonNull;
import com.android.window.flags.Flags;
+import java.util.Objects;
+
/**
* Threshold values that are sent with
* {@link android.view.WindowManager#registerTrustedPresentationListener(IBinder,
@@ -36,33 +36,53 @@ import com.android.window.flags.Flags;
@FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
public final class TrustedPresentationThresholds implements Parcelable {
/**
- * The min alpha the {@link SurfaceControl} is required to have to be considered inside the
+ * The min alpha the Window is required to have to be considered inside the
* threshold.
*/
@FloatRange(from = 0f, fromInclusive = false, to = 1f)
- @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
- @SuppressLint("InternalField") // simple data class
- public final float minAlpha;
+ private final float mMinAlpha;
/**
- * The min fraction of the SurfaceControl that was presented to the user to be considered
+ * The min fraction of the Window that was presented to the user to be considered
* inside the threshold.
*/
@FloatRange(from = 0f, fromInclusive = false, to = 1f)
- @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
- @SuppressLint("InternalField") // simple data class
- public final float minFractionRendered;
+ private final float mMinFractionRendered;
/**
- * The time in milliseconds required for the {@link SurfaceControl} to be in the threshold.
+ * The time in milliseconds required for the Window to be in the threshold.
*/
@IntRange(from = 1)
+ private final int mStabilityRequirementMs;
+
+ /**
+ * The min alpha the Window is required to have to be considered inside the
+ * threshold.
+ */
+ @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
+ public @FloatRange(from = 0f, fromInclusive = false, to = 1f) float getMinAlpha() {
+ return mMinAlpha;
+ }
+
+ /**
+ * The min fraction of the Window that was presented to the user to be considered
+ * inside the threshold.
+ */
+ @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
+ public @FloatRange(from = 0f, fromInclusive = false, to = 1f) float getMinFractionRendered() {
+ return mMinFractionRendered;
+ }
+
+ /**
+ * The time in milliseconds required for the Window to be in the threshold.
+ */
@FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
- @SuppressLint("InternalField") // simple data class
- public final int stabilityRequirementMs;
+ public @IntRange(from = 1) int getStabilityRequirementMillis() {
+ return mStabilityRequirementMs;
+ }
private void checkValid() {
- if (minAlpha <= 0 || minFractionRendered <= 0 || stabilityRequirementMs < 1) {
+ if (mMinAlpha <= 0 || mMinFractionRendered <= 0 || mStabilityRequirementMs < 1) {
throw new IllegalArgumentException(
"TrustedPresentationThresholds values are invalid");
}
@@ -71,23 +91,23 @@ public final class TrustedPresentationThresholds implements Parcelable {
/**
* Creates a new TrustedPresentationThresholds.
*
- * @param minAlpha The min alpha the {@link SurfaceControl} is required to
+ * @param minAlpha The min alpha the Window is required to
* have to be considered inside the
* threshold.
- * @param minFractionRendered The min fraction of the SurfaceControl that was presented
+ * @param minFractionRendered The min fraction of the Window that was presented
* to the user to be considered
* inside the threshold.
* @param stabilityRequirementMs The time in milliseconds required for the
- * {@link SurfaceControl} to be in the threshold.
+ * Window to be in the threshold.
*/
@FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
public TrustedPresentationThresholds(
@FloatRange(from = 0f, fromInclusive = false, to = 1f) float minAlpha,
@FloatRange(from = 0f, fromInclusive = false, to = 1f) float minFractionRendered,
@IntRange(from = 1) int stabilityRequirementMs) {
- this.minAlpha = minAlpha;
- this.minFractionRendered = minFractionRendered;
- this.stabilityRequirementMs = stabilityRequirementMs;
+ this.mMinAlpha = minAlpha;
+ this.mMinFractionRendered = minFractionRendered;
+ this.mStabilityRequirementMs = stabilityRequirementMs;
checkValid();
}
@@ -95,18 +115,18 @@ public final class TrustedPresentationThresholds implements Parcelable {
@FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
public String toString() {
return "TrustedPresentationThresholds { "
- + "minAlpha = " + minAlpha + ", "
- + "minFractionRendered = " + minFractionRendered + ", "
- + "stabilityRequirementMs = " + stabilityRequirementMs
+ + "minAlpha = " + mMinAlpha + ", "
+ + "minFractionRendered = " + mMinFractionRendered + ", "
+ + "stabilityRequirementMs = " + mStabilityRequirementMs
+ " }";
}
@Override
@FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeFloat(minAlpha);
- dest.writeFloat(minFractionRendered);
- dest.writeInt(stabilityRequirementMs);
+ dest.writeFloat(mMinAlpha);
+ dest.writeFloat(mMinFractionRendered);
+ dest.writeInt(mStabilityRequirementMs);
}
@Override
@@ -115,13 +135,34 @@ public final class TrustedPresentationThresholds implements Parcelable {
return 0;
}
+
+ @Override
+ @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
+ public int hashCode() {
+ return Objects.hash(mMinAlpha, mMinFractionRendered, mStabilityRequirementMs);
+ }
+
+ @Override
+ @FlaggedApi(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof TrustedPresentationThresholds that)) {
+ return false;
+ }
+ return mMinAlpha == that.mMinAlpha
+ && mMinFractionRendered == that.mMinFractionRendered
+ && mStabilityRequirementMs == that.mStabilityRequirementMs;
+ }
+
/**
* @hide
*/
TrustedPresentationThresholds(@NonNull Parcel in) {
- minAlpha = in.readFloat();
- minFractionRendered = in.readFloat();
- stabilityRequirementMs = in.readInt();
+ mMinAlpha = in.readFloat();
+ mMinFractionRendered = in.readFloat();
+ mStabilityRequirementMs = in.readInt();
checkValid();
}
diff --git a/core/java/android/window/flags/OWNERS b/core/java/android/window/flags/OWNERS
index fa81ee3905c3..3fa376003123 100644
--- a/core/java/android/window/flags/OWNERS
+++ b/core/java/android/window/flags/OWNERS
@@ -1 +1,2 @@
-per-file responsible_apis.aconfig = file:/BAL_OWNERS \ No newline at end of file
+per-file responsible_apis.aconfig = file:/BAL_OWNERS
+per-file large_screen_experiences_app_compat.aconfig = file:/LSE_APP_COMPAT_OWNERS
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
new file mode 100644
index 000000000000..4e8661652e85
--- /dev/null
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -0,0 +1,9 @@
+package: "com.android.window.flags"
+
+flag {
+ name: "enable_scaled_resizing"
+ namespace: "lse_desktop_experience"
+ description: "Enable the resizing of un-resizable apps through scaling their bounds up/down"
+ bug: "320350734"
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig
index 069affb4c06c..8b3bd974b3fa 100644
--- a/core/java/android/window/flags/window_surfaces.aconfig
+++ b/core/java/android/window/flags/window_surfaces.aconfig
@@ -20,21 +20,6 @@ flag {
flag {
namespace: "window_surfaces"
- name: "get_host_token_api"
- description: "Feature flag to associate the host and embedded windows"
- is_fixed_read_only: true
- bug: "304508760"
-}
-
-flag {
- namespace: "window_surfaces"
- name: "transfer_gesture_to_embedded"
- description: "Enable public API for Window Surfaces"
- bug: "287076178"
-}
-
-flag {
- namespace: "window_surfaces"
name: "delete_capture_display"
description: "Delete uses of ScreenCapture#captureDisplay"
is_fixed_read_only: true
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index bc63881163f0..ce74848705e4 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -69,4 +69,12 @@ flag {
name: "embedded_activity_back_nav_flag"
description: "Refines embedded activity back navigation behavior"
bug: "293642394"
+}
+
+flag {
+ namespace: "windowing_sdk"
+ name: "cover_display_opt_in"
+ description: "Properties to allow apps and activities to opt-in to cover display rendering"
+ bug: "312530526"
+ is_fixed_read_only: true
} \ No newline at end of file
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
index 51a5ddfa8dd6..3d3db47faddb 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
@@ -111,8 +111,9 @@ public final class AccessibilityTargetHelper {
* @param context The context of the application.
* @param shortcutType The shortcut type.
* @return The list of {@link AccessibilityTarget}.
+ * @hide
*/
- static List<AccessibilityTarget> getInstalledTargets(Context context,
+ public static List<AccessibilityTarget> getInstalledTargets(Context context,
@ShortcutType int shortcutType) {
final List<AccessibilityTarget> targets = new ArrayList<>();
targets.addAll(getAccessibilityFilteredTargets(context, shortcutType));
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 3a321e5c26f7..3ec70649294b 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -162,4 +162,5 @@ interface IAppOpsService {
int attributionFlags, int attributionChainId);
void finishOperationForDevice(IBinder clientId, int code, int uid, String packageName,
@nullable String attributionTag, int virtualDeviceId);
+ List<AppOpsManager.PackageOps> getPackagesForOpsForDevice(in int[] ops, String persistentDeviceId);
}
diff --git a/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl b/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl
index ba87caa0697c..5cb5963112a6 100644
--- a/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl
+++ b/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl
@@ -40,6 +40,13 @@ oneway interface IHotwordRecognitionStatusCallback {
in SoundTrigger.KeyphraseRecognitionEvent recognitionEvent,
in HotwordDetectedResult result);
+ /**
+ * Called when the keyphrase is detected from audio coming from an external source.
+ *
+ * @param result Successful detection result payload.
+ */
+ void onKeyphraseDetectedFromExternalSource(in HotwordDetectedResult result);
+
/**
* Called when a generic sound trigger event is witnessed.
*
diff --git a/core/java/com/android/internal/app/IVoiceInteractionAccessibilitySettingsListener.aidl b/core/java/com/android/internal/app/IVoiceInteractionAccessibilitySettingsListener.aidl
new file mode 100644
index 000000000000..a9190353dc66
--- /dev/null
+++ b/core/java/com/android/internal/app/IVoiceInteractionAccessibilitySettingsListener.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+oneway interface IVoiceInteractionAccessibilitySettingsListener {
+ /**
+ * Called when the value of secure setting has changed.
+ */
+ void onAccessibilityDetectionChanged(boolean enable);
+}
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 314ed69cb885..98d393977958 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -35,6 +35,7 @@ import android.service.voice.VisibleActivityInfo;
import com.android.internal.app.IHotwordRecognitionStatusCallback;
import com.android.internal.app.IVoiceActionCheckCallback;
+import com.android.internal.app.IVoiceInteractionAccessibilitySettingsListener;
import com.android.internal.app.IVoiceInteractionSessionListener;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
import com.android.internal.app.IVoiceInteractionSoundTriggerSession;
@@ -382,4 +383,21 @@ interface IVoiceInteractionManagerService {
oneway void notifyActivityEventChanged(
in IBinder activityToken,
int type);
+
+ /**
+ * rely on the system server to get the secure settings
+ */
+ boolean getAccessibilityDetectionEnabled();
+
+ /**
+ * register the listener
+ */
+ oneway void registerAccessibilityDetectionSettingsListener(
+ in IVoiceInteractionAccessibilitySettingsListener listener);
+
+ /**
+ * unregister the listener
+ */
+ oneway void unregisterAccessibilityDetectionSettingsListener(
+ in IVoiceInteractionAccessibilitySettingsListener listener);
}
diff --git a/core/java/com/android/internal/app/NfcResolverActivity.java b/core/java/com/android/internal/app/NfcResolverActivity.java
index 402192abaf0a..78427fe91088 100644
--- a/core/java/com/android/internal/app/NfcResolverActivity.java
+++ b/core/java/com/android/internal/app/NfcResolverActivity.java
@@ -16,25 +16,25 @@
package com.android.internal.app;
-import static android.service.chooser.CustomChoosers.EXTRA_RESOLVE_INFOS;
-import static android.service.chooser.Flags.supportNfcResolver;
+import static android.nfc.Flags.enableNfcMainline;
import android.content.Intent;
import android.content.pm.ResolveInfo;
+import android.nfc.NfcAdapter;
import android.os.Bundle;
import java.util.ArrayList;
/**
* Caller-customizable variant of {@link ResolverActivity} to support the
- * {@link CustomChoosers#showNfcResolver()} API.
+ * NFC resolver intent.
*/
public class NfcResolverActivity extends ResolverActivity {
@Override
@SuppressWarnings("MissingSuperCall") // Called indirectly via `super_onCreate()`.
protected void onCreate(Bundle savedInstanceState) {
- if (!supportNfcResolver()) {
+ if (!enableNfcMainline()) {
super_onCreate(savedInstanceState);
finish();
return;
@@ -43,7 +43,8 @@ public class NfcResolverActivity extends ResolverActivity {
Intent intent = getIntent();
Intent target = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent.class);
ArrayList<ResolveInfo> rList =
- intent.getParcelableArrayListExtra(EXTRA_RESOLVE_INFOS, ResolveInfo.class);
+ intent.getParcelableArrayListExtra(
+ NfcAdapter.EXTRA_RESOLVE_INFOS, ResolveInfo.class);
CharSequence title = intent.getExtras().getCharSequence(
Intent.EXTRA_TITLE,
getResources().getText(com.android.internal.R.string.chooseActivity));
diff --git a/core/java/com/android/internal/app/SetScreenLockDialogActivity.java b/core/java/com/android/internal/app/SetScreenLockDialogActivity.java
new file mode 100644
index 000000000000..93fe37c974b2
--- /dev/null
+++ b/core/java/com/android/internal/app/SetScreenLockDialogActivity.java
@@ -0,0 +1,160 @@
+/*
+ * 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.internal.app;
+
+import static android.Manifest.permission.HIDE_OVERLAY_WINDOWS;
+import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.hardware.biometrics.BiometricManager.Authenticators.DEVICE_CREDENTIAL;
+import static android.provider.Settings.ACTION_BIOMETRIC_ENROLL;
+import static android.provider.Settings.EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED;
+
+import android.Manifest;
+import android.annotation.IntDef;
+import android.annotation.RequiresPermission;
+import android.app.AlertDialog;
+import android.app.KeyguardManager;
+import android.content.ComponentName;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.UserInfo;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+
+import com.android.internal.R;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A dialog shown to the user that prompts them to set the screen lock for the current foreground
+ * user. Should be called from the context of foreground user.
+ */
+public class SetScreenLockDialogActivity extends AlertActivity
+ implements DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
+ private static final String TAG = "SetScreenLockDialog";
+ public static final String EXTRA_LAUNCH_REASON = "launch_reason";
+ /**
+ * User id associated with the workflow that wants to launch the prompt to set up the
+ * screen lock
+ */
+ public static final String EXTRA_ORIGIN_USER_ID = "origin_user_id";
+ private static final String PACKAGE_NAME = "android";
+ @IntDef(prefix = "LAUNCH_REASON_", value = {
+ LAUNCH_REASON_PRIVATE_SPACE_SETTINGS_ACCESS,
+ LAUNCH_REASON_DISABLE_QUIET_MODE,
+ LAUNCH_REASON_UNKNOWN,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface LaunchReason {
+ }
+ public static final int LAUNCH_REASON_UNKNOWN = -1;
+ public static final int LAUNCH_REASON_DISABLE_QUIET_MODE = 1;
+ public static final int LAUNCH_REASON_PRIVATE_SPACE_SETTINGS_ACCESS = 2;
+ private @LaunchReason int mReason;
+ private int mOriginUserId;
+
+ @Override
+ @RequiresPermission(HIDE_OVERLAY_WINDOWS)
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (!(android.os.Flags.allowPrivateProfile()
+ && android.multiuser.Flags.showSetScreenLockDialog())) {
+ finish();
+ return;
+ }
+ Intent intent = getIntent();
+ mReason = intent.getIntExtra(EXTRA_LAUNCH_REASON, LAUNCH_REASON_UNKNOWN);
+ mOriginUserId = intent.getIntExtra(EXTRA_ORIGIN_USER_ID, UserHandle.USER_NULL);
+
+ if (mReason == LAUNCH_REASON_UNKNOWN) {
+ Log.e(TAG, "Invalid launch reason: " + mReason);
+ finish();
+ return;
+ }
+
+ final KeyguardManager km = getSystemService(KeyguardManager.class);
+ if (km == null) {
+ Log.e(TAG, "Error fetching keyguard manager");
+ return;
+ }
+ if (km.isDeviceSecure()) {
+ Log.w(TAG, "Closing the activity since screen lock is already set");
+ return;
+ }
+
+ Log.d(TAG, "Launching screen lock setup dialog due to " + mReason);
+ final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.set_up_screen_lock_title)
+ .setOnDismissListener(this)
+ .setPositiveButton(R.string.set_up_screen_lock_action_label, this)
+ .setNegativeButton(R.string.cancel, this);
+ setLaunchUserSpecificMessage(builder);
+ final AlertDialog dialog = builder.create();
+ dialog.create();
+ getWindow().setHideOverlayWindows(true);
+ dialog.getButton(DialogInterface.BUTTON_POSITIVE).setFilterTouchesWhenObscured(true);
+ dialog.show();
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ finish();
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ Intent setNewLockIntent = new Intent(ACTION_BIOMETRIC_ENROLL);
+ setNewLockIntent.putExtra(EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED, DEVICE_CREDENTIAL);
+ startActivity(setNewLockIntent);
+ } else {
+ finish();
+ }
+ }
+
+ @RequiresPermission(anyOf = {
+ Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS,
+ Manifest.permission.QUERY_USERS})
+ private void setLaunchUserSpecificMessage(AlertDialog.Builder builder) {
+ if (mReason == LAUNCH_REASON_PRIVATE_SPACE_SETTINGS_ACCESS) {
+ // Always set private space message if launch reason is specific to private space
+ builder.setMessage(R.string.private_space_set_up_screen_lock_message);
+ return;
+ }
+ final UserManager userManager = getApplicationContext().getSystemService(UserManager.class);
+ if (userManager != null) {
+ UserInfo userInfo = userManager.getUserInfo(mOriginUserId);
+ if (userInfo != null && userInfo.isPrivateProfile()) {
+ builder.setMessage(R.string.private_space_set_up_screen_lock_message);
+ }
+ }
+ }
+
+ /** Returns a basic intent to display the screen lock dialog */
+ public static Intent createBaseIntent(@LaunchReason int launchReason) {
+ Intent intent = new Intent();
+ intent.setComponent(new ComponentName(PACKAGE_NAME,
+ SetScreenLockDialogActivity.class.getName()));
+ intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ intent.putExtra(EXTRA_LAUNCH_REASON, launchReason);
+ return intent;
+ }
+}
diff --git a/core/java/com/android/internal/app/UnlaunchableAppActivity.java b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
index 73914a2a99f6..4ef0a1baa4d1 100644
--- a/core/java/com/android/internal/app/UnlaunchableAppActivity.java
+++ b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
@@ -67,6 +67,8 @@ public class UnlaunchableAppActivity extends Activity
mUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
mTarget = intent.getParcelableExtra(Intent.EXTRA_INTENT,
android.content.IntentSender.class);
+ String targetPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
+ final UserManager userManager = UserManager.get(this);
if (mUserId == UserHandle.USER_NULL) {
Log.wtf(TAG, "Invalid user id: " + mUserId + ". Stopping.");
@@ -74,13 +76,20 @@ public class UnlaunchableAppActivity extends Activity
return;
}
+ if (android.os.Flags.allowPrivateProfile()
+ && !userManager.isManagedProfile(mUserId)) {
+ Log.e(TAG, "Unlaunchable activity for target package " + targetPackageName
+ + " called for a non-managed-profile " + mUserId);
+ finish();
+ return;
+ }
+
if (mReason != UNLAUNCHABLE_REASON_QUIET_MODE) {
Log.wtf(TAG, "Invalid unlaunchable type: " + mReason);
finish();
return;
}
- String targetPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
boolean showEmergencyCallButton =
(targetPackageName != null && targetPackageName.equals(
mTelecomManager.getDefaultDialerPackage(UserHandle.of(mUserId))));
diff --git a/core/java/com/android/internal/colorextraction/OWNERS b/core/java/com/android/internal/colorextraction/OWNERS
index ffade1ec4ebd..041559cd048d 100644
--- a/core/java/com/android/internal/colorextraction/OWNERS
+++ b/core/java/com/android/internal/colorextraction/OWNERS
@@ -1,3 +1,2 @@
-dupin@google.com
cinek@google.com
-jamesoleary@google.com
+arteiro@google.com
diff --git a/core/java/com/android/internal/content/ReferrerIntent.java b/core/java/com/android/internal/content/ReferrerIntent.java
index 6af03dd29899..2c68203d9cae 100644
--- a/core/java/com/android/internal/content/ReferrerIntent.java
+++ b/core/java/com/android/internal/content/ReferrerIntent.java
@@ -18,6 +18,7 @@ package com.android.internal.content;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Intent;
+import android.os.IBinder;
import android.os.Parcel;
import java.util.Objects;
@@ -29,20 +30,29 @@ public class ReferrerIntent extends Intent {
@UnsupportedAppUsage
public final String mReferrer;
+ public final IBinder mCallerToken;
+
@UnsupportedAppUsage
public ReferrerIntent(Intent baseIntent, String referrer) {
+ this(baseIntent, referrer, /* callerToken */ null);
+ }
+
+ public ReferrerIntent(Intent baseIntent, String referrer, IBinder callerToken) {
super(baseIntent);
mReferrer = referrer;
+ mCallerToken = callerToken;
}
public void writeToParcel(Parcel dest, int parcelableFlags) {
super.writeToParcel(dest, parcelableFlags);
dest.writeString(mReferrer);
+ dest.writeStrongBinder(mCallerToken);
}
ReferrerIntent(Parcel in) {
readFromParcel(in);
mReferrer = in.readString();
+ mCallerToken = in.readStrongBinder();
}
public static final Creator<ReferrerIntent> CREATOR = new Creator<ReferrerIntent>() {
@@ -60,7 +70,8 @@ public class ReferrerIntent extends Intent {
return false;
}
final ReferrerIntent other = (ReferrerIntent) obj;
- return filterEquals(other) && Objects.equals(mReferrer, other.mReferrer);
+ return filterEquals(other) && Objects.equals(mReferrer, other.mReferrer)
+ && Objects.equals(mCallerToken, other.mCallerToken);
}
@Override
@@ -68,6 +79,7 @@ public class ReferrerIntent extends Intent {
int result = 17;
result = 31 * result + filterHashCode();
result = 31 * result + Objects.hashCode(mReferrer);
+ result = 31 * result + Objects.hashCode(mCallerToken);
return result;
}
}
diff --git a/core/java/com/android/internal/display/RefreshRateSettingsUtils.java b/core/java/com/android/internal/display/RefreshRateSettingsUtils.java
index fab8984ce067..c23a5012923b 100644
--- a/core/java/com/android/internal/display/RefreshRateSettingsUtils.java
+++ b/core/java/com/android/internal/display/RefreshRateSettingsUtils.java
@@ -16,6 +16,8 @@
package com.android.internal.display;
+import static android.hardware.display.DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED;
+
import android.content.Context;
import android.hardware.display.DisplayManager;
import android.util.Log;
@@ -54,4 +56,31 @@ public class RefreshRateSettingsUtils {
}
return maxRefreshRate;
}
+
+ /**
+ * Find the highest refresh rate among all the modes of all the displays.
+ *
+ * This method will acquire DisplayManager.mLock, so calling it while holding other locks
+ * should be done with care.
+ * @param context The context
+ * @return The highest refresh rate
+ */
+ public static float findHighestRefreshRateAmongAllDisplays(Context context) {
+ final DisplayManager dm = context.getSystemService(DisplayManager.class);
+ final Display[] displays = dm.getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED);
+ if (displays.length == 0) {
+ Log.w(TAG, "No valid display devices");
+ return DEFAULT_REFRESH_RATE;
+ }
+
+ float maxRefreshRate = DEFAULT_REFRESH_RATE;
+ for (Display display : displays) {
+ for (Display.Mode mode : display.getSupportedModes()) {
+ if (mode.getRefreshRate() > maxRefreshRate) {
+ maxRefreshRate = mode.getRefreshRate();
+ }
+ }
+ }
+ return maxRefreshRate;
+ }
}
diff --git a/core/java/com/android/internal/inputmethod/IBooleanListener.aidl b/core/java/com/android/internal/inputmethod/IBooleanListener.aidl
new file mode 100644
index 000000000000..8830b1c2f21a
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/IBooleanListener.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.inputmethod;
+
+/**
+ * Interface for providing a Boolean result.
+ */
+oneway interface IBooleanListener
+{
+ void onResult(boolean value);
+} \ No newline at end of file
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodClient.aidl b/core/java/com/android/internal/inputmethod/IInputMethodClient.aidl
index 9251d2d5dc31..babd9a0950fd 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethodClient.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethodClient.aidl
@@ -24,6 +24,7 @@ import com.android.internal.inputmethod.InputBindResult;
*/
oneway interface IInputMethodClient {
void onBindMethod(in InputBindResult res);
+ void onStartInputResult(in InputBindResult res, int startInputSeq);
void onBindAccessibilityService(in InputBindResult res, int id);
void onUnbindMethod(int sequence, int unbindReason);
void onUnbindAccessibilityService(int sequence, int id);
diff --git a/core/java/com/android/internal/inputmethod/InputBindResult.java b/core/java/com/android/internal/inputmethod/InputBindResult.java
index b6eca07a0858..243b1031bd4b 100644
--- a/core/java/com/android/internal/inputmethod/InputBindResult.java
+++ b/core/java/com/android/internal/inputmethod/InputBindResult.java
@@ -271,6 +271,7 @@ public final class InputBindResult implements Parcelable {
public String toString() {
return "InputBindResult{result=" + getResultString() + " method=" + method + " id=" + id
+ " sequence=" + sequence
+ + " result=" + result
+ " isInputMethodSuppressingSpellChecker=" + isInputMethodSuppressingSpellChecker
+ "}";
}
diff --git a/core/java/com/android/internal/jank/Cuj.java b/core/java/com/android/internal/jank/Cuj.java
index 48c455aa70f2..3662d69e1974 100644
--- a/core/java/com/android/internal/jank/Cuj.java
+++ b/core/java/com/android/internal/jank/Cuj.java
@@ -120,7 +120,7 @@ public class Cuj {
public static final int CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY = 84;
public static final int CUJ_PREDICTIVE_BACK_CROSS_TASK = 85;
public static final int CUJ_PREDICTIVE_BACK_HOME = 86;
- public static final int CUJ_LAUNCHER_SEARCH_QSB_OPEN = 87;
+ // 87 is reserved - previously assigned to deprecated CUJ_LAUNCHER_SEARCH_QSB_OPEN.
public static final int CUJ_BACK_PANEL_ARROW = 88;
public static final int CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK = 89;
public static final int CUJ_LAUNCHER_SEARCH_QSB_WEB_SEARCH = 90;
@@ -209,7 +209,6 @@ public class Cuj {
CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY,
CUJ_PREDICTIVE_BACK_CROSS_TASK,
CUJ_PREDICTIVE_BACK_HOME,
- CUJ_LAUNCHER_SEARCH_QSB_OPEN,
CUJ_BACK_PANEL_ARROW,
CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK,
CUJ_LAUNCHER_SEARCH_QSB_WEB_SEARCH,
@@ -304,7 +303,6 @@ public class Cuj {
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_CROSS_ACTIVITY;
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_CROSS_TASK] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_CROSS_TASK;
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_HOME] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_HOME;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_SEARCH_QSB_OPEN] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_SEARCH_QSB_OPEN;
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_BACK_PANEL_ARROW] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__BACK_PANEL_ARROW;
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_CLOSE_ALL_APPS_BACK;
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_SEARCH_QSB_WEB_SEARCH] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_SEARCH_QSB_WEB_SEARCH;
@@ -480,8 +478,6 @@ public class Cuj {
return "PREDICTIVE_BACK_CROSS_TASK";
case CUJ_PREDICTIVE_BACK_HOME:
return "PREDICTIVE_BACK_HOME";
- case CUJ_LAUNCHER_SEARCH_QSB_OPEN:
- return "LAUNCHER_SEARCH_QSB_OPEN";
case CUJ_BACK_PANEL_ARROW:
return "BACK_PANEL_ARROW";
case CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK:
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index c5c17cffa48b..2a52264515fc 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -136,10 +136,8 @@ public class BatteryStatsHistory {
private final Parcel mHistoryBuffer;
private final File mSystemDir;
private final HistoryStepDetailsCalculator mStepDetailsCalculator;
- private final File mHistoryDir;
private final Clock mClock;
- private int mMaxHistoryFiles;
private int mMaxHistoryBufferSize;
/**
@@ -150,7 +148,7 @@ public class BatteryStatsHistory {
/**
* A list of history files with increasing timestamps.
*/
- private final List<BatteryHistoryFile> mHistoryFiles = new ArrayList<>();
+ private final BatteryHistoryDirectory mHistoryDir;
/**
* A list of small history parcels, used when BatteryStatsImpl object is created from
@@ -161,7 +159,8 @@ public class BatteryStatsHistory {
/**
* When iterating history files, the current file index.
*/
- private int mCurrentFileIndex;
+ private BatteryHistoryFile mCurrentFile;
+
/**
* When iterating history files, the current file parcel.
*/
@@ -203,7 +202,6 @@ public class BatteryStatsHistory {
private byte mLastHistoryStepLevel = 0;
private boolean mMutable = true;
private final BatteryStatsHistory mWritableHistory;
- private boolean mCleanupEnabled = true;
private static class BatteryHistoryFile implements Comparable<BatteryHistoryFile> {
public final long monotonicTimeMs;
@@ -235,6 +233,271 @@ public class BatteryStatsHistory {
}
}
+ private static class BatteryHistoryDirectory {
+ private final File mDirectory;
+ private final MonotonicClock mMonotonicClock;
+ private int mMaxHistoryFiles;
+ private final List<BatteryHistoryFile> mHistoryFiles = new ArrayList<>();
+ private final ReentrantLock mLock = new ReentrantLock();
+ private boolean mCleanupNeeded;
+
+ BatteryHistoryDirectory(File directory, MonotonicClock monotonicClock,
+ int maxHistoryFiles) {
+ mDirectory = directory;
+ mMonotonicClock = monotonicClock;
+ mMaxHistoryFiles = maxHistoryFiles;
+ if (mMaxHistoryFiles == 0) {
+ Slog.wtf(TAG, "mMaxHistoryFiles should not be zero when writing history");
+ }
+ }
+
+ void setMaxHistoryFiles(int maxHistoryFiles) {
+ mMaxHistoryFiles = maxHistoryFiles;
+ cleanup();
+ }
+
+ void lock() {
+ mLock.lock();
+ }
+
+ void unlock() {
+ mLock.unlock();
+ if (mCleanupNeeded) {
+ cleanup();
+ }
+ }
+
+ boolean isLocked() {
+ return mLock.isLocked();
+ }
+
+ void load() {
+ mDirectory.mkdirs();
+ if (!mDirectory.exists()) {
+ Slog.wtf(TAG, "HistoryDir does not exist:" + mDirectory.getPath());
+ }
+
+ final List<File> toRemove = new ArrayList<>();
+ final Set<BatteryHistoryFile> dedup = new ArraySet<>();
+ mDirectory.listFiles((dir, name) -> {
+ final int b = name.lastIndexOf(FILE_SUFFIX);
+ if (b <= 0) {
+ toRemove.add(new File(dir, name));
+ return false;
+ }
+ try {
+ long monotonicTime = Long.parseLong(name.substring(0, b));
+ dedup.add(new BatteryHistoryFile(mDirectory, monotonicTime));
+ } catch (NumberFormatException e) {
+ toRemove.add(new File(dir, name));
+ return false;
+ }
+ return true;
+ });
+ if (!dedup.isEmpty()) {
+ mHistoryFiles.addAll(dedup);
+ Collections.sort(mHistoryFiles);
+ }
+ if (!toRemove.isEmpty()) {
+ // Clear out legacy history files, which did not follow the X-Y.bin naming format.
+ BackgroundThread.getHandler().post(() -> {
+ lock();
+ try {
+ for (File file : toRemove) {
+ file.delete();
+ }
+ } finally {
+ unlock();
+ }
+ });
+ }
+ }
+
+ List<String> getFileNames() {
+ lock();
+ try {
+ List<String> names = new ArrayList<>();
+ for (BatteryHistoryFile historyFile : mHistoryFiles) {
+ names.add(historyFile.atomicFile.getBaseFile().getName());
+ }
+ return names;
+ } finally {
+ unlock();
+ }
+ }
+
+ @Nullable
+ BatteryHistoryFile getFirstFile() {
+ lock();
+ try {
+ if (!mHistoryFiles.isEmpty()) {
+ return mHistoryFiles.get(0);
+ }
+ return null;
+ } finally {
+ unlock();
+ }
+ }
+
+ @Nullable
+ BatteryHistoryFile getLastFile() {
+ lock();
+ try {
+ if (!mHistoryFiles.isEmpty()) {
+ return mHistoryFiles.get(mHistoryFiles.size() - 1);
+ }
+ return null;
+ } finally {
+ unlock();
+ }
+ }
+
+ @Nullable
+ BatteryHistoryFile getNextFile(BatteryHistoryFile current, long startTimeMs,
+ long endTimeMs) {
+ if (!mLock.isHeldByCurrentThread()) {
+ throw new IllegalStateException("Iterating battery history without a lock");
+ }
+
+ int nextFileIndex = 0;
+ int firstFileIndex = 0;
+ // skip the last file because its data is in history buffer.
+ int lastFileIndex = mHistoryFiles.size() - 2;
+ for (int i = lastFileIndex; i >= 0; i--) {
+ BatteryHistoryFile file = mHistoryFiles.get(i);
+ if (current != null && file.monotonicTimeMs == current.monotonicTimeMs) {
+ nextFileIndex = i + 1;
+ }
+ if (file.monotonicTimeMs > endTimeMs) {
+ lastFileIndex = i - 1;
+ }
+ if (file.monotonicTimeMs <= startTimeMs) {
+ firstFileIndex = i;
+ break;
+ }
+ }
+
+ if (nextFileIndex < firstFileIndex) {
+ nextFileIndex = firstFileIndex;
+ }
+
+ if (nextFileIndex <= lastFileIndex) {
+ return mHistoryFiles.get(nextFileIndex);
+ }
+
+ return null;
+ }
+
+ BatteryHistoryFile makeBatteryHistoryFile() {
+ BatteryHistoryFile file = new BatteryHistoryFile(mDirectory,
+ mMonotonicClock.monotonicTime());
+ lock();
+ try {
+ mHistoryFiles.add(file);
+ } finally {
+ unlock();
+ }
+ return file;
+ }
+
+ void writeToParcel(Parcel out, boolean useBlobs) {
+ lock();
+ try {
+ final long start = SystemClock.uptimeMillis();
+ out.writeInt(mHistoryFiles.size() - 1);
+ for (int i = 0; i < mHistoryFiles.size() - 1; i++) {
+ AtomicFile file = mHistoryFiles.get(i).atomicFile;
+ byte[] raw = new byte[0];
+ try {
+ raw = file.readFully();
+ } catch (Exception e) {
+ Slog.e(TAG, "Error reading file " + file.getBaseFile().getPath(), e);
+ }
+ if (useBlobs) {
+ out.writeBlob(raw);
+ } else {
+ // Avoiding blobs in the check-in file for compatibility
+ out.writeByteArray(raw);
+ }
+ }
+ if (DEBUG) {
+ Slog.d(TAG,
+ "writeToParcel duration ms:" + (SystemClock.uptimeMillis() - start));
+ }
+ } finally {
+ unlock();
+ }
+ }
+
+ int getFileCount() {
+ lock();
+ try {
+ return mHistoryFiles.size();
+ } finally {
+ unlock();
+ }
+ }
+
+ int getSize() {
+ lock();
+ try {
+ int ret = 0;
+ for (int i = 0; i < mHistoryFiles.size() - 1; i++) {
+ ret += (int) mHistoryFiles.get(i).atomicFile.getBaseFile().length();
+ }
+ return ret;
+ } finally {
+ unlock();
+ }
+ }
+
+ void reset() {
+ lock();
+ try {
+ if (DEBUG) Slog.i(TAG, "********** CLEARING HISTORY!");
+ for (BatteryHistoryFile file : mHistoryFiles) {
+ file.atomicFile.delete();
+ }
+ mHistoryFiles.clear();
+ } finally {
+ unlock();
+ }
+ }
+
+ private void cleanup() {
+ if (mDirectory == null) {
+ return;
+ }
+
+ if (isLocked()) {
+ mCleanupNeeded = true;
+ return;
+ }
+
+ mCleanupNeeded = false;
+
+ lock();
+ try {
+ // if free disk space is less than 100MB, delete oldest history file.
+ if (!hasFreeDiskSpace(mDirectory)) {
+ BatteryHistoryFile oldest = mHistoryFiles.remove(0);
+ oldest.atomicFile.delete();
+ }
+
+ // if there are more history files than allowed, delete oldest history files.
+ // mMaxHistoryFiles comes from Constants.MAX_HISTORY_FILES and
+ // can be updated by DeviceConfig at run time.
+ while (mHistoryFiles.size() > mMaxHistoryFiles) {
+ BatteryHistoryFile oldest = mHistoryFiles.get(0);
+ oldest.atomicFile.delete();
+ mHistoryFiles.remove(0);
+ }
+ } finally {
+ unlock();
+ }
+ }
+ }
+
/**
* A delegate responsible for computing additional details for a step in battery history.
*/
@@ -351,7 +614,6 @@ public class BatteryStatsHistory {
BatteryStatsHistory writableHistory) {
mHistoryBuffer = historyBuffer;
mSystemDir = systemDir;
- mMaxHistoryFiles = maxHistoryFiles;
mMaxHistoryBufferSize = maxHistoryBufferSize;
mStepDetailsCalculator = stepDetailsCalculator;
mTracer = tracer;
@@ -363,66 +625,32 @@ public class BatteryStatsHistory {
mMutable = false;
}
- mHistoryDir = new File(systemDir, HISTORY_DIR);
- mHistoryDir.mkdirs();
- if (!mHistoryDir.exists()) {
- Slog.wtf(TAG, "HistoryDir does not exist:" + mHistoryDir.getPath());
- }
-
- final List<File> toRemove = new ArrayList<>();
- final Set<BatteryHistoryFile> dedup = new ArraySet<>();
- mHistoryDir.listFiles((dir, name) -> {
- final int b = name.lastIndexOf(FILE_SUFFIX);
- if (b <= 0) {
- toRemove.add(new File(dir, name));
- return false;
- }
- try {
- long monotonicTime = Long.parseLong(name.substring(0, b));
- dedup.add(new BatteryHistoryFile(mHistoryDir, monotonicTime));
- } catch (NumberFormatException e) {
- toRemove.add(new File(dir, name));
- return false;
+ if (writableHistory != null) {
+ mHistoryDir = writableHistory.mHistoryDir;
+ } else {
+ mHistoryDir = new BatteryHistoryDirectory(new File(systemDir, HISTORY_DIR),
+ monotonicClock, maxHistoryFiles);
+ mHistoryDir.load();
+ BatteryHistoryFile activeFile = mHistoryDir.getLastFile();
+ if (activeFile == null) {
+ activeFile = mHistoryDir.makeBatteryHistoryFile();
}
- return true;
- });
- if (!dedup.isEmpty()) {
- mHistoryFiles.addAll(dedup);
- Collections.sort(mHistoryFiles);
- setActiveFile(mHistoryFiles.get(mHistoryFiles.size() - 1));
- } else if (mMutable) {
- // No file found, default to have the initial file.
- BatteryHistoryFile name = makeBatteryHistoryFile();
- mHistoryFiles.add(name);
- setActiveFile(name);
- }
- if (!toRemove.isEmpty()) {
- // Clear out legacy history files, which did not follow the X-Y.bin naming format.
- BackgroundThread.getHandler().post(() -> {
- for (File file : toRemove) {
- file.delete();
- }
- });
+ setActiveFile(activeFile);
}
}
- private BatteryHistoryFile makeBatteryHistoryFile() {
- return new BatteryHistoryFile(mHistoryDir, mMonotonicClock.monotonicTime());
- }
-
- public BatteryStatsHistory(int maxHistoryFiles, int maxHistoryBufferSize,
+ public BatteryStatsHistory(int maxHistoryBufferSize,
HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock,
MonotonicClock monotonicClock) {
- this(maxHistoryFiles, maxHistoryBufferSize, stepDetailsCalculator, clock, monotonicClock,
+ this(maxHistoryBufferSize, stepDetailsCalculator, clock, monotonicClock,
new TraceDelegate(), new EventLogger());
}
@VisibleForTesting
- public BatteryStatsHistory(int maxHistoryFiles, int maxHistoryBufferSize,
+ public BatteryStatsHistory(int maxHistoryBufferSize,
HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock,
MonotonicClock monotonicClock, TraceDelegate traceDelegate,
EventLogger eventLogger) {
- mMaxHistoryFiles = maxHistoryFiles;
mMaxHistoryBufferSize = maxHistoryBufferSize;
mStepDetailsCalculator = stepDetailsCalculator;
mTracer = traceDelegate;
@@ -484,7 +712,9 @@ public class BatteryStatsHistory {
* Changes the maximum number of history files to be kept.
*/
public void setMaxHistoryFiles(int maxHistoryFiles) {
- mMaxHistoryFiles = maxHistoryFiles;
+ if (mHistoryDir != null) {
+ mHistoryDir.setMaxHistoryFiles(maxHistoryFiles);
+ }
}
/**
@@ -513,7 +743,7 @@ public class BatteryStatsHistory {
* Returns true if this instance only supports reading history.
*/
public boolean isReadOnly() {
- return !mMutable || mActiveFile == null || mHistoryDir == null;
+ return !mMutable || mActiveFile == null/* || mHistoryDir == null*/;
}
/**
@@ -538,25 +768,13 @@ public class BatteryStatsHistory {
@GuardedBy("this")
private void startNextFileLocked(long elapsedRealtimeMs) {
- if (mMaxHistoryFiles == 0) {
- Slog.wtf(TAG, "mMaxHistoryFiles should not be zero when writing history");
- return;
- }
-
- if (mHistoryFiles.isEmpty()) {
- Slog.wtf(TAG, "mFileNumbers should never be empty");
- return;
- }
-
final long start = SystemClock.uptimeMillis();
writeHistory();
if (DEBUG) {
Slog.d(TAG, "writeHistory took ms:" + (SystemClock.uptimeMillis() - start));
}
- final BatteryHistoryFile next = makeBatteryHistoryFile();
- mHistoryFiles.add(next);
- setActiveFile(next);
+ setActiveFile(mHistoryDir.makeBatteryHistoryFile());
try {
mActiveFile.getBaseFile().createNewFile();
} catch (IOException e) {
@@ -578,37 +796,7 @@ public class BatteryStatsHistory {
}
mWrittenPowerStatsDescriptors.clear();
- cleanupLocked();
- }
-
- @GuardedBy("this")
- private void setCleanupEnabledLocked(boolean enabled) {
- mCleanupEnabled = enabled;
- if (mCleanupEnabled) {
- cleanupLocked();
- }
- }
-
- @GuardedBy("this")
- private void cleanupLocked() {
- if (!mCleanupEnabled || mHistoryDir == null) {
- return;
- }
-
- // if free disk space is less than 100MB, delete oldest history file.
- if (!hasFreeDiskSpace()) {
- BatteryHistoryFile oldest = mHistoryFiles.remove(0);
- oldest.atomicFile.delete();
- }
-
- // if there are more history files than allowed, delete oldest history files.
- // mMaxHistoryFiles comes from Constants.MAX_HISTORY_FILES and can be updated by GService
- // config at run time.
- while (mHistoryFiles.size() > mMaxHistoryFiles) {
- BatteryHistoryFile oldest = mHistoryFiles.get(0);
- oldest.atomicFile.delete();
- mHistoryFiles.remove(0);
- }
+ mHistoryDir.cleanup();
}
/**
@@ -616,9 +804,7 @@ public class BatteryStatsHistory {
* currently being read.
*/
public boolean isResetEnabled() {
- synchronized (this) {
- return mCleanupEnabled;
- }
+ return mHistoryDir == null || !mHistoryDir.isLocked();
}
/**
@@ -627,16 +813,10 @@ public class BatteryStatsHistory {
*/
public void reset() {
synchronized (this) {
- if (DEBUG) Slog.i(TAG, "********** CLEARING HISTORY!");
- for (BatteryHistoryFile file : mHistoryFiles) {
- file.atomicFile.delete();
+ if (mHistoryDir != null) {
+ mHistoryDir.reset();
+ setActiveFile(mHistoryDir.makeBatteryHistoryFile());
}
- mHistoryFiles.clear();
-
- BatteryHistoryFile name = makeBatteryHistoryFile();
- mHistoryFiles.add(name);
- setActiveFile(name);
-
initHistoryBuffer();
}
}
@@ -646,8 +826,9 @@ public class BatteryStatsHistory {
*/
public long getStartTime() {
synchronized (this) {
- if (!mHistoryFiles.isEmpty()) {
- return mHistoryFiles.get(0).monotonicTimeMs;
+ BatteryHistoryFile file = mHistoryDir.getFirstFile();
+ if (file != null) {
+ return file.monotonicTimeMs;
} else {
return mHistoryBufferStartTime;
}
@@ -668,15 +849,13 @@ public class BatteryStatsHistory {
return copy().iterate(startTimeMs, endTimeMs);
}
- mCurrentFileIndex = 0;
+ if (mHistoryDir != null) {
+ mHistoryDir.lock();
+ }
+ mCurrentFile = null;
mCurrentParcel = null;
mCurrentParcelEnd = 0;
mParcelIndex = 0;
- if (mWritableHistory != null) {
- synchronized (mWritableHistory) {
- mWritableHistory.setCleanupEnabledLocked(false);
- }
- }
return new BatteryStatsHistoryIterator(this, startTimeMs, endTimeMs);
}
@@ -685,10 +864,8 @@ public class BatteryStatsHistory {
*/
void iteratorFinished() {
mHistoryBuffer.setDataPosition(mHistoryBuffer.dataSize());
- if (mWritableHistory != null) {
- synchronized (mWritableHistory) {
- mWritableHistory.setCleanupEnabledLocked(true);
- }
+ if (mHistoryDir != null) {
+ mHistoryDir.unlock();
}
}
@@ -719,39 +896,27 @@ public class BatteryStatsHistory {
}
}
- int firstFileIndex = 0;
- // skip the last file because its data is in history buffer.
- int lastFileIndex = mHistoryFiles.size() - 1;
- for (int i = mHistoryFiles.size() - 1; i >= 0; i--) {
- BatteryHistoryFile file = mHistoryFiles.get(i);
- if (file.monotonicTimeMs >= endTimeMs) {
- lastFileIndex = i;
- }
- if (file.monotonicTimeMs <= startTimeMs) {
- firstFileIndex = i;
- break;
- }
- }
-
- if (mCurrentFileIndex < firstFileIndex) {
- mCurrentFileIndex = firstFileIndex;
- }
-
- while (mCurrentFileIndex < lastFileIndex) {
- mCurrentParcel = null;
- mCurrentParcelEnd = 0;
- final Parcel p = Parcel.obtain();
- AtomicFile file = mHistoryFiles.get(mCurrentFileIndex++).atomicFile;
- if (readFileToParcel(p, file)) {
- int bufSize = p.readInt();
- int curPos = p.dataPosition();
- mCurrentParcelEnd = curPos + bufSize;
- mCurrentParcel = p;
- if (curPos < mCurrentParcelEnd) {
- return mCurrentParcel;
+ if (mHistoryDir != null) {
+ BatteryHistoryFile nextFile = mHistoryDir.getNextFile(mCurrentFile, startTimeMs,
+ endTimeMs);
+ while (nextFile != null) {
+ mCurrentParcel = null;
+ mCurrentParcelEnd = 0;
+ final Parcel p = Parcel.obtain();
+ AtomicFile file = nextFile.atomicFile;
+ if (readFileToParcel(p, file)) {
+ int bufSize = p.readInt();
+ int curPos = p.dataPosition();
+ mCurrentParcelEnd = curPos + bufSize;
+ mCurrentParcel = p;
+ if (curPos < mCurrentParcelEnd) {
+ mCurrentFile = nextFile;
+ return mCurrentParcel;
+ }
+ } else {
+ p.recycle();
}
- } else {
- p.recycle();
+ nextFile = mHistoryDir.getNextFile(nextFile, startTimeMs, endTimeMs);
}
}
@@ -922,25 +1087,8 @@ public class BatteryStatsHistory {
}
private void writeToParcel(Parcel out, boolean useBlobs) {
- final long start = SystemClock.uptimeMillis();
- out.writeInt(mHistoryFiles.size() - 1);
- for (int i = 0; i < mHistoryFiles.size() - 1; i++) {
- AtomicFile file = mHistoryFiles.get(i).atomicFile;
- byte[] raw = new byte[0];
- try {
- raw = file.readFully();
- } catch (Exception e) {
- Slog.e(TAG, "Error reading file " + file.getBaseFile().getPath(), e);
- }
- if (useBlobs) {
- out.writeBlob(raw);
- } else {
- // Avoiding blobs in the check-in file for compatibility
- out.writeByteArray(raw);
- }
- }
- if (DEBUG) {
- Slog.d(TAG, "writeToParcel duration ms:" + (SystemClock.uptimeMillis() - start));
+ if (mHistoryDir != null) {
+ mHistoryDir.writeToParcel(out, useBlobs);
}
}
@@ -1021,22 +1169,18 @@ public class BatteryStatsHistory {
* @return true if there is more than 100MB free disk space left.
*/
@android.ravenwood.annotation.RavenwoodReplace
- private boolean hasFreeDiskSpace() {
- final StatFs stats = new StatFs(mHistoryDir.getAbsolutePath());
+ private static boolean hasFreeDiskSpace(File systemDir) {
+ final StatFs stats = new StatFs(systemDir.getAbsolutePath());
return stats.getAvailableBytes() > MIN_FREE_SPACE;
}
- private boolean hasFreeDiskSpace$ravenwood() {
+ private static boolean hasFreeDiskSpace$ravenwood(File systemDir) {
return true;
}
@VisibleForTesting
public List<String> getFilesNames() {
- List<String> names = new ArrayList<>();
- for (BatteryHistoryFile historyFile : mHistoryFiles) {
- names.add(historyFile.atomicFile.getBaseFile().getName());
- }
- return names;
+ return mHistoryDir.getFileNames();
}
@VisibleForTesting
@@ -1048,10 +1192,7 @@ public class BatteryStatsHistory {
* @return the total size of all history files and history buffer.
*/
public int getHistoryUsedSize() {
- int ret = 0;
- for (int i = 0; i < mHistoryFiles.size() - 1; i++) {
- ret += mHistoryFiles.get(i).atomicFile.getBaseFile().length();
- }
+ int ret = mHistoryDir.getSize();
ret += mHistoryBuffer.dataSize();
if (mHistoryParcels != null) {
for (int i = 0; i < mHistoryParcels.size(); i++) {
@@ -1109,7 +1250,7 @@ public class BatteryStatsHistory {
*/
public void continueRecordingHistory() {
synchronized (this) {
- if (mHistoryBuffer.dataPosition() <= 0 && mHistoryFiles.size() <= 1) {
+ if (mHistoryBuffer.dataPosition() <= 0 && mHistoryDir.getFileCount() <= 1) {
return;
}
diff --git a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
index b2a6a934ba3f..83e9407fbdde 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
@@ -44,6 +44,7 @@ public class BatteryStatsHistoryIterator implements Iterator<BatteryStats.Histor
private BatteryStats.HistoryItem mHistoryItem = new BatteryStats.HistoryItem();
private boolean mNextItemReady;
private boolean mTimeInitialized;
+ private boolean mClosed;
public BatteryStatsHistoryIterator(@NonNull BatteryStatsHistory history, long startTimeMs,
long endTimeMs) {
@@ -322,6 +323,9 @@ public class BatteryStatsHistoryIterator implements Iterator<BatteryStats.Histor
*/
@Override
public void close() {
- mBatteryStatsHistory.iteratorFinished();
+ if (!mClosed) {
+ mClosed = true;
+ mBatteryStatsHistory.iteratorFinished();
+ }
}
}
diff --git a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
index ed943cb2385d..eef6ce7619e8 100644
--- a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
+++ b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
@@ -57,7 +57,7 @@ import java.util.concurrent.atomic.AtomicReference;
*/
@android.ravenwood.annotation.RavenwoodKeepWholeClass
@android.ravenwood.annotation.RavenwoodNativeSubstitutionClass(
- "com.android.hoststubgen.nativesubstitution.LongArrayMultiStateCounter_host")
+ "com.android.platform.test.ravenwood.nativesubstitution.LongArrayMultiStateCounter_host")
public final class LongArrayMultiStateCounter implements Parcelable {
/**
@@ -65,7 +65,7 @@ public final class LongArrayMultiStateCounter implements Parcelable {
*/
@android.ravenwood.annotation.RavenwoodKeepWholeClass
@android.ravenwood.annotation.RavenwoodNativeSubstitutionClass(
- "com.android.hoststubgen.nativesubstitution"
+ "com.android.platform.test.ravenwood.nativesubstitution"
+ ".LongArrayMultiStateCounter_host$LongArrayContainer_host")
public static class LongArrayContainer {
private static NativeAllocationRegistry sRegistry;
diff --git a/core/java/com/android/internal/os/LongMultiStateCounter.java b/core/java/com/android/internal/os/LongMultiStateCounter.java
index 064609f9dfe4..e5662c7d5145 100644
--- a/core/java/com/android/internal/os/LongMultiStateCounter.java
+++ b/core/java/com/android/internal/os/LongMultiStateCounter.java
@@ -57,7 +57,7 @@ import libcore.util.NativeAllocationRegistry;
*/
@android.ravenwood.annotation.RavenwoodKeepWholeClass
@android.ravenwood.annotation.RavenwoodNativeSubstitutionClass(
- "com.android.hoststubgen.nativesubstitution.LongMultiStateCounter_host")
+ "com.android.platform.test.ravenwood.nativesubstitution.LongMultiStateCounter_host")
public final class LongMultiStateCounter implements Parcelable {
private static NativeAllocationRegistry sRegistry;
diff --git a/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java b/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java
index d433ca3246b6..73df5e8e7b1b 100644
--- a/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java
+++ b/core/java/com/android/internal/pm/parsing/pkg/PackageImpl.java
@@ -408,6 +408,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
// Derived fields
private long mLongVersionCode;
private int mLocaleConfigRes;
+ private boolean mAllowCrossUidActivitySwitchFromBelow;
private List<AndroidPackageSplit> mSplits;
@@ -1542,6 +1543,11 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
}
@Override
+ public boolean isAllowCrossUidActivitySwitchFromBelow() {
+ return mAllowCrossUidActivitySwitchFromBelow;
+ }
+
+ @Override
public boolean hasPreserveLegacyExternalStorage() {
return getBoolean(Booleans.PRESERVE_LEGACY_EXTERNAL_STORAGE);
}
@@ -2199,6 +2205,12 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
}
@Override
+ public ParsingPackage setAllowCrossUidActivitySwitchFromBelow(boolean value) {
+ mAllowCrossUidActivitySwitchFromBelow = value;
+ return this;
+ }
+
+ @Override
public PackageImpl setResourceOverlay(boolean value) {
return setBoolean(Booleans.OVERLAY, value);
}
@@ -2656,6 +2668,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
if (!mKnownActivityEmbeddingCerts.isEmpty()) {
appInfo.setKnownActivityEmbeddingCerts(mKnownActivityEmbeddingCerts);
}
+ appInfo.allowCrossUidActivitySwitchFromBelow = mAllowCrossUidActivitySwitchFromBelow;
return appInfo;
}
@@ -3250,6 +3263,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
dest.writeInt(this.uid);
dest.writeLong(this.mBooleans);
dest.writeLong(this.mBooleans2);
+ dest.writeBoolean(this.mAllowCrossUidActivitySwitchFromBelow);
}
public PackageImpl(Parcel in) {
@@ -3411,6 +3425,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
this.uid = in.readInt();
this.mBooleans = in.readLong();
this.mBooleans2 = in.readLong();
+ this.mAllowCrossUidActivitySwitchFromBelow = in.readBoolean();
assignDerivedFields();
assignDerivedFields2();
diff --git a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java
index ef106e0268a6..5d185af17d48 100644
--- a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java
+++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackage.java
@@ -374,6 +374,9 @@ public interface ParsingPackage {
ParsingPackage setZygotePreloadName(String zygotePreloadName);
+ ParsingPackage setAllowCrossUidActivitySwitchFromBelow(
+ boolean allowCrossUidActivitySwitchFromBelow);
+
ParsingPackage sortActivities();
ParsingPackage sortReceivers();
@@ -518,6 +521,8 @@ public interface ParsingPackage {
@Nullable
String getZygotePreloadName();
+ boolean isAllowCrossUidActivitySwitchFromBelow();
+
boolean isBackupAllowed();
boolean isTaskReparentingAllowed();
diff --git a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
index e0fdbc68a41f..2e6053d3d904 100644
--- a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
@@ -2374,8 +2374,10 @@ public class ParsingPackageUtils {
.setRestrictedAccountType(string(R.styleable.AndroidManifestApplication_restrictedAccountType, sa))
.setZygotePreloadName(string(R.styleable.AndroidManifestApplication_zygotePreloadName, sa))
// Non-Config String
- .setPermission(nonConfigString(0, R.styleable.AndroidManifestApplication_permission, sa));
- // CHECKSTYLE:on
+ .setPermission(nonConfigString(0, R.styleable.AndroidManifestApplication_permission, sa))
+ .setAllowCrossUidActivitySwitchFromBelow(bool(true, R.styleable.AndroidManifestApplication_allowCrossUidActivitySwitchFromBelow, sa));
+
+ // CHECKSTYLE:on
//@formatter:on
}
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 523924566dd7..0dd01e48db0a 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -363,6 +363,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
private boolean mUseDecorContext = false;
+ private boolean mIsFrameRatePowerSavingsBalanced = true;
+
/** @see ViewRootImpl#mActivityConfigCallback */
private ActivityConfigCallback mActivityConfigCallback;
@@ -2211,6 +2213,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
void onViewRootImplSet(ViewRootImpl viewRoot) {
viewRoot.setActivityConfigCallback(mActivityConfigCallback);
viewRoot.getOnBackInvokedDispatcher().updateContext(getContext());
+ viewRoot.setFrameRatePowerSavingsBalanced(mIsFrameRatePowerSavingsBalanced);
mProxyOnBackInvokedDispatcher.setActualDispatcher(viewRoot.getOnBackInvokedDispatcher());
applyDecorFitsSystemWindows();
}
@@ -2559,6 +2562,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
if (a.getBoolean(R.styleable.Window_windowActivityTransitions, false)) {
requestFeature(FEATURE_ACTIVITY_TRANSITIONS);
}
+ if (a.hasValue(R.styleable.Window_windowIsFrameRatePowerSavingsBalanced)) {
+ mIsFrameRatePowerSavingsBalanced =
+ a.getBoolean(R.styleable.Window_windowIsFrameRatePowerSavingsBalanced, true);
+ }
mIsTranslucent = a.getBoolean(R.styleable.Window_windowIsTranslucent, false);
diff --git a/core/java/com/android/internal/protolog/BaseProtoLogImpl.java b/core/java/com/android/internal/protolog/LegacyProtoLogImpl.java
index abe6c7c079c2..d9ac5a9fe47a 100644
--- a/core/java/com/android/internal/protolog/BaseProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/LegacyProtoLogImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -37,8 +37,11 @@ import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ILogger;
+import com.android.internal.protolog.common.IProtoLog;
import com.android.internal.protolog.common.IProtoLogGroup;
import com.android.internal.protolog.common.LogDataType;
+import com.android.internal.protolog.common.LogLevel;
import com.android.internal.util.TraceBuffer;
import java.io.File;
@@ -48,52 +51,49 @@ import java.util.ArrayList;
import java.util.TreeMap;
import java.util.stream.Collectors;
-
/**
* A service for the ProtoLog logging system.
*/
-public class BaseProtoLogImpl {
- protected static final TreeMap<String, IProtoLogGroup> LOG_GROUPS = new TreeMap<>();
-
- /**
- * A runnable to update the cached output of {@link #isEnabled}.
- *
- * Must be invoked after every action that could change the result of {@link #isEnabled}, eg.
- * starting / stopping proto log, or enabling / disabling log groups.
- */
- public static Runnable sCacheUpdater = () -> { };
-
- protected static void addLogGroupEnum(IProtoLogGroup[] config) {
- for (IProtoLogGroup group : config) {
- LOG_GROUPS.put(group.name(), group);
- }
- }
+public class LegacyProtoLogImpl implements IProtoLog {
+ private final TreeMap<String, IProtoLogGroup> mLogGroups = new TreeMap<>();
+ private static final int BUFFER_CAPACITY = 1024 * 1024;
+ private static final int PER_CHUNK_SIZE = 1024;
private static final String TAG = "ProtoLog";
private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
static final String PROTOLOG_VERSION = "1.0.0";
private static final int DEFAULT_PER_CHUNK_SIZE = 0;
private final File mLogFile;
- private final String mViewerConfigFilename;
+ private final String mLegacyViewerConfigFilename;
private final TraceBuffer mBuffer;
- protected final ProtoLogViewerConfigReader mViewerConfig;
+ private final LegacyProtoLogViewerConfigReader mViewerConfig;
private final int mPerChunkSize;
private boolean mProtoLogEnabled;
private boolean mProtoLogEnabledLockFree;
private final Object mProtoLogEnabledLock = new Object();
- @VisibleForTesting
- public enum LogLevel {
- DEBUG, VERBOSE, INFO, WARN, ERROR, WTF
+ public LegacyProtoLogImpl(String outputFile, String viewerConfigFilename) {
+ this(new File(outputFile), viewerConfigFilename, BUFFER_CAPACITY,
+ new LegacyProtoLogViewerConfigReader(), PER_CHUNK_SIZE);
+ }
+
+ public LegacyProtoLogImpl(File file, String viewerConfigFilename, int bufferCapacity,
+ LegacyProtoLogViewerConfigReader viewerConfig, int perChunkSize) {
+ mLogFile = file;
+ mBuffer = new TraceBuffer(bufferCapacity);
+ mLegacyViewerConfigFilename = viewerConfigFilename;
+ mViewerConfig = viewerConfig;
+ mPerChunkSize = perChunkSize;
}
/**
* Main log method, do not call directly.
*/
@VisibleForTesting
- public void log(LogLevel level, IProtoLogGroup group, int messageHash, int paramsMask,
+ @Override
+ public void log(LogLevel level, IProtoLogGroup group, long messageHash, int paramsMask,
@Nullable String messageString, Object[] args) {
if (group.isLogToProto()) {
logToProto(messageHash, paramsMask, args);
@@ -103,7 +103,7 @@ public class BaseProtoLogImpl {
}
}
- private void logToLogcat(String tag, LogLevel level, int messageHash,
+ private void logToLogcat(String tag, LogLevel level, long messageHash,
@Nullable String messageString, Object[] args) {
String message = null;
if (messageString == null) {
@@ -157,7 +157,7 @@ public class BaseProtoLogImpl {
}
}
- private void logToProto(int messageHash, int paramsMask, Object[] args) {
+ private void logToProto(long messageHash, int paramsMask, Object[] args) {
if (!isProtoEnabled()) {
return;
}
@@ -219,20 +219,6 @@ public class BaseProtoLogImpl {
}
}
- public BaseProtoLogImpl(File file, String viewerConfigFilename, int bufferCapacity,
- ProtoLogViewerConfigReader viewerConfig) {
- this(file, viewerConfigFilename, bufferCapacity, viewerConfig, DEFAULT_PER_CHUNK_SIZE);
- }
-
- public BaseProtoLogImpl(File file, String viewerConfigFilename, int bufferCapacity,
- ProtoLogViewerConfigReader viewerConfig, int perChunkSize) {
- mLogFile = file;
- mBuffer = new TraceBuffer(bufferCapacity);
- mViewerConfigFilename = viewerConfigFilename;
- mViewerConfig = viewerConfig;
- mPerChunkSize = perChunkSize;
- }
-
/**
* Starts the logging a circular proto buffer.
*
@@ -248,7 +234,6 @@ public class BaseProtoLogImpl {
mProtoLogEnabled = true;
mProtoLogEnabledLockFree = true;
}
- sCacheUpdater.run();
}
/**
@@ -274,7 +259,6 @@ public class BaseProtoLogImpl {
throw new IllegalStateException("logging enabled while waiting for flush.");
}
}
- sCacheUpdater.run();
}
/**
@@ -284,11 +268,11 @@ public class BaseProtoLogImpl {
return mProtoLogEnabledLockFree;
}
- protected int setLogging(boolean setTextLogging, boolean value, PrintWriter pw,
+ private int setLogging(boolean setTextLogging, boolean value, ILogger logger,
String... groups) {
for (int i = 0; i < groups.length; i++) {
String group = groups[i];
- IProtoLogGroup g = LOG_GROUPS.get(group);
+ IProtoLogGroup g = mLogGroups.get(group);
if (g != null) {
if (setTextLogging) {
g.setLogToLogcat(value);
@@ -296,11 +280,10 @@ public class BaseProtoLogImpl {
g.setLogToProto(value);
}
} else {
- logAndPrintln(pw, "No IProtoLogGroup named " + group);
+ logger.log("No IProtoLogGroup named " + group);
return -1;
}
}
- sCacheUpdater.run();
return 0;
}
@@ -330,6 +313,7 @@ public class BaseProtoLogImpl {
while ((arg = shell.getNextArg()) != null) {
args.add(arg);
}
+ final ILogger logger = (msg) -> logAndPrintln(pw, msg);
String[] groups = args.toArray(new String[args.size()]);
switch (cmd) {
case "start":
@@ -342,14 +326,14 @@ public class BaseProtoLogImpl {
logAndPrintln(pw, getStatus());
return 0;
case "enable":
- return setLogging(false, true, pw, groups);
+ return setLogging(false, true, logger, groups);
case "enable-text":
- mViewerConfig.loadViewerConfig(pw, mViewerConfigFilename);
- return setLogging(true, true, pw, groups);
+ mViewerConfig.loadViewerConfig(logger, mLegacyViewerConfigFilename);
+ return setLogging(true, true, logger, groups);
case "disable":
- return setLogging(false, false, pw, groups);
+ return setLogging(false, false, logger, groups);
case "disable-text":
- return setLogging(true, false, pw, groups);
+ return setLogging(true, false, logger, groups);
default:
return unknownCommand(pw);
}
@@ -362,12 +346,12 @@ public class BaseProtoLogImpl {
return "ProtoLog status: "
+ ((isProtoEnabled()) ? "Enabled" : "Disabled")
+ "\nEnabled log groups: \n Proto: "
- + LOG_GROUPS.values().stream().filter(
- it -> it.isEnabled() && it.isLogToProto())
+ + mLogGroups.values().stream().filter(
+ it -> it.isEnabled() && it.isLogToProto())
.map(IProtoLogGroup::name).collect(Collectors.joining(" "))
+ "\n Logcat: "
- + LOG_GROUPS.values().stream().filter(
- it -> it.isEnabled() && it.isLogToLogcat())
+ + mLogGroups.values().stream().filter(
+ it -> it.isEnabled() && it.isLogToLogcat())
.map(IProtoLogGroup::name).collect(Collectors.joining(" "))
+ "\nLogging definitions loaded: " + mViewerConfig.knownViewerStringsNumber();
}
@@ -393,5 +377,26 @@ public class BaseProtoLogImpl {
pw.flush();
}
}
+
+ /**
+ * Start text logging
+ * @param groups Groups to start text logging for
+ * @param logger A logger to write status updates to
+ * @return status code
+ */
+ public int startLoggingToLogcat(String[] groups, ILogger logger) {
+ mViewerConfig.loadViewerConfig(logger, mLegacyViewerConfigFilename);
+ return setLogging(true /* setTextLogging */, true, logger, groups);
+ }
+
+ /**
+ * Stop text logging
+ * @param groups Groups to start text logging for
+ * @param logger A logger to write status updates to
+ * @return status code
+ */
+ public int stopLoggingToLogcat(String[] groups, ILogger logger) {
+ return setLogging(true /* setTextLogging */, false, logger, groups);
+ }
}
diff --git a/core/java/com/android/internal/protolog/LegacyProtoLogViewerConfigReader.java b/core/java/com/android/internal/protolog/LegacyProtoLogViewerConfigReader.java
new file mode 100644
index 000000000000..1833410950ba
--- /dev/null
+++ b/core/java/com/android/internal/protolog/LegacyProtoLogViewerConfigReader.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.protolog;
+
+import com.android.internal.protolog.common.ILogger;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.zip.GZIPInputStream;
+
+/**
+ * Handles loading and parsing of ProtoLog viewer configuration.
+ */
+public class LegacyProtoLogViewerConfigReader {
+
+ private static final String TAG = "ProtoLogViewerConfigReader";
+ private Map<Long, String> mLogMessageMap = null;
+
+ /** Returns message format string for its hash or null if unavailable. */
+ public synchronized String getViewerString(long messageHash) {
+ if (mLogMessageMap != null) {
+ return mLogMessageMap.get(messageHash);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Reads the specified viewer configuration file. Does nothing if the config is already loaded.
+ */
+ public synchronized void loadViewerConfig(ILogger logger, String viewerConfigFilename) {
+ try {
+ loadViewerConfig(new GZIPInputStream(new FileInputStream(viewerConfigFilename)));
+ logger.log("Loaded " + mLogMessageMap.size()
+ + " log definitions from " + viewerConfigFilename);
+ } catch (FileNotFoundException e) {
+ logger.log("Unable to load log definitions: File "
+ + viewerConfigFilename + " not found." + e);
+ } catch (IOException e) {
+ logger.log("Unable to load log definitions: IOException while reading "
+ + viewerConfigFilename + ". " + e);
+ } catch (JSONException e) {
+ logger.log("Unable to load log definitions: JSON parsing exception while reading "
+ + viewerConfigFilename + ". " + e);
+ }
+ }
+
+ /**
+ * Reads the specified viewer configuration input stream.
+ * Does nothing if the config is already loaded.
+ */
+ public synchronized void loadViewerConfig(InputStream viewerConfigInputStream)
+ throws IOException, JSONException {
+ if (mLogMessageMap != null) {
+ return;
+ }
+ InputStreamReader config = new InputStreamReader(viewerConfigInputStream);
+ BufferedReader reader = new BufferedReader(config);
+ StringBuilder builder = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ builder.append(line).append('\n');
+ }
+ reader.close();
+ JSONObject json = new JSONObject(builder.toString());
+ JSONObject messages = json.getJSONObject("messages");
+
+ mLogMessageMap = new TreeMap<>();
+ Iterator it = messages.keys();
+ while (it.hasNext()) {
+ String key = (String) it.next();
+ try {
+ long hash = Long.parseLong(key);
+ JSONObject val = messages.getJSONObject(key);
+ String msg = val.getString("message");
+ mLogMessageMap.put(hash, msg);
+ } catch (NumberFormatException expected) {
+ // Not a messageHash - skip it
+ }
+ }
+ }
+
+ /**
+ * Returns the number of loaded log definitions kept in memory.
+ */
+ public synchronized int knownViewerStringsNumber() {
+ if (mLogMessageMap != null) {
+ return mLogMessageMap.size();
+ }
+ return 0;
+ }
+
+}
diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
new file mode 100644
index 000000000000..53062d837cfa
--- /dev/null
+++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
@@ -0,0 +1,559 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.protolog;
+
+import static perfetto.protos.PerfettoTrace.InternedData.PROTOLOG_STACKTRACE;
+import static perfetto.protos.PerfettoTrace.InternedData.PROTOLOG_STRING_ARGS;
+import static perfetto.protos.PerfettoTrace.InternedString.IID;
+import static perfetto.protos.PerfettoTrace.InternedString.STR;
+import static perfetto.protos.PerfettoTrace.ProtoLogMessage.BOOLEAN_PARAMS;
+import static perfetto.protos.PerfettoTrace.ProtoLogMessage.DOUBLE_PARAMS;
+import static perfetto.protos.PerfettoTrace.ProtoLogMessage.STACKTRACE_IID;
+import static perfetto.protos.PerfettoTrace.ProtoLogMessage.MESSAGE_ID;
+import static perfetto.protos.PerfettoTrace.ProtoLogMessage.SINT64_PARAMS;
+import static perfetto.protos.PerfettoTrace.ProtoLogMessage.STR_PARAM_IIDS;
+import static perfetto.protos.PerfettoTrace.ProtoLogViewerConfig.GROUPS;
+import static perfetto.protos.PerfettoTrace.ProtoLogViewerConfig.Group.ID;
+import static perfetto.protos.PerfettoTrace.ProtoLogViewerConfig.Group.NAME;
+import static perfetto.protos.PerfettoTrace.ProtoLogViewerConfig.Group.TAG;
+import static perfetto.protos.PerfettoTrace.ProtoLogViewerConfig.MESSAGES;
+import static perfetto.protos.PerfettoTrace.ProtoLogViewerConfig.MessageData.GROUP_ID;
+import static perfetto.protos.PerfettoTrace.ProtoLogViewerConfig.MessageData.LEVEL;
+import static perfetto.protos.PerfettoTrace.ProtoLogViewerConfig.MessageData.MESSAGE;
+import static perfetto.protos.PerfettoTrace.TracePacket.INTERNED_DATA;
+import static perfetto.protos.PerfettoTrace.TracePacket.PROTOLOG_MESSAGE;
+import static perfetto.protos.PerfettoTrace.TracePacket.PROTOLOG_VIEWER_CONFIG;
+import static perfetto.protos.PerfettoTrace.TracePacket.SEQUENCE_FLAGS;
+import static perfetto.protos.PerfettoTrace.TracePacket.SEQ_INCREMENTAL_STATE_CLEARED;
+import static perfetto.protos.PerfettoTrace.TracePacket.SEQ_NEEDS_INCREMENTAL_STATE;
+import static perfetto.protos.PerfettoTrace.TracePacket.TIMESTAMP;
+
+import android.annotation.Nullable;
+import android.os.ShellCommand;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.text.TextUtils;
+import android.tracing.perfetto.DataSourceParams;
+import android.tracing.perfetto.InitArguments;
+import android.tracing.perfetto.Producer;
+import android.tracing.perfetto.TracingContext;
+import android.util.LongArray;
+import android.util.Slog;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ILogger;
+import com.android.internal.protolog.common.IProtoLog;
+import com.android.internal.protolog.common.IProtoLogGroup;
+import com.android.internal.protolog.common.LogDataType;
+import com.android.internal.protolog.common.LogLevel;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import perfetto.protos.PerfettoTrace.ProtoLogViewerConfig.MessageData;
+
+/**
+ * A service for the ProtoLog logging system.
+ */
+public class PerfettoProtoLogImpl implements IProtoLog {
+ private final TreeMap<String, IProtoLogGroup> mLogGroups = new TreeMap<>();
+ private static final String LOG_TAG = "ProtoLog";
+ private final AtomicInteger mTracingInstances = new AtomicInteger();
+
+ private final ProtoLogDataSource mDataSource = new ProtoLogDataSource(
+ this.mTracingInstances::incrementAndGet,
+ this::dumpTransitionTraceConfig,
+ this.mTracingInstances::decrementAndGet
+ );
+ private final ProtoLogViewerConfigReader mViewerConfigReader;
+ private final ViewerConfigInputStreamProvider mViewerConfigInputStreamProvider;
+
+ public PerfettoProtoLogImpl(String viewerConfigFilePath) {
+ this(() -> {
+ try {
+ return new ProtoInputStream(new FileInputStream(viewerConfigFilePath));
+ } catch (FileNotFoundException e) {
+ Slog.w(LOG_TAG, "Failed to load viewer config file " + viewerConfigFilePath, e);
+ return null;
+ }
+ });
+ }
+
+ public PerfettoProtoLogImpl(ViewerConfigInputStreamProvider viewerConfigInputStreamProvider) {
+ this(viewerConfigInputStreamProvider,
+ new ProtoLogViewerConfigReader(viewerConfigInputStreamProvider));
+ }
+
+ @VisibleForTesting
+ public PerfettoProtoLogImpl(
+ ViewerConfigInputStreamProvider viewerConfigInputStreamProvider,
+ ProtoLogViewerConfigReader viewerConfigReader
+ ) {
+ Producer.init(InitArguments.DEFAULTS);
+ mDataSource.register(DataSourceParams.DEFAULTS);
+ this.mViewerConfigInputStreamProvider = viewerConfigInputStreamProvider;
+ this.mViewerConfigReader = viewerConfigReader;
+ }
+
+ /**
+ * Main log method, do not call directly.
+ */
+ @VisibleForTesting
+ @Override
+ public void log(LogLevel level, IProtoLogGroup group, long messageHash, int paramsMask,
+ @Nullable String messageString, Object[] args) {
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "log");
+
+ long tsNanos = SystemClock.elapsedRealtimeNanos();
+ try {
+ logToProto(level, group.name(), messageHash, paramsMask, args, tsNanos);
+ if (group.isLogToLogcat()) {
+ logToLogcat(group.getTag(), level, messageHash, messageString, args);
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ }
+ }
+
+ private void dumpTransitionTraceConfig() {
+ ProtoInputStream pis = mViewerConfigInputStreamProvider.getInputStream();
+
+ if (pis == null) {
+ Slog.w(LOG_TAG, "Failed to get viewer input stream.");
+ return;
+ }
+
+ mDataSource.trace(ctx -> {
+ final ProtoOutputStream os = ctx.newTracePacket();
+
+ os.write(TIMESTAMP, SystemClock.elapsedRealtimeNanos());
+
+ final long outProtologViewerConfigToken = os.start(PROTOLOG_VIEWER_CONFIG);
+ while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ if (pis.getFieldNumber() == (int) MESSAGES) {
+ final long inMessageToken = pis.start(MESSAGES);
+ final long outMessagesToken = os.start(MESSAGES);
+
+ while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pis.getFieldNumber()) {
+ case (int) MessageData.MESSAGE_ID:
+ os.write(MessageData.MESSAGE_ID,
+ pis.readLong(MessageData.MESSAGE_ID));
+ break;
+ case (int) MESSAGE:
+ os.write(MESSAGE, pis.readString(MESSAGE));
+ break;
+ case (int) LEVEL:
+ os.write(LEVEL, pis.readInt(LEVEL));
+ break;
+ case (int) GROUP_ID:
+ os.write(GROUP_ID, pis.readInt(GROUP_ID));
+ break;
+ default:
+ throw new RuntimeException(
+ "Unexpected field id " + pis.getFieldNumber());
+ }
+ }
+
+ pis.end(inMessageToken);
+ os.end(outMessagesToken);
+ }
+
+ if (pis.getFieldNumber() == (int) GROUPS) {
+ final long inGroupToken = pis.start(GROUPS);
+ final long outGroupToken = os.start(GROUPS);
+
+ while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pis.getFieldNumber()) {
+ case (int) ID:
+ int id = pis.readInt(ID);
+ os.write(ID, id);
+ break;
+ case (int) NAME:
+ String name = pis.readString(NAME);
+ os.write(NAME, name);
+ break;
+ case (int) TAG:
+ String tag = pis.readString(TAG);
+ os.write(TAG, tag);
+ break;
+ default:
+ throw new RuntimeException(
+ "Unexpected field id " + pis.getFieldNumber());
+ }
+ }
+
+ pis.end(inGroupToken);
+ os.end(outGroupToken);
+ }
+ }
+
+ os.end(outProtologViewerConfigToken);
+
+ ctx.flush();
+ });
+
+ mDataSource.flush();
+ }
+
+ private void logToLogcat(String tag, LogLevel level, long messageHash,
+ @Nullable String messageString, Object[] args) {
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "logToLogcat");
+ try {
+ doLogToLogcat(tag, level, messageHash, messageString, args);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ }
+ }
+
+ private void doLogToLogcat(String tag, LogLevel level, long messageHash,
+ @androidx.annotation.Nullable String messageString, Object[] args) {
+ String message = null;
+ if (messageString == null) {
+ messageString = mViewerConfigReader.getViewerString(messageHash);
+ }
+ if (messageString != null) {
+ if (args != null) {
+ try {
+ message = TextUtils.formatSimple(messageString, args);
+ } catch (Exception ex) {
+ Slog.w(LOG_TAG, "Invalid ProtoLog format string.", ex);
+ }
+ } else {
+ message = messageString;
+ }
+ }
+ if (message == null) {
+ StringBuilder builder = new StringBuilder("UNKNOWN MESSAGE (" + messageHash + ")");
+ for (Object o : args) {
+ builder.append(" ").append(o);
+ }
+ message = builder.toString();
+ }
+ passToLogcat(tag, level, message);
+ }
+
+ /**
+ * SLog wrapper.
+ */
+ @VisibleForTesting
+ public void passToLogcat(String tag, LogLevel level, String message) {
+ switch (level) {
+ case DEBUG:
+ Slog.d(tag, message);
+ break;
+ case VERBOSE:
+ Slog.v(tag, message);
+ break;
+ case INFO:
+ Slog.i(tag, message);
+ break;
+ case WARN:
+ Slog.w(tag, message);
+ break;
+ case ERROR:
+ Slog.e(tag, message);
+ break;
+ case WTF:
+ Slog.wtf(tag, message);
+ break;
+ }
+ }
+
+ private void logToProto(LogLevel level, String groupName, long messageHash, int paramsMask,
+ Object[] args, long tsNanos) {
+ if (!isProtoEnabled()) {
+ return;
+ }
+
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "logToProto");
+ try {
+ doLogToProto(level, groupName, messageHash, paramsMask, args, tsNanos);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ }
+ }
+
+ private void doLogToProto(LogLevel level, String groupName, long messageHash, int paramsMask,
+ Object[] args, long tsNanos) {
+ mDataSource.trace(ctx -> {
+ final ProtoLogDataSource.TlsState tlsState = ctx.getCustomTlsState();
+ final LogLevel logFrom = tlsState.getLogFromLevel(groupName);
+
+ if (level.ordinal() < logFrom.ordinal()) {
+ return;
+ }
+
+ if (args != null) {
+ // Intern all string params before creating the trace packet for the proto
+ // message so that the interned strings appear before in the trace to make the
+ // trace processing easier.
+ int argIndex = 0;
+ for (Object o : args) {
+ int type = LogDataType.bitmaskToLogDataType(paramsMask, argIndex);
+ if (type == LogDataType.STRING) {
+ internStringArg(ctx, o.toString());
+ }
+ argIndex++;
+ }
+ }
+
+ int internedStacktrace = 0;
+ if (tlsState.getShouldCollectStacktrace(groupName)) {
+ // Intern stackstraces before creating the trace packet for the proto message so
+ // that the interned stacktrace strings appear before in the trace to make the
+ // trace processing easier.
+ String stacktrace = collectStackTrace();
+ internedStacktrace = internStacktraceString(ctx, stacktrace);
+ }
+
+ final ProtoOutputStream os = ctx.newTracePacket();
+ os.write(TIMESTAMP, tsNanos);
+ long token = os.start(PROTOLOG_MESSAGE);
+ os.write(MESSAGE_ID, messageHash);
+
+ boolean needsIncrementalState = false;
+
+ if (args != null) {
+
+ int argIndex = 0;
+ LongArray longParams = new LongArray();
+ ArrayList<Double> doubleParams = new ArrayList<>();
+ ArrayList<Boolean> booleanParams = new ArrayList<>();
+ for (Object o : args) {
+ int type = LogDataType.bitmaskToLogDataType(paramsMask, argIndex);
+ try {
+ switch (type) {
+ case LogDataType.STRING:
+ final int internedStringId = internStringArg(ctx, o.toString());
+ os.write(STR_PARAM_IIDS, internedStringId);
+ needsIncrementalState = true;
+ break;
+ case LogDataType.LONG:
+ longParams.add(((Number) o).longValue());
+ break;
+ case LogDataType.DOUBLE:
+ doubleParams.add(((Number) o).doubleValue());
+ break;
+ case LogDataType.BOOLEAN:
+ booleanParams.add((boolean) o);
+ break;
+ }
+ } catch (ClassCastException ex) {
+ Slog.e(LOG_TAG, "Invalid ProtoLog paramsMask", ex);
+ }
+ argIndex++;
+ }
+
+ for (int i = 0; i < longParams.size(); ++i) {
+ os.write(SINT64_PARAMS, longParams.get(i));
+ }
+ doubleParams.forEach(it -> os.write(DOUBLE_PARAMS, it));
+ // Converting booleans to int because Perfetto doesn't yet support repeated
+ // booleans, so we use a repeated integers instead (b/313651412).
+ booleanParams.forEach(it -> os.write(BOOLEAN_PARAMS, it ? 1 : 0));
+ }
+
+ if (tlsState.getShouldCollectStacktrace(groupName)) {
+ os.write(STACKTRACE_IID, internedStacktrace);
+ }
+
+ os.end(token);
+
+ if (needsIncrementalState) {
+ os.write(SEQUENCE_FLAGS, SEQ_NEEDS_INCREMENTAL_STATE);
+ }
+
+ });
+ }
+
+ private static final int STACK_SIZE_TO_PROTO_LOG_ENTRY_CALL = 12;
+
+ private String collectStackTrace() {
+ StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
+ StringWriter sw = new StringWriter();
+ try (PrintWriter pw = new PrintWriter(sw)) {
+ for (int i = STACK_SIZE_TO_PROTO_LOG_ENTRY_CALL; i < stackTrace.length; ++i) {
+ pw.println("\tat " + stackTrace[i]);
+ }
+ }
+
+ return sw.toString();
+ }
+
+ private int internStacktraceString(TracingContext<ProtoLogDataSource.Instance,
+ ProtoLogDataSource.TlsState,
+ ProtoLogDataSource.IncrementalState> ctx,
+ String stacktrace) {
+ final ProtoLogDataSource.IncrementalState incrementalState = ctx.getIncrementalState();
+ return internString(ctx, incrementalState.stacktraceInterningMap,
+ PROTOLOG_STACKTRACE, stacktrace);
+ }
+
+ private int internStringArg(
+ TracingContext<ProtoLogDataSource.Instance,
+ ProtoLogDataSource.TlsState,
+ ProtoLogDataSource.IncrementalState> ctx,
+ String string
+ ) {
+ final ProtoLogDataSource.IncrementalState incrementalState = ctx.getIncrementalState();
+ return internString(ctx, incrementalState.argumentInterningMap,
+ PROTOLOG_STRING_ARGS, string);
+ }
+
+ private int internString(
+ TracingContext<ProtoLogDataSource.Instance,
+ ProtoLogDataSource.TlsState,
+ ProtoLogDataSource.IncrementalState> ctx,
+ Map<String, Integer> internMap,
+ long fieldId,
+ String string
+ ) {
+ final ProtoLogDataSource.IncrementalState incrementalState = ctx.getIncrementalState();
+
+ if (!incrementalState.clearReported) {
+ final ProtoOutputStream os = ctx.newTracePacket();
+ os.write(SEQUENCE_FLAGS, SEQ_INCREMENTAL_STATE_CLEARED);
+ incrementalState.clearReported = true;
+ }
+
+ if (!internMap.containsKey(string)) {
+ final int internedIndex = internMap.size() + 1;
+ internMap.put(string, internedIndex);
+
+ final ProtoOutputStream os = ctx.newTracePacket();
+ final long token = os.start(INTERNED_DATA);
+ final long innerToken = os.start(fieldId);
+ os.write(IID, internedIndex);
+ os.write(STR, string.getBytes());
+ os.end(innerToken);
+ os.end(token);
+ }
+
+ return internMap.get(string);
+ }
+
+ /**
+ * Responds to a shell command.
+ */
+ public int onShellCommand(ShellCommand shell) {
+ PrintWriter pw = shell.getOutPrintWriter();
+ String cmd = shell.getNextArg();
+ if (cmd == null) {
+ return unknownCommand(pw);
+ }
+ ArrayList<String> args = new ArrayList<>();
+ String arg;
+ while ((arg = shell.getNextArg()) != null) {
+ args.add(arg);
+ }
+ final ILogger logger = (msg) -> logAndPrintln(pw, msg);
+ String[] groups = args.toArray(new String[args.size()]);
+ switch (cmd) {
+ case "enable-text":
+ return this.startLoggingToLogcat(groups, logger);
+ case "disable-text":
+ return this.stopLoggingToLogcat(groups, logger);
+ default:
+ return unknownCommand(pw);
+ }
+ }
+
+ private int unknownCommand(PrintWriter pw) {
+ pw.println("Unknown command");
+ pw.println("Window manager logging options:");
+ pw.println(" enable-text [group...]: Enable logcat logging for given groups");
+ pw.println(" disable-text [group...]: Disable logcat logging for given groups");
+ return -1;
+ }
+
+ /**
+ * Returns {@code true} iff logging to proto is enabled.
+ */
+ public boolean isProtoEnabled() {
+ return mTracingInstances.get() > 0;
+ }
+
+ /**
+ * Start text logging
+ * @param groups Groups to start text logging for
+ * @param logger A logger to write status updates to
+ * @return status code
+ */
+ public int startLoggingToLogcat(String[] groups, ILogger logger) {
+ mViewerConfigReader.loadViewerConfig(logger);
+ return setTextLogging(true, logger, groups);
+ }
+
+ /**
+ * Stop text logging
+ * @param groups Groups to start text logging for
+ * @param logger A logger to write status updates to
+ * @return status code
+ */
+ public int stopLoggingToLogcat(String[] groups, ILogger logger) {
+ mViewerConfigReader.unloadViewerConfig();
+ return setTextLogging(false, logger, groups);
+ }
+
+ /**
+ * Start logging the stack trace of the when the log message happened for target groups
+ * @return status code
+ */
+ public int startLoggingStackTrace(String[] groups, ILogger logger) {
+ return -1;
+ }
+
+ /**
+ * Stop logging the stack trace of the when the log message happened for target groups
+ * @return status code
+ */
+ public int stopLoggingStackTrace() {
+ return -1;
+ }
+
+ private int setTextLogging(boolean value, ILogger logger, String... groups) {
+ for (int i = 0; i < groups.length; i++) {
+ String group = groups[i];
+ IProtoLogGroup g = mLogGroups.get(group);
+ if (g != null) {
+ g.setLogToLogcat(value);
+ } else {
+ logger.log("No IProtoLogGroup named " + group);
+ return -1;
+ }
+ }
+ return 0;
+ }
+
+ static void logAndPrintln(@Nullable PrintWriter pw, String msg) {
+ Slog.i(LOG_TAG, msg);
+ if (pw != null) {
+ pw.println(msg);
+ pw.flush();
+ }
+ }
+}
+
diff --git a/core/java/com/android/internal/protolog/ProtoLogDataSource.java b/core/java/com/android/internal/protolog/ProtoLogDataSource.java
new file mode 100644
index 000000000000..a8ff75d6595e
--- /dev/null
+++ b/core/java/com/android/internal/protolog/ProtoLogDataSource.java
@@ -0,0 +1,294 @@
+/*
+ * 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.internal.protolog;
+
+import static perfetto.protos.PerfettoTrace.DataSourceConfig.PROTOLOG_CONFIG;
+import static perfetto.protos.PerfettoTrace.ProtoLogConfig.GROUP_OVERRIDES;
+import static perfetto.protos.PerfettoTrace.ProtoLogConfig.TRACING_MODE;
+import static perfetto.protos.PerfettoTrace.ProtoLogGroup.COLLECT_STACKTRACE;
+import static perfetto.protos.PerfettoTrace.ProtoLogGroup.LOG_FROM;
+import static perfetto.protos.PerfettoTrace.ProtoLogGroup.GROUP_NAME;
+
+import android.tracing.perfetto.CreateIncrementalStateArgs;
+import android.tracing.perfetto.CreateTlsStateArgs;
+import android.tracing.perfetto.DataSource;
+import android.tracing.perfetto.DataSourceInstance;
+import android.tracing.perfetto.FlushCallbackArguments;
+import android.tracing.perfetto.StartCallbackArguments;
+import android.tracing.perfetto.StopCallbackArguments;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.WireTypeMismatchException;
+
+import com.android.internal.protolog.common.LogLevel;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import perfetto.protos.PerfettoTrace;
+
+public class ProtoLogDataSource extends DataSource<ProtoLogDataSource.Instance,
+ ProtoLogDataSource.TlsState,
+ ProtoLogDataSource.IncrementalState> {
+
+ private final Runnable mOnStart;
+ private final Runnable mOnFlush;
+ private final Runnable mOnStop;
+
+ public ProtoLogDataSource(Runnable onStart, Runnable onFlush, Runnable onStop) {
+ super("android.protolog");
+ this.mOnStart = onStart;
+ this.mOnFlush = onFlush;
+ this.mOnStop = onStop;
+ }
+
+ @Override
+ public Instance createInstance(ProtoInputStream configStream, int instanceIndex) {
+ ProtoLogConfig config = null;
+
+ try {
+ while (configStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ try {
+ if (configStream.getFieldNumber() == (int) PROTOLOG_CONFIG) {
+ if (config != null) {
+ throw new RuntimeException("ProtoLog config already set in loop");
+ }
+ config = readProtoLogConfig(configStream);
+ }
+ } catch (WireTypeMismatchException e) {
+ throw new RuntimeException("Failed to parse ProtoLog DataSource config", e);
+ }
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to read ProtoLog DataSource config", e);
+ }
+
+ if (config == null) {
+ // No config found
+ config = ProtoLogConfig.DEFAULT;
+ }
+
+ return new Instance(
+ this, instanceIndex, config, mOnStart, mOnFlush, mOnStop);
+ }
+
+ @Override
+ public TlsState createTlsState(CreateTlsStateArgs<Instance> args) {
+ try (Instance dsInstance = args.getDataSourceInstanceLocked()) {
+ if (dsInstance == null) {
+ // Datasource instance has been removed
+ return new TlsState(ProtoLogConfig.DEFAULT);
+ }
+ return new TlsState(dsInstance.mConfig);
+ }
+ }
+
+ @Override
+ public IncrementalState createIncrementalState(CreateIncrementalStateArgs<Instance> args) {
+ return new IncrementalState();
+ }
+
+ public static class TlsState {
+ private final ProtoLogConfig mConfig;
+
+ private TlsState(ProtoLogConfig config) {
+ this.mConfig = config;
+ }
+
+ /**
+ * Get the log from level for a group.
+ * @param groupTag The tag of the group to get the log from level.
+ * @return The lowest LogLevel (inclusive) to log message from.
+ */
+ public LogLevel getLogFromLevel(String groupTag) {
+ return getConfigFor(groupTag).logFrom;
+ }
+
+ /**
+ * Get if the stacktrace for the log message should be collected for this group.
+ * @param groupTag The tag of the group to get whether or not a stacktrace was requested.
+ * @return True iff a stacktrace was requested to be collected from this group in the
+ * tracing config.
+ */
+ public boolean getShouldCollectStacktrace(String groupTag) {
+ return getConfigFor(groupTag).collectStackTrace;
+ }
+
+ private GroupConfig getConfigFor(String groupTag) {
+ return mConfig.getConfigFor(groupTag);
+ }
+ }
+
+ public static class IncrementalState {
+ public final Map<String, Integer> argumentInterningMap = new HashMap<>();
+ public final Map<String, Integer> stacktraceInterningMap = new HashMap<>();
+ public boolean clearReported = false;
+ }
+
+ private static class ProtoLogConfig {
+ private final LogLevel mDefaultLogFromLevel;
+ private final Map<String, GroupConfig> mGroupConfigs;
+
+ private static final ProtoLogConfig DEFAULT =
+ new ProtoLogConfig(LogLevel.WTF, new HashMap<>());
+
+ private ProtoLogConfig(
+ LogLevel defaultLogFromLevel, Map<String, GroupConfig> groupConfigs) {
+ this.mDefaultLogFromLevel = defaultLogFromLevel;
+ this.mGroupConfigs = groupConfigs;
+ }
+
+ private GroupConfig getConfigFor(String groupTag) {
+ return mGroupConfigs.getOrDefault(groupTag, getDefaultGroupConfig());
+ }
+
+ private GroupConfig getDefaultGroupConfig() {
+ return new GroupConfig(mDefaultLogFromLevel, false);
+ }
+ }
+
+ public static class GroupConfig {
+ public final LogLevel logFrom;
+ public final boolean collectStackTrace;
+
+ public GroupConfig(LogLevel logFromLevel, boolean collectStackTrace) {
+ this.logFrom = logFromLevel;
+ this.collectStackTrace = collectStackTrace;
+ }
+ }
+
+ private ProtoLogConfig readProtoLogConfig(ProtoInputStream configStream)
+ throws IOException {
+ final long config_token = configStream.start(PROTOLOG_CONFIG);
+
+ LogLevel defaultLogFromLevel = LogLevel.WTF;
+ final Map<String, GroupConfig> groupConfigs = new HashMap<>();
+
+ while (configStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ if (configStream.getFieldNumber() == (int) TRACING_MODE) {
+ int tracingMode = configStream.readInt(TRACING_MODE);
+ switch (tracingMode) {
+ case PerfettoTrace.ProtoLogConfig.DEFAULT:
+ break;
+ case PerfettoTrace.ProtoLogConfig.ENABLE_ALL:
+ defaultLogFromLevel = LogLevel.DEBUG;
+ break;
+ default:
+ throw new RuntimeException("Unhandled ProtoLog tracing mode type");
+ }
+ }
+ if (configStream.getFieldNumber() == (int) GROUP_OVERRIDES) {
+ final long group_overrides_token = configStream.start(GROUP_OVERRIDES);
+
+ String tag = null;
+ LogLevel logFromLevel = defaultLogFromLevel;
+ boolean collectStackTrace = false;
+ while (configStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ if (configStream.getFieldNumber() == (int) GROUP_NAME) {
+ tag = configStream.readString(GROUP_NAME);
+ }
+ if (configStream.getFieldNumber() == (int) LOG_FROM) {
+ final int logFromInt = configStream.readInt(LOG_FROM);
+ switch (logFromInt) {
+ case (PerfettoTrace.PROTOLOG_LEVEL_DEBUG): {
+ logFromLevel = LogLevel.DEBUG;
+ break;
+ }
+ case (PerfettoTrace.PROTOLOG_LEVEL_VERBOSE): {
+ logFromLevel = LogLevel.VERBOSE;
+ break;
+ }
+ case (PerfettoTrace.PROTOLOG_LEVEL_INFO): {
+ logFromLevel = LogLevel.INFO;
+ break;
+ }
+ case (PerfettoTrace.PROTOLOG_LEVEL_WARN): {
+ logFromLevel = LogLevel.WARN;
+ break;
+ }
+ case (PerfettoTrace.PROTOLOG_LEVEL_ERROR): {
+ logFromLevel = LogLevel.ERROR;
+ break;
+ }
+ case (PerfettoTrace.PROTOLOG_LEVEL_WTF): {
+ logFromLevel = LogLevel.WTF;
+ break;
+ }
+ default: {
+ throw new RuntimeException("Unhandled log level");
+ }
+ }
+ }
+ if (configStream.getFieldNumber() == (int) COLLECT_STACKTRACE) {
+ collectStackTrace = configStream.readBoolean(COLLECT_STACKTRACE);
+ }
+ }
+
+ if (tag == null) {
+ throw new RuntimeException("Failed to decode proto config. "
+ + "Got a group override without a group tag.");
+ }
+
+ groupConfigs.put(tag, new GroupConfig(logFromLevel, collectStackTrace));
+
+ configStream.end(group_overrides_token);
+ }
+ }
+
+ configStream.end(config_token);
+
+ return new ProtoLogConfig(defaultLogFromLevel, groupConfigs);
+ }
+
+ public static class Instance extends DataSourceInstance {
+
+ private final Runnable mOnStart;
+ private final Runnable mOnFlush;
+ private final Runnable mOnStop;
+ private final ProtoLogConfig mConfig;
+
+ public Instance(
+ DataSource<Instance, TlsState, IncrementalState> dataSource,
+ int instanceIdx,
+ ProtoLogConfig config,
+ Runnable onStart,
+ Runnable onFlush,
+ Runnable onStop
+ ) {
+ super(dataSource, instanceIdx);
+ this.mOnStart = onStart;
+ this.mOnFlush = onFlush;
+ this.mOnStop = onStop;
+ this.mConfig = config;
+ }
+
+ @Override
+ public void onStart(StartCallbackArguments args) {
+ this.mOnStart.run();
+ }
+
+ @Override
+ public void onFlush(FlushCallbackArguments args) {
+ this.mOnFlush.run();
+ }
+
+ @Override
+ public void onStop(StopCallbackArguments args) {
+ this.mOnStop.run();
+ }
+ }
+}
diff --git a/core/java/com/android/internal/protolog/ProtoLogImpl.java b/core/java/com/android/internal/protolog/ProtoLogImpl.java
index 527cfddf6d8e..78bed9478d84 100644
--- a/core/java/com/android/internal/protolog/ProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/ProtoLogImpl.java
@@ -16,30 +16,35 @@
package com.android.internal.protolog;
+import static com.android.internal.protolog.common.ProtoLogToolInjected.Value.LEGACY_OUTPUT_FILE_PATH;
+import static com.android.internal.protolog.common.ProtoLogToolInjected.Value.LEGACY_VIEWER_CONFIG_PATH;
+import static com.android.internal.protolog.common.ProtoLogToolInjected.Value.VIEWER_CONFIG_PATH;
+
import android.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.IProtoLog;
import com.android.internal.protolog.common.IProtoLogGroup;
-
-import java.io.File;
+import com.android.internal.protolog.common.LogLevel;
+import com.android.internal.protolog.common.ProtoLogToolInjected;
/**
* A service for the ProtoLog logging system.
*/
-public class ProtoLogImpl extends BaseProtoLogImpl {
- private static final int BUFFER_CAPACITY = 1024 * 1024;
- private static final String LOG_FILENAME = "/data/misc/wmtrace/wm_log.winscope";
- private static final String VIEWER_CONFIG_FILENAME = "/system/etc/protolog.conf.json.gz";
- private static final int PER_CHUNK_SIZE = 1024;
+public class ProtoLogImpl {
+ private static IProtoLog sServiceInstance = null;
- private static ProtoLogImpl sServiceInstance = null;
+ @ProtoLogToolInjected(VIEWER_CONFIG_PATH)
+ private static String sViewerConfigPath;
- static {
- addLogGroupEnum(ProtoLogGroup.values());
- }
+ @ProtoLogToolInjected(LEGACY_VIEWER_CONFIG_PATH)
+ private static String sLegacyViewerConfigPath;
+
+ @ProtoLogToolInjected(LEGACY_OUTPUT_FILE_PATH)
+ private static String sLegacyOutputFilePath;
/** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
- public static void d(IProtoLogGroup group, int messageHash, int paramsMask,
+ public static void d(IProtoLogGroup group, long messageHash, int paramsMask,
@Nullable String messageString,
Object... args) {
getSingleInstance()
@@ -47,7 +52,7 @@ public class ProtoLogImpl extends BaseProtoLogImpl {
}
/** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
- public static void v(IProtoLogGroup group, int messageHash, int paramsMask,
+ public static void v(IProtoLogGroup group, long messageHash, int paramsMask,
@Nullable String messageString,
Object... args) {
getSingleInstance().log(LogLevel.VERBOSE, group, messageHash, paramsMask, messageString,
@@ -55,21 +60,21 @@ public class ProtoLogImpl extends BaseProtoLogImpl {
}
/** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
- public static void i(IProtoLogGroup group, int messageHash, int paramsMask,
+ public static void i(IProtoLogGroup group, long messageHash, int paramsMask,
@Nullable String messageString,
Object... args) {
getSingleInstance().log(LogLevel.INFO, group, messageHash, paramsMask, messageString, args);
}
/** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
- public static void w(IProtoLogGroup group, int messageHash, int paramsMask,
+ public static void w(IProtoLogGroup group, long messageHash, int paramsMask,
@Nullable String messageString,
Object... args) {
getSingleInstance().log(LogLevel.WARN, group, messageHash, paramsMask, messageString, args);
}
/** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
- public static void e(IProtoLogGroup group, int messageHash, int paramsMask,
+ public static void e(IProtoLogGroup group, long messageHash, int paramsMask,
@Nullable String messageString,
Object... args) {
getSingleInstance()
@@ -77,40 +82,36 @@ public class ProtoLogImpl extends BaseProtoLogImpl {
}
/** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
- public static void wtf(IProtoLogGroup group, int messageHash, int paramsMask,
+ public static void wtf(IProtoLogGroup group, long messageHash, int paramsMask,
@Nullable String messageString,
Object... args) {
getSingleInstance().log(LogLevel.WTF, group, messageHash, paramsMask, messageString, args);
}
- /** Returns true iff logging is enabled for the given {@code IProtoLogGroup}. */
public static boolean isEnabled(IProtoLogGroup group) {
- return group.isLogToLogcat()
- || (group.isLogToProto() && getSingleInstance().isProtoEnabled());
+ // TODO: Implement for performance reasons, with optional level parameter?
+ return true;
}
/**
* Returns the single instance of the ProtoLogImpl singleton class.
*/
- public static synchronized ProtoLogImpl getSingleInstance() {
+ public static synchronized IProtoLog getSingleInstance() {
if (sServiceInstance == null) {
- sServiceInstance = new ProtoLogImpl(
- new File(LOG_FILENAME)
- , BUFFER_CAPACITY
- , new ProtoLogViewerConfigReader()
- , PER_CHUNK_SIZE);
+ if (android.tracing.Flags.perfettoProtolog()) {
+ sServiceInstance =
+ new PerfettoProtoLogImpl(sViewerConfigPath);
+ } else {
+ sServiceInstance =
+ new LegacyProtoLogImpl(sLegacyOutputFilePath, sLegacyViewerConfigPath);
+ }
}
return sServiceInstance;
}
@VisibleForTesting
- public static synchronized void setSingleInstance(@Nullable ProtoLogImpl instance) {
+ public static synchronized void setSingleInstance(@Nullable IProtoLog instance) {
sServiceInstance = instance;
}
-
- public ProtoLogImpl(File logFile, int bufferCapacity,
- ProtoLogViewerConfigReader viewConfigReader, int perChunkSize) {
- super(logFile, VIEWER_CONFIG_FILENAME, bufferCapacity, viewConfigReader, perChunkSize);
- }
}
diff --git a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
index aa30a7723ad9..3c206acf7c0f 100644
--- a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
+++ b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
@@ -1,125 +1,93 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
package com.android.internal.protolog;
-import android.annotation.Nullable;
-import android.util.Slog;
+import static perfetto.protos.PerfettoTrace.ProtoLogViewerConfig.MESSAGES;
+import static perfetto.protos.PerfettoTrace.ProtoLogViewerConfig.MessageData.MESSAGE;
+import static perfetto.protos.PerfettoTrace.ProtoLogViewerConfig.MessageData.MESSAGE_ID;
+
+import android.util.proto.ProtoInputStream;
-import org.json.JSONException;
-import org.json.JSONObject;
+import com.android.internal.protolog.common.ILogger;
-import java.io.BufferedReader;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.PrintWriter;
-import java.util.Iterator;
import java.util.Map;
-import java.util.TreeMap;
-import java.util.zip.GZIPInputStream;
-/**
- * Handles loading and parsing of ProtoLog viewer configuration.
- */
public class ProtoLogViewerConfigReader {
- private static final String TAG = "ProtoLogViewerConfigReader";
- private Map<Integer, String> mLogMessageMap = null;
+ private final ViewerConfigInputStreamProvider mViewerConfigInputStreamProvider;
+ private Map<Long, String> mLogMessageMap = null;
- /** Returns message format string for its hash or null if unavailable. */
- public synchronized String getViewerString(int messageHash) {
- if (mLogMessageMap != null) {
- return mLogMessageMap.get(messageHash);
- } else {
- return null;
- }
+ public ProtoLogViewerConfigReader(
+ ViewerConfigInputStreamProvider viewerConfigInputStreamProvider) {
+ this.mViewerConfigInputStreamProvider = viewerConfigInputStreamProvider;
}
/**
- * Reads the specified viewer configuration file. Does nothing if the config is already loaded.
+ * Returns message format string for its hash or null if unavailable
+ * or the viewer config is not loaded into memory.
*/
- public synchronized void loadViewerConfig(PrintWriter pw, String viewerConfigFilename) {
- try {
- loadViewerConfig(new GZIPInputStream(new FileInputStream(viewerConfigFilename)));
- logAndPrintln(pw, "Loaded " + mLogMessageMap.size()
- + " log definitions from " + viewerConfigFilename);
- } catch (FileNotFoundException e) {
- logAndPrintln(pw, "Unable to load log definitions: File "
- + viewerConfigFilename + " not found." + e);
- } catch (IOException e) {
- logAndPrintln(pw, "Unable to load log definitions: IOException while reading "
- + viewerConfigFilename + ". " + e);
- } catch (JSONException e) {
- logAndPrintln(pw, "Unable to load log definitions: JSON parsing exception while reading "
- + viewerConfigFilename + ". " + e);
+ public synchronized String getViewerString(long messageHash) {
+ if (mLogMessageMap != null) {
+ return mLogMessageMap.get(messageHash);
+ } else {
+ return null;
}
}
/**
- * Reads the specified viewer configuration input stream.
- * Does nothing if the config is already loaded.
+ * Loads the viewer config into memory. No-op if already loaded in memory.
*/
- public synchronized void loadViewerConfig(InputStream viewerConfigInputStream)
- throws IOException, JSONException {
+ public synchronized void loadViewerConfig(ILogger logger) {
if (mLogMessageMap != null) {
return;
}
- InputStreamReader config = new InputStreamReader(viewerConfigInputStream);
- BufferedReader reader = new BufferedReader(config);
- StringBuilder builder = new StringBuilder();
- String line;
- while ((line = reader.readLine()) != null) {
- builder.append(line).append('\n');
- }
- reader.close();
- JSONObject json = new JSONObject(builder.toString());
- JSONObject messages = json.getJSONObject("messages");
-
- mLogMessageMap = new TreeMap<>();
- Iterator it = messages.keys();
- while (it.hasNext()) {
- String key = (String) it.next();
- try {
- int hash = Integer.parseInt(key);
- JSONObject val = messages.getJSONObject(key);
- String msg = val.getString("message");
- mLogMessageMap.put(hash, msg);
- } catch (NumberFormatException expected) {
- // Not a messageHash - skip it
- }
+
+ try {
+ doLoadViewerConfig();
+ logger.log("Loaded " + mLogMessageMap.size() + " log definitions");
+ } catch (IOException e) {
+ logger.log("Unable to load log definitions: "
+ + "IOException while processing viewer config" + e);
}
}
/**
- * Returns the number of loaded log definitions kept in memory.
+ * Unload the viewer config from memory.
*/
- public synchronized int knownViewerStringsNumber() {
- if (mLogMessageMap != null) {
- return mLogMessageMap.size();
- }
- return 0;
+ public synchronized void unloadViewerConfig() {
+ mLogMessageMap = null;
}
- static void logAndPrintln(@Nullable PrintWriter pw, String msg) {
- Slog.i(TAG, msg);
- if (pw != null) {
- pw.println(msg);
- pw.flush();
+ private void doLoadViewerConfig() throws IOException {
+ final ProtoInputStream pis = mViewerConfigInputStreamProvider.getInputStream();
+
+ while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ if (pis.getFieldNumber() == (int) MESSAGES) {
+ final long inMessageToken = pis.start(MESSAGES);
+
+ long messageId = 0;
+ String message = null;
+ while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pis.getFieldNumber()) {
+ case (int) MESSAGE_ID:
+ messageId = pis.readLong(MESSAGE_ID);
+ break;
+ case (int) MESSAGE:
+ message = pis.readString(MESSAGE);
+ break;
+ }
+ }
+
+ if (messageId == 0) {
+ throw new IOException("Failed to get message id");
+ }
+
+ if (message == null) {
+ throw new IOException("Failed to get message string");
+ }
+
+ mLogMessageMap.put(messageId, message);
+
+ pis.end(inMessageToken);
+ }
}
}
}
diff --git a/core/java/com/android/internal/protolog/ViewerConfigInputStreamProvider.java b/core/java/com/android/internal/protolog/ViewerConfigInputStreamProvider.java
new file mode 100644
index 000000000000..334f5488425a
--- /dev/null
+++ b/core/java/com/android/internal/protolog/ViewerConfigInputStreamProvider.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.protolog;
+
+import android.util.proto.ProtoInputStream;
+
+public interface ViewerConfigInputStreamProvider {
+ /**
+ * @return a ProtoInputStream.
+ */
+ ProtoInputStream getInputStream();
+}
diff --git a/core/java/com/android/internal/protolog/common/ILogger.java b/core/java/com/android/internal/protolog/common/ILogger.java
new file mode 100644
index 000000000000..cc6fa5e73969
--- /dev/null
+++ b/core/java/com/android/internal/protolog/common/ILogger.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.protolog.common;
+
+public interface ILogger {
+ /**
+ * Log a message.
+ * @param message The log message.
+ */
+ void log(String message);
+}
diff --git a/core/java/com/android/internal/protolog/common/IProtoLog.java b/core/java/com/android/internal/protolog/common/IProtoLog.java
new file mode 100644
index 000000000000..c06d14b2075e
--- /dev/null
+++ b/core/java/com/android/internal/protolog/common/IProtoLog.java
@@ -0,0 +1,55 @@
+/*
+ * 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.internal.protolog.common;
+
+/**
+ * Interface for ProtoLog implementations.
+ */
+public interface IProtoLog {
+
+ /**
+ * Log a ProtoLog message
+ * @param logLevel Log level of the proto message.
+ * @param group The group this message belongs to.
+ * @param messageHash The hash of the message.
+ * @param paramsMask The parameters mask of the message.
+ * @param messageString The message string.
+ * @param args The arguments of the message.
+ */
+ void log(LogLevel logLevel, IProtoLogGroup group, long messageHash, int paramsMask,
+ String messageString, Object[] args);
+
+ /**
+ * Check if ProtoLog is tracing.
+ * @return true iff a ProtoLog tracing session is active.
+ */
+ boolean isProtoEnabled();
+
+ /**
+ * Start logging log groups to logcat
+ * @param groups Groups to start text logging for
+ * @return status code
+ */
+ int startLoggingToLogcat(String[] groups, ILogger logger);
+
+ /**
+ * Stop logging log groups to logcat
+ * @param groups Groups to start text logging for
+ * @return status code
+ */
+ int stopLoggingToLogcat(String[] groups, ILogger logger);
+}
diff --git a/core/java/com/android/internal/protolog/common/IProtoLogGroup.java b/core/java/com/android/internal/protolog/common/IProtoLogGroup.java
index e3db46832a6f..4e9686f99f4b 100644
--- a/core/java/com/android/internal/protolog/common/IProtoLogGroup.java
+++ b/core/java/com/android/internal/protolog/common/IProtoLogGroup.java
@@ -26,6 +26,7 @@ public interface IProtoLogGroup {
boolean isEnabled();
/**
+ * @deprecated TODO(b/324128613) remove once we migrate fully to Perfetto
* is binary logging enabled for the group.
*/
boolean isLogToProto();
diff --git a/core/java/com/android/internal/protolog/common/LogLevel.java b/core/java/com/android/internal/protolog/common/LogLevel.java
new file mode 100644
index 000000000000..16c34e1f333e
--- /dev/null
+++ b/core/java/com/android/internal/protolog/common/LogLevel.java
@@ -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.internal.protolog.common;
+
+public enum LogLevel {
+ DEBUG("d"), VERBOSE("v"), INFO("i"), WARN("w"), ERROR("e"), WTF("wtf");
+
+ public final String shortCode;
+ LogLevel(String shortCode) {
+ this.shortCode = shortCode;
+ }
+}
diff --git a/core/java/com/android/internal/protolog/common/ProtoLog.java b/core/java/com/android/internal/protolog/common/ProtoLog.java
index 8870096f3db7..18e3f66c4795 100644
--- a/core/java/com/android/internal/protolog/common/ProtoLog.java
+++ b/core/java/com/android/internal/protolog/common/ProtoLog.java
@@ -16,8 +16,6 @@
package com.android.internal.protolog.common;
-import android.util.Log;
-
/**
* ProtoLog API - exposes static logging methods. Usage of this API is similar
* to {@code android.utils.Log} class. Instead of plain text log messages each call consists of
@@ -55,9 +53,6 @@ public class ProtoLog {
throw new UnsupportedOperationException(
"ProtoLog calls MUST be processed with ProtoLogTool");
}
- if (group.isLogToLogcat()) {
- Log.d(group.getTag(), String.format(messageString, args));
- }
}
/**
@@ -73,9 +68,6 @@ public class ProtoLog {
throw new UnsupportedOperationException(
"ProtoLog calls MUST be processed with ProtoLogTool");
}
- if (group.isLogToLogcat()) {
- Log.v(group.getTag(), String.format(messageString, args));
- }
}
/**
@@ -91,9 +83,6 @@ public class ProtoLog {
throw new UnsupportedOperationException(
"ProtoLog calls MUST be processed with ProtoLogTool");
}
- if (group.isLogToLogcat()) {
- Log.i(group.getTag(), String.format(messageString, args));
- }
}
/**
@@ -109,9 +98,6 @@ public class ProtoLog {
throw new UnsupportedOperationException(
"ProtoLog calls MUST be processed with ProtoLogTool");
}
- if (group.isLogToLogcat()) {
- Log.w(group.getTag(), String.format(messageString, args));
- }
}
/**
@@ -127,9 +113,6 @@ public class ProtoLog {
throw new UnsupportedOperationException(
"ProtoLog calls MUST be processed with ProtoLogTool");
}
- if (group.isLogToLogcat()) {
- Log.e(group.getTag(), String.format(messageString, args));
- }
}
/**
@@ -145,8 +128,30 @@ public class ProtoLog {
throw new UnsupportedOperationException(
"ProtoLog calls MUST be processed with ProtoLogTool");
}
- if (group.isLogToLogcat()) {
- Log.wtf(group.getTag(), String.format(messageString, args));
+ }
+
+ /**
+ * Check if ProtoLog isEnabled for a target group.
+ * @param group Group to check enable status of.
+ * @return true iff this is being logged.
+ */
+ public static boolean isEnabled(IProtoLogGroup group) {
+ if (REQUIRE_PROTOLOGTOOL) {
+ throw new UnsupportedOperationException(
+ "ProtoLog calls MUST be processed with ProtoLogTool");
+ }
+ return false;
+ }
+
+ /**
+ * Get the single ProtoLog instance.
+ * @return A singleton instance of ProtoLog.
+ */
+ public static IProtoLog getSingleInstance() {
+ if (REQUIRE_PROTOLOGTOOL) {
+ throw new UnsupportedOperationException(
+ "ProtoLog calls MUST be processed with ProtoLogTool");
}
+ return null;
}
}
diff --git a/core/java/com/android/internal/protolog/common/ProtoLogToolInjected.java b/core/java/com/android/internal/protolog/common/ProtoLogToolInjected.java
new file mode 100644
index 000000000000..ffd0d7623852
--- /dev/null
+++ b/core/java/com/android/internal/protolog/common/ProtoLogToolInjected.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.internal.protolog.common;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@Target({ElementType.FIELD, ElementType.PARAMETER})
+public @interface ProtoLogToolInjected {
+ enum Value { VIEWER_CONFIG_PATH, LEGACY_OUTPUT_FILE_PATH, LEGACY_VIEWER_CONFIG_PATH }
+
+ Value value();
+}
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index ed43b8190f93..d463b62e62a3 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -376,4 +376,10 @@ oneway interface IStatusBar
* @param packageName of the session for which the output switcher is shown.
*/
void showMediaOutputSwitcher(String packageName);
+
+ /** Enters desktop mode.
+ *
+ * @param displayId the id of the current display.
+ */
+ void enterDesktop(int displayId);
}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index e95127be8543..1f4503a69428 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -24,6 +24,7 @@ import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.EditorInfo;
import android.window.ImeOnBackInvokedDispatcher;
+import com.android.internal.inputmethod.IBooleanListener;
import com.android.internal.inputmethod.IConnectionlessHandwritingCallback;
import com.android.internal.inputmethod.IImeTracker;
import com.android.internal.inputmethod.IInputMethodClient;
@@ -69,6 +70,8 @@ interface IInputMethodManager {
boolean hideSoftInput(in IInputMethodClient client, @nullable IBinder windowToken,
in @nullable ImeTracker.Token statsToken, int flags,
in @nullable ResultReceiver resultReceiver, int reason);
+
+ // TODO(b/293640003): Remove method once Flags.useZeroJankProxy() is enabled.
// If windowToken is null, this just does startInput(). Otherwise this reports that a window
// has gained focus, and if 'editorInfo' is non-null then also does startInput.
// @NonNull
@@ -85,6 +88,21 @@ interface IInputMethodManager {
int unverifiedTargetSdkVersion, int userId,
in ImeOnBackInvokedDispatcher imeDispatcher);
+ // If windowToken is null, this just does startInput(). Otherwise this reports that a window
+ // has gained focus, and if 'editorInfo' is non-null then also does startInput.
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ + "android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)")
+ void startInputOrWindowGainedFocusAsync(
+ /* @StartInputReason */ int startInputReason,
+ in IInputMethodClient client, in @nullable IBinder windowToken,
+ /* @StartInputFlags */ int startInputFlags,
+ /* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode,
+ /* @android.view.WindowManager.LayoutParams.Flags */ int windowFlags,
+ in @nullable EditorInfo editorInfo, in @nullable IRemoteInputConnection inputConnection,
+ in @nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
+ int unverifiedTargetSdkVersion, int userId,
+ in ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq);
+
void showInputMethodPickerFromClient(in IInputMethodClient client,
int auxiliarySubtypeMode);
@@ -160,6 +178,12 @@ interface IInputMethodManager {
boolean acceptStylusHandwritingDelegation(in IInputMethodClient client, in int userId,
in String delegatePackageName, in String delegatorPackageName, int flags);
+ /** Accepts and starts a stylus handwriting session for the delegate view and provides result
+ * async **/
+ oneway void acceptStylusHandwritingDelegationAsync(in IInputMethodClient client, in int userId,
+ in String delegatePackageName, in String delegatorPackageName, int flags,
+ in IBooleanListener callback);
+
/** Returns {@code true} if currently selected IME supports Stylus handwriting. */
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ "android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)")
diff --git a/core/java/com/android/internal/widget/CallLayout.java b/core/java/com/android/internal/widget/CallLayout.java
index 89f46599322e..c85257578492 100644
--- a/core/java/com/android/internal/widget/CallLayout.java
+++ b/core/java/com/android/internal/widget/CallLayout.java
@@ -31,6 +31,7 @@ import android.view.RemotableViewMethod;
import android.widget.FrameLayout;
import android.widget.RemoteViews;
import android.widget.TextView;
+import android.widget.flags.Flags;
import com.android.internal.R;
@@ -41,7 +42,17 @@ import com.android.internal.R;
public class CallLayout extends FrameLayout {
private final PeopleHelper mPeopleHelper = new PeopleHelper();
+ /**
+ * Layout Color is used for creating CallLayout person avatar.
+ * It will be set on the background thread during CallLayout's inflation
+ * when call_style_set_data_async is enabled.
+ */
private int mLayoutColor;
+ /**
+ * LargeIcon is used for creating CallLayout person avatar.
+ * It will be set on the background thread during CallLayout's inflation
+ * when call_style_set_data_async is enabled.
+ */
private Icon mLargeIcon;
private Person mUser;
@@ -49,7 +60,6 @@ public class CallLayout extends FrameLayout {
private CachingIconView mIcon;
private CachingIconView mConversationIconBadgeBg;
private TextView mConversationText;
- private boolean mSetDataAsyncEnabled = false;
public CallLayout(@NonNull Context context) {
super(context);
@@ -103,7 +113,19 @@ public class CallLayout extends FrameLayout {
return icon;
}
- @RemotableViewMethod
+ /**
+ * async version of {@link CallLayout#setLayoutColor}
+ */
+ public Runnable setLayoutColorAsync(int color) {
+ if (!Flags.callStyleSetDataAsync()) {
+ return () -> setLayoutColor(color);
+ }
+
+ mLayoutColor = color;
+ return () -> {};
+ }
+
+ @RemotableViewMethod(asyncImpl = "setLayoutColorAsync")
public void setLayoutColor(int color) {
mLayoutColor = color;
}
@@ -116,7 +138,19 @@ public class CallLayout extends FrameLayout {
mConversationIconBadgeBg.setImageTintList(ColorStateList.valueOf(color));
}
- @RemotableViewMethod
+ /**
+ * async version of {@link CallLayout#setLargeIcon}
+ */
+ public Runnable setLargeIconAsync(Icon largeIcon) {
+ if (!Flags.callStyleSetDataAsync()) {
+ return () -> setLargeIcon(largeIcon);
+ }
+
+ mLargeIcon = largeIcon;
+ return () -> {};
+ }
+
+ @RemotableViewMethod(asyncImpl = "setLargeIconAsync")
public void setLargeIcon(Icon largeIcon) {
mLargeIcon = largeIcon;
}
@@ -133,16 +167,11 @@ public class CallLayout extends FrameLayout {
mConversationIconView.setImageIcon(icon);
}
-
- public void setSetDataAsyncEnabled(boolean setDataAsyncEnabled) {
- mSetDataAsyncEnabled = setDataAsyncEnabled;
- }
-
/**
* Async implementation for setData
*/
public Runnable setDataAsync(Bundle extras) {
- if (!mSetDataAsyncEnabled) {
+ if (!Flags.callStyleSetDataAsync()) {
return () -> setData(extras);
}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index b5b3a48dacb7..e46b8d7c5fae 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1615,7 +1615,8 @@ public class LockPatternUtils {
STRONG_AUTH_REQUIRED_AFTER_TIMEOUT,
STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN,
STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT,
- SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED})
+ SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED,
+ SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST})
@Retention(RetentionPolicy.SOURCE)
public @interface StrongAuthFlags {}
@@ -1641,7 +1642,8 @@ public class LockPatternUtils {
/**
* Strong authentication is required because the user has been locked out after too many
- * attempts.
+ * attempts using primary auth methods (i.e. PIN/pattern/password) from the lock screen,
+ * Android Settings, and BiometricPrompt where user authentication is required.
*/
public static final int STRONG_AUTH_REQUIRED_AFTER_LOCKOUT = 0x8;
@@ -1674,12 +1676,23 @@ public class LockPatternUtils {
public static final int SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED = 0x100;
/**
+ * Some authentication is required because adaptive auth has requested to lock device due to
+ * repeated failed primary auth (i.e. PIN/pattern/password) or biometric auth attempts which
+ * can come from Android Settings or BiometricPrompt where user authentication is required,
+ * in addition to from the lock screen. When a risk is determined, adaptive auth will
+ * proactively prompt the lock screen and will require users to re-enter the device with
+ * either primary auth or biometric auth (if not prohibited by other flags).
+ */
+ public static final int SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST = 0x200;
+
+ /**
* Strong auth flags that do not prevent biometric methods from being accepted as auth.
* If any other flags are set, biometric authentication is disabled.
*/
private static final int ALLOWING_BIOMETRIC = STRONG_AUTH_NOT_REQUIRED
| SOME_AUTH_REQUIRED_AFTER_USER_REQUEST
- | SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED;
+ | SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED
+ | SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST;
private final SparseIntArray mStrongAuthRequiredForUser = new SparseIntArray();
private final H mHandler;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
index a7260bb7c14b..c34730fdef04 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
@@ -49,8 +49,13 @@ public class RemoteComposeBuffer {
this.mRemoteComposeState = remoteComposeState;
}
- public void reset() {
- mBuffer.reset();
+ /**
+ * Reset the internal buffers
+ *
+ * @param expectedSize provided hint for the main buffer size
+ */
+ public void reset(int expectedSize) {
+ mBuffer.reset(expectedSize);
mRemoteComposeState.reset();
}
@@ -288,8 +293,7 @@ public class RemoteComposeBuffer {
public static void read(InputStream fd, RemoteComposeBuffer buffer) {
try {
byte[] bytes = readAllBytes(fd);
- buffer.reset();
- buffer.mBuffer.resize(bytes.length);
+ buffer.reset(bytes.length);
System.arraycopy(bytes, 0, buffer.mBuffer.mBuffer, 0, bytes.length);
buffer.mBuffer.mSize = bytes.length;
} catch (Exception e) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java b/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java
index 4518d94498d6..b7cb3926d936 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java
@@ -83,10 +83,18 @@ public class WireBuffer {
mIndex = currentIndex;
}
- public void reset() {
+ /**
+ * Reset the internal buffer
+ *
+ * @param expectedSize provided hint for the buffer size
+ */
+ public void reset(int expectedSize) {
mIndex = 0;
mStartingIndex = 0;
mSize = 0;
+ if (expectedSize > mMaxSize) {
+ resize(expectedSize);
+ }
}
public int size() {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
index 4bfdc59aad3a..76b714443990 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
@@ -33,7 +33,7 @@ public class BitmapData implements Operation {
int mImageWidth;
int mImageHeight;
byte[] mBitmap;
- public static final int MAX_IMAGE_DIMENSION = 6000;
+ public static final int MAX_IMAGE_DIMENSION = 8000;
public static final Companion COMPANION = new Companion();
diff --git a/core/java/com/android/server/pm/pkg/AndroidPackage.java b/core/java/com/android/server/pm/pkg/AndroidPackage.java
index 096f246b1bab..d430fe32a64e 100644
--- a/core/java/com/android/server/pm/pkg/AndroidPackage.java
+++ b/core/java/com/android/server/pm/pkg/AndroidPackage.java
@@ -1507,4 +1507,11 @@ public interface AndroidPackage {
* @hide
*/
boolean isVisibleToInstantApps();
+
+ /**
+ * @see ApplicationInfo#allowCrossUidActivitySwitchFromBelow
+ * @see R.styleable#AndroidManifestApplication_allowCrossUidActivitySwitchFromBelow
+ * @hide
+ */
+ boolean isAllowCrossUidActivitySwitchFromBelow();
}
diff --git a/core/jni/android_hardware_OverlayProperties.cpp b/core/jni/android_hardware_OverlayProperties.cpp
index f6fe3dd842da..96494b16cc21 100644
--- a/core/jni/android_hardware_OverlayProperties.cpp
+++ b/core/jni/android_hardware_OverlayProperties.cpp
@@ -35,7 +35,6 @@ static struct {
jclass clazz;
jmethodID ctor;
} gOverlayPropertiesClassInfo;
-
// ----------------------------------------------------------------------------
// OverlayProperties lifecycle
// ----------------------------------------------------------------------------
@@ -52,21 +51,21 @@ static jlong android_hardware_OverlayProperties_getDestructor(JNIEnv*, jclass) {
// Accessors
// ----------------------------------------------------------------------------
-static jboolean android_hardware_OverlayProperties_supportFp16ForHdr(JNIEnv* env, jobject thiz,
- jlong nativeObject) {
+static jboolean android_hardware_OverlayProperties_isCombinationSupported(JNIEnv* env, jobject thiz,
+ jlong nativeObject,
+ jint dataspace,
+ jint format) {
gui::OverlayProperties* properties = reinterpret_cast<gui::OverlayProperties*>(nativeObject);
if (properties != nullptr) {
for (const auto& i : properties->combinations) {
- if (std::find(i.pixelFormats.begin(), i.pixelFormats.end(),
- static_cast<int32_t>(HAL_PIXEL_FORMAT_RGBA_FP16)) !=
+ if (std::find(i.pixelFormats.begin(), i.pixelFormats.end(), format) !=
i.pixelFormats.end() &&
std::find(i.standards.begin(), i.standards.end(),
- static_cast<int32_t>(HAL_DATASPACE_STANDARD_BT709)) !=
- i.standards.end() &&
+ dataspace & HAL_DATASPACE_STANDARD_MASK) != i.standards.end() &&
std::find(i.transfers.begin(), i.transfers.end(),
- static_cast<int32_t>(HAL_DATASPACE_TRANSFER_SRGB)) != i.transfers.end() &&
- std::find(i.ranges.begin(), i.ranges.end(),
- static_cast<int32_t>(HAL_DATASPACE_RANGE_EXTENDED)) != i.ranges.end()) {
+ dataspace & HAL_DATASPACE_TRANSFER_MASK) != i.transfers.end() &&
+ std::find(i.ranges.begin(), i.ranges.end(), dataspace & HAL_DATASPACE_RANGE_MASK) !=
+ i.ranges.end()) {
return true;
}
}
@@ -88,7 +87,7 @@ static jlong android_hardware_OverlayProperties_createDefault(JNIEnv* env, jobje
gui::OverlayProperties* overlayProperties = new gui::OverlayProperties;
gui::OverlayProperties::SupportedBufferCombinations combination;
combination.pixelFormats = {HAL_PIXEL_FORMAT_RGBA_8888};
- combination.standards = {HAL_DATASPACE_BT709};
+ combination.standards = {HAL_DATASPACE_STANDARD_BT709};
combination.transfers = {HAL_DATASPACE_TRANSFER_SRGB};
combination.ranges = {HAL_DATASPACE_RANGE_FULL};
overlayProperties->combinations.emplace_back(combination);
@@ -153,8 +152,8 @@ const char* const kClassPathName = "android/hardware/OverlayProperties";
// clang-format off
static const JNINativeMethod gMethods[] = {
{ "nGetDestructor", "()J", (void*) android_hardware_OverlayProperties_getDestructor },
- { "nSupportFp16ForHdr", "(J)Z",
- (void*) android_hardware_OverlayProperties_supportFp16ForHdr },
+ { "nIsCombinationSupported", "(JII)Z",
+ (void*) android_hardware_OverlayProperties_isCombinationSupported },
{ "nSupportMixedColorSpaces", "(J)Z",
(void*) android_hardware_OverlayProperties_supportMixedColorSpaces },
{ "nWriteOverlayPropertiesToParcel", "(JLandroid/os/Parcel;)V",
@@ -172,6 +171,5 @@ int register_android_hardware_OverlayProperties(JNIEnv* env) {
gOverlayPropertiesClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
gOverlayPropertiesClassInfo.ctor =
GetMethodIDOrDie(env, gOverlayPropertiesClassInfo.clazz, "<init>", "(J)V");
-
return err;
}
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 3ee15ab734b9..3d0ab4ef0981 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -314,7 +314,8 @@ static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
}
static void NativeSetApkAssets(JNIEnv* env, jclass /*clazz*/, jlong ptr,
- jobjectArray apk_assets_array, jboolean invalidate_caches) {
+ jobjectArray apk_assets_array, jboolean invalidate_caches,
+ jboolean preset) {
ATRACE_NAME("AssetManager::SetApkAssets");
const jsize apk_assets_len = env->GetArrayLength(apk_assets_array);
@@ -343,7 +344,11 @@ static void NativeSetApkAssets(JNIEnv* env, jclass /*clazz*/, jlong ptr,
}
auto assetmanager = LockAndStartAssetManager(ptr);
- assetmanager->SetApkAssets(apk_assets, invalidate_caches);
+ if (preset) {
+ assetmanager->PresetApkAssets(apk_assets);
+ } else {
+ assetmanager->SetApkAssets(apk_assets, invalidate_caches);
+ }
}
static void NativeSetConfiguration(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint mcc, jint mnc,
@@ -353,7 +358,7 @@ static void NativeSetConfiguration(JNIEnv* env, jclass /*clazz*/, jlong ptr, jin
jint screen_height, jint smallest_screen_width_dp,
jint screen_width_dp, jint screen_height_dp, jint screen_layout,
jint ui_mode, jint color_mode, jint grammatical_gender,
- jint major_version) {
+ jint major_version, jboolean force_refresh) {
ATRACE_NAME("AssetManager::SetConfiguration");
const jsize locale_count = (locales == NULL) ? 0 : env->GetArrayLength(locales);
@@ -413,7 +418,7 @@ static void NativeSetConfiguration(JNIEnv* env, jclass /*clazz*/, jlong ptr, jin
}
auto assetmanager = LockAndStartAssetManager(ptr);
- assetmanager->SetConfigurations(configs);
+ assetmanager->SetConfigurations(std::move(configs), force_refresh != JNI_FALSE);
assetmanager->SetDefaultLocale(default_locale_int);
}
@@ -1522,8 +1527,8 @@ static const JNINativeMethod gAssetManagerMethods[] = {
// AssetManager setup methods.
{"nativeCreate", "()J", (void*)NativeCreate},
{"nativeDestroy", "(J)V", (void*)NativeDestroy},
- {"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;Z)V", (void*)NativeSetApkAssets},
- {"nativeSetConfiguration", "(JIILjava/lang/String;[Ljava/lang/String;IIIIIIIIIIIIIIII)V",
+ {"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;ZZ)V", (void*)NativeSetApkAssets},
+ {"nativeSetConfiguration", "(JIILjava/lang/String;[Ljava/lang/String;IIIIIIIIIIIIIIIIZ)V",
(void*)NativeSetConfiguration},
{"nativeGetAssignedPackageIdentifiers", "(JZZ)Landroid/util/SparseArray;",
(void*)NativeGetAssignedPackageIdentifiers},
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index f93b3068a229..07cbaadf0580 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -15,6 +15,7 @@
*/
#define LOG_TAG "InputEventReceiver"
+#define ATRACE_TAG ATRACE_TAG_INPUT
//#define LOG_NDEBUG 0
@@ -46,6 +47,16 @@ static const char* toString(bool value) {
return value ? "true" : "false";
}
+/**
+ * Trace a bool variable, writing "1" if the value is "true" and "0" otherwise.
+ * TODO(b/311142655): delete this tracing. It's only useful for debugging very specific issues.
+ * @param var the name of the variable
+ * @param value the value of the variable
+ */
+static void traceBoolVariable(const char* var, bool value) {
+ ATRACE_INT(var, value ? 1 : 0);
+}
+
static struct {
jclass clazz;
@@ -130,6 +141,7 @@ NativeInputEventReceiver::NativeInputEventReceiver(
mMessageQueue(messageQueue),
mBatchedInputEventPending(false),
mFdEvents(0) {
+ traceBoolVariable("mBatchedInputEventPending", mBatchedInputEventPending);
if (kDebugDispatchCycle) {
ALOGD("channel '%s' ~ Initializing input event receiver.", getInputChannelName().c_str());
}
@@ -311,6 +323,7 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
if (consumeBatches) {
mBatchedInputEventPending = false;
+ traceBoolVariable("mBatchedInputEventPending", mBatchedInputEventPending);
}
if (outConsumedBatch) {
*outConsumedBatch = false;
@@ -344,6 +357,7 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
}
mBatchedInputEventPending = true;
+ traceBoolVariable("mBatchedInputEventPending", mBatchedInputEventPending);
if (kDebugDispatchCycle) {
ALOGD("channel '%s' ~ Dispatching batched input event pending notification.",
getInputChannelName().c_str());
@@ -355,6 +369,7 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
if (env->ExceptionCheck()) {
ALOGE("Exception dispatching batched input events.");
mBatchedInputEventPending = false; // try again later
+ traceBoolVariable("mBatchedInputEventPending", mBatchedInputEventPending);
}
}
return OK;
@@ -371,15 +386,15 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
}
}
- jobject inputEventObj;
+ ScopedLocalRef<jobject> inputEventObj(env);
switch (inputEvent->getType()) {
case InputEventType::KEY:
if (kDebugDispatchCycle) {
ALOGD("channel '%s' ~ Received key event.", getInputChannelName().c_str());
}
inputEventObj =
- android_view_KeyEvent_fromNative(env,
- static_cast<KeyEvent&>(*inputEvent));
+ android_view_KeyEvent_obtainAsCopy(env,
+ static_cast<KeyEvent&>(*inputEvent));
break;
case InputEventType::MOTION: {
@@ -447,20 +462,19 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
default:
assert(false); // InputConsumer should prevent this from ever happening
- inputEventObj = nullptr;
}
- if (inputEventObj) {
+ if (inputEventObj.get()) {
if (kDebugDispatchCycle) {
ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName().c_str());
}
env->CallVoidMethod(receiverObj.get(),
- gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
+ gInputEventReceiverClassInfo.dispatchInputEvent, seq,
+ inputEventObj.get());
if (env->ExceptionCheck()) {
ALOGE("Exception dispatching input event.");
skipCallbacks = true;
}
- env->DeleteLocalRef(inputEventObj);
} else {
ALOGW("channel '%s' ~ Failed to obtain event object.",
getInputChannelName().c_str());
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index b5fbb22215b4..88b02baab924 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -359,7 +359,7 @@ static jboolean nativeSendKeyEvent(JNIEnv* env, jclass clazz, jlong senderPtr,
jint seq, jobject eventObj) {
sp<NativeInputEventSender> sender =
reinterpret_cast<NativeInputEventSender*>(senderPtr);
- const KeyEvent event = android_view_KeyEvent_toNative(env, eventObj);
+ const KeyEvent event = android_view_KeyEvent_obtainAsCopy(env, eventObj);
status_t status = sender->sendKeyEvent(seq, &event);
return !status;
}
diff --git a/core/jni/android_view_InputQueue.cpp b/core/jni/android_view_InputQueue.cpp
index a0d081d2cd26..50d2cbe2ce74 100644
--- a/core/jni/android_view_InputQueue.cpp
+++ b/core/jni/android_view_InputQueue.cpp
@@ -221,7 +221,7 @@ static jlong nativeSendKeyEvent(JNIEnv* env, jobject clazz, jlong ptr, jobject e
jboolean predispatch) {
InputQueue* queue = reinterpret_cast<InputQueue*>(ptr);
KeyEvent* event = queue->createKeyEvent();
- *event = android_view_KeyEvent_toNative(env, eventObj);
+ *event = android_view_KeyEvent_obtainAsCopy(env, eventObj);
if (predispatch) {
event->setFlags(event->getFlags() | AKEY_EVENT_FLAG_PREDISPATCH);
diff --git a/core/jni/android_view_KeyCharacterMap.cpp b/core/jni/android_view_KeyCharacterMap.cpp
index a79e37afd4cd..2b19ddfed5eb 100644
--- a/core/jni/android_view_KeyCharacterMap.cpp
+++ b/core/jni/android_view_KeyCharacterMap.cpp
@@ -219,10 +219,10 @@ static jobjectArray nativeGetEvents(JNIEnv *env, jobject clazz, jlong ptr,
result = env->NewObjectArray(jsize(events.size()), gKeyEventClassInfo.clazz, NULL);
if (result) {
for (size_t i = 0; i < events.size(); i++) {
- jobject keyEventObj = android_view_KeyEvent_fromNative(env, events.itemAt(i));
- if (!keyEventObj) break; // threw OOM exception
- env->SetObjectArrayElement(result, jsize(i), keyEventObj);
- env->DeleteLocalRef(keyEventObj);
+ ScopedLocalRef<jobject> keyEventObj =
+ android_view_KeyEvent_obtainAsCopy(env, events.itemAt(i));
+ if (!keyEventObj.get()) break; // threw OOM exception
+ env->SetObjectArrayElement(result, jsize(i), keyEventObj.get());
}
}
}
diff --git a/core/jni/android_view_KeyEvent.cpp b/core/jni/android_view_KeyEvent.cpp
index a9c991919361..ca8752f93e11 100644
--- a/core/jni/android_view_KeyEvent.cpp
+++ b/core/jni/android_view_KeyEvent.cpp
@@ -94,26 +94,28 @@ static struct {
// ----------------------------------------------------------------------------
-jobject android_view_KeyEvent_fromNative(JNIEnv* env, const KeyEvent& event) {
+ScopedLocalRef<jobject> android_view_KeyEvent_obtainAsCopy(JNIEnv* env, const KeyEvent& event) {
ScopedLocalRef<jbyteArray> hmac = toJbyteArray(env, event.getHmac());
- jobject eventObj =
- env->CallStaticObjectMethod(gKeyEventClassInfo.clazz, gKeyEventClassInfo.obtain,
- event.getId(), event.getDownTime(), event.getEventTime(),
- event.getAction(), event.getKeyCode(),
- event.getRepeatCount(), event.getMetaState(),
- event.getDeviceId(), event.getScanCode(), event.getFlags(),
- event.getSource(), event.getDisplayId(), hmac.get(),
- nullptr);
+ ScopedLocalRef<jobject>
+ eventObj(env,
+ env->CallStaticObjectMethod(gKeyEventClassInfo.clazz,
+ gKeyEventClassInfo.obtain, event.getId(),
+ event.getDownTime(), event.getEventTime(),
+ event.getAction(), event.getKeyCode(),
+ event.getRepeatCount(), event.getMetaState(),
+ event.getDeviceId(), event.getScanCode(),
+ event.getFlags(), event.getSource(),
+ event.getDisplayId(), hmac.get(), nullptr));
if (env->ExceptionCheck()) {
ALOGE("An exception occurred while obtaining a key event.");
LOGE_EX(env);
env->ExceptionClear();
- return NULL;
+ return ScopedLocalRef<jobject>(env);
}
return eventObj;
}
-KeyEvent android_view_KeyEvent_toNative(JNIEnv* env, jobject eventObj) {
+KeyEvent android_view_KeyEvent_obtainAsCopy(JNIEnv* env, jobject eventObj) {
jint id = env->GetIntField(eventObj, gKeyEventClassInfo.mId);
jint deviceId = env->GetIntField(eventObj, gKeyEventClassInfo.mDeviceId);
jint source = env->GetIntField(eventObj, gKeyEventClassInfo.mSource);
diff --git a/core/jni/android_view_KeyEvent.h b/core/jni/android_view_KeyEvent.h
index bc4876a7a835..838f0130bd40 100644
--- a/core/jni/android_view_KeyEvent.h
+++ b/core/jni/android_view_KeyEvent.h
@@ -17,21 +17,24 @@
#ifndef _ANDROID_VIEW_KEYEVENT_H
#define _ANDROID_VIEW_KEYEVENT_H
-#include "jni.h"
+#include <nativehelper/scoped_local_ref.h>
#include <utils/Errors.h>
#include <utils/threads.h>
+#include "jni.h"
+
namespace android {
class KeyEvent;
/* Obtains an instance of a DVM KeyEvent object as a copy of a native KeyEvent instance.
* Returns NULL on error. */
-extern jobject android_view_KeyEvent_fromNative(JNIEnv* env, const KeyEvent& event);
+extern ScopedLocalRef<jobject> android_view_KeyEvent_obtainAsCopy(JNIEnv* env,
+ const KeyEvent& event);
/* Copies the contents of a DVM KeyEvent object to a native KeyEvent instance.
* Returns non-zero on error. */
-extern KeyEvent android_view_KeyEvent_toNative(JNIEnv* env, jobject eventObj);
+extern KeyEvent android_view_KeyEvent_obtainAsCopy(JNIEnv* env, jobject eventObj);
/* Recycles a DVM KeyEvent object.
* Key events should only be recycled if they are owned by the system since user
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 2e9f1790a4a5..285dee364bb0 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -77,39 +77,28 @@ MotionEvent* android_view_MotionEvent_getNativePtr(JNIEnv* env, jobject eventObj
env->GetLongField(eventObj, gMotionEventClassInfo.mNativePtr));
}
-static void android_view_MotionEvent_setNativePtr(JNIEnv* env, jobject eventObj,
- MotionEvent* event) {
- env->SetLongField(eventObj, gMotionEventClassInfo.mNativePtr,
- reinterpret_cast<jlong>(event));
+static void android_view_MotionEvent_setNativePtr(JNIEnv* env, ScopedLocalRef<jobject>& eventObj,
+ MotionEvent* event) {
+ env->SetLongField(eventObj.get(), gMotionEventClassInfo.mNativePtr,
+ reinterpret_cast<jlong>(event));
}
-jobject android_view_MotionEvent_obtainAsCopy(JNIEnv* env, const MotionEvent& event) {
- jobject eventObj = env->CallStaticObjectMethod(gMotionEventClassInfo.clazz,
- gMotionEventClassInfo.obtain);
- if (env->ExceptionCheck() || !eventObj) {
- ALOGE("An exception occurred while obtaining a motion event.");
- LOGE_EX(env);
- env->ExceptionClear();
- return NULL;
- }
-
- MotionEvent* destEvent = android_view_MotionEvent_getNativePtr(env, eventObj);
- if (!destEvent) {
- destEvent = new MotionEvent();
- android_view_MotionEvent_setNativePtr(env, eventObj, destEvent);
- }
-
+ScopedLocalRef<jobject> android_view_MotionEvent_obtainAsCopy(JNIEnv* env,
+ const MotionEvent& event) {
+ std::unique_ptr<MotionEvent> destEvent = std::make_unique<MotionEvent>();
destEvent->copyFrom(&event, true);
- return eventObj;
+ return android_view_MotionEvent_obtainFromNative(env, std::move(destEvent));
}
-jobject android_view_MotionEvent_obtainFromNative(JNIEnv* env, std::unique_ptr<MotionEvent> event) {
+ScopedLocalRef<jobject> android_view_MotionEvent_obtainFromNative(
+ JNIEnv* env, std::unique_ptr<MotionEvent> event) {
if (event == nullptr) {
- return nullptr;
+ return ScopedLocalRef<jobject>(env);
}
- jobject eventObj =
- env->CallStaticObjectMethod(gMotionEventClassInfo.clazz, gMotionEventClassInfo.obtain);
- if (env->ExceptionCheck() || !eventObj) {
+ ScopedLocalRef<jobject> eventObj(env,
+ env->CallStaticObjectMethod(gMotionEventClassInfo.clazz,
+ gMotionEventClassInfo.obtain));
+ if (env->ExceptionCheck() || !eventObj.get()) {
LOGE_EX(env);
LOG_ALWAYS_FATAL("An exception occurred while obtaining a Java motion event.");
}
diff --git a/core/jni/android_view_MotionEvent.h b/core/jni/android_view_MotionEvent.h
index e81213608d68..b1bf1c435e26 100644
--- a/core/jni/android_view_MotionEvent.h
+++ b/core/jni/android_view_MotionEvent.h
@@ -17,21 +17,24 @@
#ifndef _ANDROID_VIEW_MOTIONEVENT_H
#define _ANDROID_VIEW_MOTIONEVENT_H
-#include "jni.h"
+#include <nativehelper/scoped_local_ref.h>
#include <utils/Errors.h>
+#include "jni.h"
+
namespace android {
class MotionEvent;
/* Obtains an instance of a DVM MotionEvent object as a copy of a native MotionEvent instance.
* Returns NULL on error. */
-extern jobject android_view_MotionEvent_obtainAsCopy(JNIEnv* env, const MotionEvent& event);
+extern ScopedLocalRef<jobject> android_view_MotionEvent_obtainAsCopy(JNIEnv* env,
+ const MotionEvent& event);
/* Obtains an instance of a Java MotionEvent object, taking over the ownership of the provided
* native MotionEvent instance. Crashes on error. */
-extern jobject android_view_MotionEvent_obtainFromNative(JNIEnv* env,
- std::unique_ptr<MotionEvent> event);
+extern ScopedLocalRef<jobject> android_view_MotionEvent_obtainFromNative(
+ JNIEnv* env, std::unique_ptr<MotionEvent> event);
/* Gets the underlying native MotionEvent instance within a DVM MotionEvent object.
* Returns NULL if the event is NULL or if it is uninitialized. */
diff --git a/core/jni/android_view_MotionPredictor.cpp b/core/jni/android_view_MotionPredictor.cpp
index de3e81c7088b..0707e99205aa 100644
--- a/core/jni/android_view_MotionPredictor.cpp
+++ b/core/jni/android_view_MotionPredictor.cpp
@@ -61,7 +61,8 @@ static jobject android_view_MotionPredictor_nativePredict(JNIEnv* env, jclass cl
MotionPredictor* predictor = reinterpret_cast<MotionPredictor*>(ptr);
return android_view_MotionEvent_obtainFromNative(env,
predictor->predict(static_cast<nsecs_t>(
- predictionTimeNanos)));
+ predictionTimeNanos)))
+ .release();
}
static jboolean android_view_MotionPredictor_nativeIsPredictionAvailable(JNIEnv* env, jclass clazz,
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 98f409a334dc..6fec527aaa16 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -716,6 +716,13 @@ static void nativeSetExtendedRangeBrightness(JNIEnv* env, jclass clazz, jlong tr
transaction->setExtendedRangeBrightness(ctrl, currentBufferRatio, desiredRatio);
}
+static void nativeSetDesiredHdrHeadroom(JNIEnv* env, jclass clazz, jlong transactionObj,
+ jlong nativeObject, float desiredRatio) {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+ SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
+ transaction->setDesiredHdrHeadroom(ctrl, desiredRatio);
+}
+
static void nativeSetCachingHint(JNIEnv* env, jclass clazz, jlong transactionObj,
jlong nativeObject, jint cachingHint) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -2340,7 +2347,9 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeSetDataSpace },
{"nativeSetExtendedRangeBrightness", "(JJFF)V",
(void*)nativeSetExtendedRangeBrightness },
- {"nativeSetCachingHint", "(JJI)V",
+ {"nativeSetDesiredHdrHeadroom", "(JJF)V",
+ (void*)nativeSetDesiredHdrHeadroom },
+ {"nativeSetCachingHint", "(JJI)V",
(void*)nativeSetCachingHint },
{"nativeAddWindowInfosReportedListener", "(JLjava/lang/Runnable;)V",
(void*)nativeAddWindowInfosReportedListener },
diff --git a/core/proto/android/app/appstartinfo.proto b/core/proto/android/app/appstartinfo.proto
index 8c3304137904..d9ed911515ba 100644
--- a/core/proto/android/app/appstartinfo.proto
+++ b/core/proto/android/app/appstartinfo.proto
@@ -39,4 +39,5 @@ message ApplicationStartInfoProto {
optional AppStartStartType start_type = 9;
optional bytes start_intent = 10;
optional AppStartLaunchMode launch_mode = 11;
+ optional bool was_force_stopped = 12;
}
diff --git a/core/proto/android/content/intent.proto b/core/proto/android/content/intent.proto
index 1d1f88b01838..3607865cc591 100644
--- a/core/proto/android/content/intent.proto
+++ b/core/proto/android/content/intent.proto
@@ -55,6 +55,7 @@ message IntentProto {
optional string data = 3 [ (.android.privacy).dest = DEST_EXPLICIT ];
optional string type = 4;
optional string flag = 5;
+ optional string extended_flag = 14;
optional string package = 6;
optional ComponentNameProto component = 7;
optional string source_bounds = 8;
diff --git a/core/proto/android/content/package_item_info.proto b/core/proto/android/content/package_item_info.proto
index b9905e8cf446..b7408a4da381 100644
--- a/core/proto/android/content/package_item_info.proto
+++ b/core/proto/android/content/package_item_info.proto
@@ -113,6 +113,7 @@ message ApplicationInfoProto {
optional int32 enable_gwp_asan = 19;
optional int32 enable_memtag = 20;
optional bool native_heap_zero_init = 21;
+ optional bool allow_cross_uid_activity_switch_from_below = 22;
}
optional Detail detail = 17;
repeated string overlay_paths = 18;
diff --git a/core/proto/android/hardware/sensorprivacy.proto b/core/proto/android/hardware/sensorprivacy.proto
index 9359528b613b..e368c6a98a75 100644
--- a/core/proto/android/hardware/sensorprivacy.proto
+++ b/core/proto/android/hardware/sensorprivacy.proto
@@ -91,6 +91,9 @@ message SensorPrivacyIndividualEnabledSensorProto {
enum StateType {
ENABLED = 1;
DISABLED = 2;
+ AUTO_DRIVER_ASSISTANCE_HELPFUL_APPS = 3;
+ AUTO_DRIVER_ASSISTANCE_REQUIRED_APPS = 4;
+ AUTO_DRIVER_ASSISTANCE_APPS = 5;
}
// DEPRECATED
@@ -134,4 +137,4 @@ message SensorPrivacyToggleSourceProto {
// Source for which sensor privacy was toggled.
optional Source source = 1;
-} \ No newline at end of file
+}
diff --git a/core/proto/android/internal/protolog.proto b/core/proto/android/internal/protolog.proto
index fee7a878f860..9e205e284c53 100644
--- a/core/proto/android/internal/protolog.proto
+++ b/core/proto/android/internal/protolog.proto
@@ -27,7 +27,7 @@ message ProtoLogMessage {
option (.android.msg_privacy).dest = DEST_LOCAL;
/* log statement identifier, created from message string and log level. */
- optional sfixed32 message_hash = 1;
+ optional sfixed32 message_hash_legacy = 1 [deprecated = true];
/* log time, relative to the elapsed system time clock. */
optional fixed64 elapsed_realtime_nanos = 2;
/* string parameters passed to the log call. */
@@ -38,6 +38,9 @@ message ProtoLogMessage {
repeated double double_params = 5 [packed=true];
/* boolean parameters passed to the log call. */
repeated bool boolean_params = 6 [packed=true];
+
+ /* log statement identifier, created from message string and log level. */
+ optional sfixed64 message_hash = 7;
}
/* represents a log file containing ProtoLog log entries.
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e4e8f7ef5247..8720f947bb8d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -836,6 +836,7 @@
<protected-broadcast android:name="android.intent.action.PROFILE_AVAILABLE" />
<protected-broadcast android:name="android.intent.action.PROFILE_UNAVAILABLE" />
<protected-broadcast android:name="android.app.action.CONSOLIDATED_NOTIFICATION_POLICY_CHANGED" />
+ <protected-broadcast android:name="android.intent.action.MAIN_USER_LOCKSCREEN_KNOWLEDGE_FACTOR_CHANGED" />
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
@@ -1733,6 +1734,16 @@
android:protectionLevel="signature"
android:featureFlag="com.android.internal.camera.flags.camera_hsum_permission" />
+
+ <!-- @SystemApi Allows camera access of allowlisted driver assistance apps
+ to be controlled separately.
+ <p> Not for use by third-party applications.
+ @FlaggedApi("com.android.internal.camera.flags.camera_privacy_allowlist")
+ @hide
+ -->
+ <permission android:name="android.permission.CAMERA_PRIVACY_ALLOWLIST"
+ android:protectionLevel="signature|privileged" />
+
<!-- ====================================================================== -->
<!-- Permissions for accessing the device sensors -->
<!-- ====================================================================== -->
@@ -2270,7 +2281,7 @@
<!-- @SystemApi @hide Allows changing Thread network state and access to Thread network
credentials such as Network Key and PSKc.
<p>Not for use by third-party applications.
- @FlaggedApi("com.android.net.thread.flags.thread_enabled") -->
+ @FlaggedApi("com.android.net.thread.flags.thread_enabled_platform") -->
<permission android:name="android.permission.THREAD_NETWORK_PRIVILEGED"
android:protectionLevel="signature|privileged" />
@@ -3679,6 +3690,14 @@
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_SECURITY_LOGGING"
android:protectionLevel="internal|role" />
+ <!-- Allows an application to use audit logging API.
+ @hide
+ @SystemApi
+ @FlaggedApi("android.app.admin.flags.security_log_v2_enabled")
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING"
+ android:protectionLevel="internal|role" />
+
<!-- Allows an application to set policy related to system updates.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
required to call APIs protected by this permission on users different to the calling user.
@@ -3782,6 +3801,14 @@
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCK"
android:protectionLevel="internal|role" />
+ <!-- Allows an application to manage policy related to theft detection.
+ @FlaggedApi("android.app.admin.flags.device_theft_api_enabled")
+ @hide
+ @SystemApi
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_THEFT_DETECTION"
+ android:protectionLevel="internal|role" />
+
<!-- Allows an application to manage policy related to system apps.
<p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
APIs protected by this permission on users different to the calling user.
@@ -3819,6 +3846,24 @@
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS"
android:protectionLevel="internal|role" />
+ <!-- Allows an application to manage policy related to block package uninstallation.
+ @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled")
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage policy related to camera toggle.
+ @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled")
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_CAMERA_TOGGLE"
+ android:protectionLevel="internal|role" />
+
+ <!-- Allows an application to manage policy related to microphone toggle.
+ @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled")
+ -->
+ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE"
+ android:protectionLevel="internal|role" />
+
<!-- Allows an application to set device policies outside the current user
that are critical for securing data within the current user.
<p>Holding this permission allows the use of other held MANAGE_DEVICE_POLICY_*
@@ -7137,10 +7182,10 @@
<permission android:name="android.permission.MANAGE_SPEECH_RECOGNITION"
android:protectionLevel="signature" />
- <!-- @SystemApi Allows an application to manage the content suggestions service.
+ <!-- @SystemApi Allows an application to interact with the content suggestions service.
@hide <p>Not for use by third-party applications.</p> -->
<permission android:name="android.permission.MANAGE_CONTENT_SUGGESTIONS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|role" />
<!-- @SystemApi Allows an application to manage the app predictions service.
@hide <p>Not for use by third-party applications.</p> -->
@@ -7651,7 +7696,7 @@
<!-- @SystemApi Allows the holder to launch an Intent Resolver flow with custom presentation
and/or targets.
- @FlaggedApi("android.service.chooser.support_nfc_resolver")
+ @FlaggedApi("android.nfc.enable_nfc_mainline")
@hide -->
<permission android:name="android.permission.SHOW_CUSTOMIZED_RESOLVER"
android:protectionLevel="signature|privileged" />
@@ -7892,6 +7937,33 @@
<permission android:name="android.permission.MANAGE_WEARABLE_SENSING_SERVICE"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi Allows an app to use the on-device intelligence service.
+ <p>Protection level: signature|privileged
+ @hide
+ @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence")
+ -->
+ <permission android:name="android.permission.USE_ON_DEVICE_INTELLIGENCE"
+ android:protectionLevel="signature|privileged" />
+
+
+ <!-- @SystemApi Allows an app to bind the on-device intelligence service.
+ <p>Protection level: signature|privileged
+ @hide
+ @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence")
+ -->
+ <permission android:name="android.permission.BIND_ON_DEVICE_INTELLIGENCE_SERVICE"
+ android:protectionLevel="signature|privileged" />
+
+
+ <!-- @SystemApi Allows an app to bind the on-device trusted service.
+ <p>Protection level: signature|privileged
+ @hide
+ @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence")
+ -->
+ <permission android:name="android.permission.BIND_ON_DEVICE_TRUSTED_SERVICE"
+ android:protectionLevel="signature"/>
+
+
<!-- Allows applications to use the user-initiated jobs API. For more details
see {@link android.app.job.JobInfo.Builder#setUserInitiated}.
<p>Protection level: normal
@@ -8151,8 +8223,8 @@
android:multiprocess="true"
android:permission="android.permission.SHOW_CUSTOMIZED_RESOLVER"
android:exported="true">
- <intent-filter>
- <action android:name="android.service.chooser.action.SHOW_CUSTOMIZED_RESOLVER" />
+ <intent-filter android:priority="100" >
+ <action android:name="android.nfc.action.SHOW_NFC_RESOLVER" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
@@ -8298,6 +8370,12 @@
android:process=":ui">
</activity>
+ <activity android:name="com.android.internal.app.SetScreenLockDialogActivity"
+ android:theme="@style/Theme.Dialog.Confirmation"
+ android:excludeFromRecents="true"
+ android:process=":ui">
+ </activity>
+
<activity android:name="com.android.internal.app.BlockedAppActivity"
android:theme="@style/Theme.Dialog.Confirmation"
android:excludeFromRecents="true"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index eebd765705ee..6cd2c4e277ea 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Vingerafdruk is gestaaf"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Gesig is gestaaf"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Gesig is gestaaf; druk asseblief bevestig"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Vingerafdrukhardeware is nie beskikbaar nie."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Vingerafdrukhardeware is nie beskikbaar nie"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Kan nie vingerafdruk opstel nie"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Vingerafdrukopstelling het uitgetel. Probeer weer."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Vingerafdrukhandeling is gekanselleer."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Vingerafdrukhandeling is deur gebruiker gekanselleer."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Vingerafdrukhandeling is gekanselleer"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Vingerafdrukhandeling is deur gebruiker gekanselleer"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Te veel pogings. Gebruik eerder skermslot."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Te veel pogings. Gebruik eerder skermslot."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Kan nie vingerafdruk verwerk nie. Probeer weer."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Geen vingerafdrukke is geregistreer nie."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Hierdie toetstel het nie \'n vingerafdruksensor nie."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor is tydelik gedeaktiveer."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Kan nie vingerafdruksensor gebruik nie. Besoek \'n verskaffer wat herstelwerk doen"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Geen vingerafdrukke is geregistreer nie"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Hierdie toetstel het nie \'n vingerafdruksensor nie"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensor is tydelik gedeaktiveer"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Kan nie vingerafdruksensor gebruik nie. Besoek ’n verskaffer wat herstelwerk doen."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Aan/af-skakelaar is gedruk"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Vinger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Gebruik vingerafdruk"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index ea6efdb1c0c3..b485b4bdd5f0 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"የጣት አሻራ ትክክለኛነት ተረጋግጧል"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ፊት ተረጋግጧል"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ፊት ተረጋግጧል፣ እባክዎ አረጋግጥን ይጫኑ"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"የጣት አሻራ ሃርድዌር አይገኝም።"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"የጣት አሻራ ሃርድዌር አይገኝም"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"የጣት አሻራን ማዋቀር አልተቻለም"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"የጣት አሻራ ውቅረት ጊዜው አብቅቷል። እንደገና ይሞክሩ።"</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"የጣት አሻራ ስርዓተ ክወና ተትቷል።"</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"የጣት አሻራ ክወና በተጠቃሚ ተሰርዟል።"</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"የጣት አሻራ ክወና ተሰርዟል"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"የጣት አሻራ ክወና በተጠቃሚ ተሰርዟል"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"በጣም ብዙ ሙከራዎች። በምትኩ የማያ ገፅ መቆለፊያን ይጠቀሙ።"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"በጣም ብዙ ሙከራዎች። በምትኩ የማያ ገፅ መቆለፊያን ይጠቀሙ።"</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"የጣት አሻራን ማሰናዳት አልተቻለም። እንደገና ይሞክሩ።"</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ምንም የጣት አሻራዎች አልተመዘገቡም።"</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ይህ መሣሪያ የጣት አሻራ ዳሳሽ የለውም።"</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ዳሳሽ ለጊዜው ተሰናክሏል።"</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"የጣት አሻራ ዳሳሽን መጠቀም አይቻልም። የጥገና አገልግሎት ሰጪን ይጎብኙ"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"ምንም የጣት አሻራዎች አልተመዘገቡም"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"ይህ መሣሪያ የጣት አሻራ ዳሳሽ የለውም"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"ዳሳሽ ለጊዜው ተሰናክሏል"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"የጣት አሻራ ዳሳሽን መጠቀም አይቻልም። የጥገና አገልግሎት አቅራቢን ይጎብኙ።"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"የኃይል አዝራር ተጭኗል"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"ጣት <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"የጣት አሻራ ይጠቀሙ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 88adffabe394..b12c1154f038 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -670,18 +670,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"تم مصادقة بصمة الإصبع"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"تمّت مصادقة الوجه"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"تمّت مصادقة الوجه، يُرجى الضغط على \"تأكيد\"."</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"جهاز بصمة الإصبع غير متاح."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"جهاز التعرّف على بصمة الإصبع غير متاح"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"يتعذّر إعداد بصمة الإصبع."</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"انتهت مهلة إعداد بصمة الإصبع. يُرجى إعادة المحاولة."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"تم إلغاء تشغيل بصمة الإصبع."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"تم إلغاء تشغيل بصمة الإصبع بواسطة المستخدم."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"تم إلغاء عملية قراءة بصمة الإصبع"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"ألغى المستخدم عملية قراءة بصمة الإصبع"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"تم إجراء عدد كبير جدًا من المحاولات. عليك استخدام قفل الشاشة بدلاً من ذلك."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"تم إجراء عدد كبير جدًا من المحاولات. عليك استخدام قفل الشاشة بدلاً من ذلك."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"تتعذّر معالجة بصمة الإصبع. يُرجى إعادة المحاولة."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ليست هناك بصمات إصبع مسجَّلة."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"لا يحتوي هذا الجهاز على مستشعِر بصمات إصبع."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"تم إيقاف جهاز الاستشعار مؤقتًا."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"لا يمكن استخدام أداة استشعار بصمة الإصبع. يُرجى التواصل مع مقدِّم خدمات إصلاح."</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"ما مِن بصمات إصبع مسجَّلة"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"لا يحتوي هذا الجهاز على جهاز استشعار بصمات الأصابع."</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"تم مؤقتًا إيقاف أداة الاستشعار"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"يتعذّر استخدام أداة استشعار بصمة الإصبع. يُرجى التواصل مع مقدِّم خدمات إصلاح."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"تم الضغط على زر التشغيل"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"الإصبع <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"استخدام بصمة الإصبع"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 0be253222232..01705ad6cd68 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"ফিংগাৰপ্ৰিণ্টৰ সত্যাপন কৰা হ’ল"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"মুখমণ্ডলৰ বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কৰা হ’ল"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"মুখমণ্ডলৰ বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কৰা হ’ল, অনুগ্ৰহ কৰি ‘নিশ্চিত কৰক’ বুটামটো টিপক"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"ফিংগাৰপ্ৰিণ্ট হাৰ্ডৱেৰ নাই।"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"ফিংগাৰপ্ৰিণ্ট হাৰ্ডৱেৰ নাই"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"ফিংগাৰপ্ৰিণ্ট ছেট আপ কৰিব নোৱাৰি"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"ফিংগাৰপ্ৰিণ্ট ছেটআপ কৰাৰ সময় উকলি গৈছে। পুনৰ চেষ্টা কৰক।"</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"ফিংগাৰপ্ৰিণ্ট কাৰ্য বাতিল কৰা হ’ল।"</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ব্যৱহাৰকাৰীয়ে ফিংগাৰপ্ৰিণ্ট ক্ৰিয়া বাতিল কৰিছে।"</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"ফিংগাৰপ্ৰিণ্টৰ দ্বাৰা বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কাৰ্যটো বাতিল কৰা হৈছে"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"ব্যৱহাৰকাৰীয়ে ফিংগাৰপ্ৰিণ্টৰ দ্বাৰা বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কাৰ্য বাতিল কৰিছে"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"অতি বেছিসংখ্যক প্ৰয়াস। ইয়াৰ সলনি স্ক্ৰীন লক ব্যৱহাৰ কৰক।"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"অতি বেছিসংখ্যক প্ৰয়াস। ইয়াৰ সলনি স্ক্ৰীন লক ব্যৱহাৰ কৰক।"</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ফিংগাৰপ্ৰিণ্ট চিনাক্তকৰণ প্ৰক্ৰিয়া কৰিব পৰা নাই। পুনৰ চেষ্টা কৰক।"</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"কোনো ফিংগাৰপ্ৰিণ্ট যোগ কৰা নহ\'ল।"</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"এই ডিভাইচটোত ফিংগাৰপ্ৰিণ্ট ছেন্সৰ নাই।"</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ছেন্সৰটো সাময়িকভাৱে অক্ষম হৈ আছে।"</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"ফিংগাৰপ্ৰিণ্ট ছেন্সৰ ব্যৱহাৰ কৰিব নোৱাৰি। মেৰামতি সেৱা প্ৰদানকাৰী কোনো প্ৰতিষ্ঠানলৈ যাওক"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"কোনো ফিংগাৰপ্ৰিণ্ট পঞ্জীয়ন কৰা হোৱা নাই"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"এই ডিভাইচটোত ফিংগাৰপ্ৰিণ্ট ছেন্সৰ নাই"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"ছেন্সৰটো সাময়িকভাৱে অক্ষম হৈ আছে"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"ফিংগাৰপ্ৰিণ্ট ছেন্সৰ ব্যৱহাৰ কৰিব নোৱাৰি। মেৰামতি সেৱা প্ৰদানকাৰী কোনো প্ৰতিষ্ঠানলৈ যাওক।"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"পাৱাৰ বুটাম টিপা হৈছে"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g> আঙুলি"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ফিংগাৰপ্ৰিণ্ট ব্যৱহাৰ কৰক"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 63efc6655555..4ed5f5eadd5b 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Barmaq izi doğrulandı"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Üz doğrulandı"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Üz təsdiq edildi, təsdiq düyməsinə basın"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Barmaq izi üçün avadanlıq yoxdur."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Barmaq izi avadanlığı əlçatan deyil"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Barmaq izini ayarlamaq mümkün deyil"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Barmaq izi ayarlama vaxtı bitib. Yenidən cəhd edin."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Barmaq izi əməliyyatı ləğv edildi."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Barmaq izi əməliyyatı istifadəçi tərəfindən ləğv edildi."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Barmaq izi əməliyyatı ləğv edildi"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"İstifadəçi barmaq izi əməliyyatını ləğv etdi"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Həddindən çox cəhd edilib. Əvəzində ekran kilidindən istifadə edin."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Həddindən çox cəhd edilib. Əvəzində ekran kilidindən istifadə edin."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Barmaq izini emal etmək mümkün deyil. Yenidən cəhd edin."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Barmaq izi qeydə alınmayıb."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Bu cihazda barmaq izi sensoru yoxdur."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor müvəqqəti deaktivdir."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Barmaq izi sensorundan istifadə etmək mümkün deyil. Təmir provayderini ziyarət edin"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Barmaq izi qeydiyyatdan keçirilməyib"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Bu cihazda barmaq izi sensoru yoxdur"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensor müvəqqəti deaktivdir"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Barmaq izi sensorundan istifadə etmək olmur. Servis mərkəzinə gedin."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Qidalanma düyməsi basılıb"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Barmaq <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Barmaq izini istifadə edin"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 2cb924efe50c..b31dca411edb 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -667,18 +667,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Otisak prsta je potvrđen"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Lice je potvrđeno"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Lice je potvrđeno. Pritisnite Potvrdi"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardver za otiske prstiju nije dostupan."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Hardver za otisak prsta nije dostupan"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Podešavanje otiska prsta nije uspelo"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Vreme za podešavanje otiska prsta je isteklo. Probajte ponovo."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Radnja sa otiskom prsta je otkazana."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Korisnik je otkazao radnju sa otiskom prsta."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Radnja sa otiskom prsta je otkazana"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Korisnik je otkazao radnju sa otiskom prsta"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Previše pokušaja. Koristite zaključavanje ekrana umesto toga."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Previše pokušaja. Koristite zaključavanje ekrana umesto toga."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Obrađivanje otiska prsta nije uspelo. Probajte ponovo."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nije registrovan nijedan otisak prsta."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ovaj uređaj nema senzor za otisak prsta."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je privremeno onemogućen."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Ne možete da koristite senzor za otisak prsta. Posetite dobavljača za popravke"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Nije registrovan nijedan otisak prsta"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Ovaj uređaj nema senzor za otisak prsta"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Senzor je privremeno onemogućen"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Ne možete da koristite senzor za otisak prsta. Posetite servis."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Pritisnuto je dugme za uključivanje"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Koristite otisak prsta"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 8815ae2f5092..2bd78e9080c1 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -668,18 +668,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Адбітак пальца распазнаны"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Твар распазнаны"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Твар распазнаны. Націсніце, каб пацвердзіць"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Апаратныя сродкі адбіткаў пальцаў недаступныя."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Сканер адбіткаў пальцаў недаступны"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Не ўдалося захаваць адбітак пальца"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Наладжванне адбітка пальца не завершана. Паўтарыце спробу."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Аперацыя з адбіткамі пальцаў скасавана."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Аўтэнтыфікацыя па адбітках пальцаў скасавана карыстальнікам."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Аперацыя з адбіткам пальца скасавана"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Аперацыя з адбіткам пальца скасавана карыстальнікам"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Занадта шмат спроб. Скарыстайце блакіроўку экрана."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Занадта шмат спроб. Скарыстайце блакіроўку экрана."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Не ўдалося апрацаваць адбітак пальца. Паўтарыце спробу."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Адбіткі пальцаў не зарэгістраваны."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"На гэтай прыладзе няма сканера адбіткаў пальцаў."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Датчык часова выключаны."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Не ўдалося скарыстаць сканер адбіткаў пальцаў. Звярніцеся ў сэрвісны цэнтр."</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Адбіткі пальцаў не зарэгістраваны"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"На гэтай прыладзе няма сканера адбіткаў пальцаў"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Сканер часова адключаны"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Не ўдалося скарыстаць сканер адбіткаў пальцаў. Звярніцеся ў сэрвісны цэнтр."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Націснута кнопка сілкавання"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Палец <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Выкарыстоўваць адбітак пальца"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index b427349c51f5..dc1e92ca7037 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Отпечатъкът е удостоверен"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Лицето е удостоверено"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Лицето е удостоверено. Моля, натиснете „Потвърждаване“"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Хардуерът за отпечатъци не е налице."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Хардуерът за отпечатъци не е налице"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Не може да се настрои отпечатък"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Настройването на отпечатък не завърши навреме. Опитайте отново."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Операцията за отпечатък е анулирана."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Операцията за удостоверяване чрез отпечатък бе анулирана от потребителя."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Операцията за отпечатък е анулирана"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Операцията за удостоверяване чрез отпечатък бе анулирана от потребителя"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Твърде много опити. Вместо това използвайте опция за заключване на екрана."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Твърде много опити. Вместо това използвайте опция за заключване на екрана."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Отпечатъкът не може да бъде обработен. Опитайте отново."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Няма регистрирани отпечатъци."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Това устройство няма сензор за отпечатъци."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сензорът е временно деактивиран."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Сензорът за отпечатъци не може да се използва. Посетете оторизиран сервиз."</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Няма регистрирани отпечатъци"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Това устройство няма сензор за отпечатъци"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Сензорът е временно деактивиран"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Сензорът за отпечатъци не може да се използва. Посетете оторизиран сервиз."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Бутонът за захранване е натиснат"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Пръст <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Използване на отпечатък"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 8da77ae1f370..5dbaad626ab9 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"আঙ্গুলের ছাপ যাচাই করা হয়েছে"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ফেস যাচাই করা হয়েছে"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ফেস যাচাই করা হয়েছে, \'কনফার্ম করুন\' বোতাম প্রেস করুন"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"আঙ্গুলের ছাপ নেওয়ার হার্ডওয়্যার অনুপলব্ধ৷"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"ফিঙ্গারপ্রিন্ট হার্ডওয়্যার উপলভ্য নেই"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"আঙ্গুলের ছাপ সেট-আপ করতে পারছি না"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"ফিঙ্গারপ্রিন্ট সেট-আপ করার সময় সীমা পেরিয়ে গেছে। আবার চেষ্টা করুন।"</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"আঙ্গুলের ছাপ অপারেশন বাতিল করা হয়েছে৷"</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ব্যবহারকারী আঙ্গুলের ছাপের অপারেশনটি বাতিল করেছেন।"</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"ফিঙ্গারপ্রিন্ট অপারেশন বাতিল করা হয়েছে"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"ব্যবহারকারী ফিঙ্গারপ্রিন্ট অপারেশন বাতিল করেছেন"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"অনেকবার চেষ্টা করেছেন। পরিবর্তে স্ক্রিন লক ব্যবহার করুন।"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"অনেকবার চেষ্টা করেছেন। পরিবর্তে স্ক্রিন লক ব্যবহার করুন।"</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ফিঙ্গারপ্রিন্ট প্রসেস করা যায়নি। আবার চেষ্টা করুন।"</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"কোনও আঙ্গুলের ছাপ নথিভুক্ত করা হয়নি।"</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"এই ডিভাইসে আঙ্গুলের ছাপ নেওয়ার সেন্সর নেই।"</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"সেন্সর অস্থায়ীভাবে বন্ধ করা আছে।"</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"আঙ্গুলের ছাপের সেন্সর ব্যবহার করা যাচ্ছে না। একজন মেরামতি মিস্ত্রির কাছে যান"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"কোনও ফিঙ্গারপ্রিন্ট নথিভুক্ত করা নেই"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"এই ডিভাইসে আঙ্গুলের ছাপের সেন্সর নেই"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"সেন্সর অস্থায়ীভাবে বন্ধ আছে"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"ফিঙ্গারপ্রিন্ট সেন্সর ব্যবহার করা যাচ্ছে না। একজন মেরামতি মিস্ত্রির কাছে যান।"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"পাওয়ার বোতাম প্রেস করা হয়েছে"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"আঙ্গুল <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"আঙ্গুলের ছাপ ব্যবহার করুন"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 819695f91e02..838a2db90408 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -667,18 +667,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Otisak prsta je potvrđen"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Lice je provjereno"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Lice je provjereno, pritisnite dugme za potvrdu"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardver za otisak prsta nije dostupan."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Hardver za prepoznavanje otiska prsta nije dostupan"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Nije moguće postaviti otisak prsta"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Postavljanje otiska prsta je isteklo. Pokušajte ponovo."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Radnja s otiskom prsta je otkazana."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Korisnik je otkazao radnju s otiskom prsta."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Radnja s otiskom prsta je otkazana"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Korisnik je otkazao radnju s otiskom prsta"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Previše pokušaja. Umjesto toga koristite zaključavanje ekrana."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Previše pokušaja. Umjesto toga koristite zaključavanje ekrana."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Nije moguće obraditi otisak prsta. Pokušajte ponovo."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nije prijavljen nijedan otisak prsta."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ovaj uređaj nema senzor za otisak prsta."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je privremeno onemogućen."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Nije moguće koristiti senzor za otisak prsta. Posjetite pružaoca usluga za popravke"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Nije registriran nijedan otisak prsta"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Ovaj uređaj nema senzor za otisak prsta"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Senzor je privremeno onemogućen"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Nije moguće koristiti senzor za otisak prsta. Posjetite pružaoca usluga za popravke."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Dugme za uključivanje je pritisnuto"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Koristi otisak prsta"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 785fd27b0d19..feb67c42ccf6 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -667,18 +667,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"L\'empremta digital s\'ha autenticat"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Cara autenticada"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Cara autenticada; prem el botó per confirmar"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"El maquinari d\'empremtes digitals no està disponible."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"El maquinari d\'empremtes digitals no està disponible"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"No es pot configurar l\'empremta digital"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Temps d\'espera esgotat per configurar l\'empremta digital. Torna-ho a provar."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"S\'ha cancel·lat l\'operació d\'empremta digital."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"L\'usuari ha cancel·lat l\'operació d\'empremta digital."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"S\'ha cancel·lat l\'operació d\'empremta digital"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"L\'usuari ha cancel·lat l\'operació d\'empremta digital"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Massa intents. Utilitza el bloqueig de pantalla."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Massa intents. Utilitza el bloqueig de pantalla."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"No es pot processar l\'empremta digital. Torna-ho a provar."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No s\'ha registrat cap empremta digital."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Aquest dispositiu no té sensor d\'empremtes digitals."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"El sensor està desactivat temporalment."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"No es pot utilitzar el sensor d\'empremtes digitals. Visita un proveïdor de reparacions."</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"No s\'ha registrat cap empremta digital"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Aquest dispositiu no té sensor d\'empremtes digitals"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"El sensor està desactivat temporalment"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"No es pot utilitzar el sensor d\'empremtes digitals. Visita un proveïdor de reparacions."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"S\'ha premut el botó d\'engegada"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Dit <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Utilitza l\'empremta digital"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index d95536280176..1c0529a71e59 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -668,18 +668,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Otisk byl ověřen"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Obličej byl ověřen"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Obličej byl ověřen, stiskněte tlačítko pro potvrzení"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Není k dispozici hardware ke snímání otisků prstů."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Není k dispozici hardware ke snímání otisků prstů"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Otisk prstu se nepodařilo nastavit"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Časový limit nastavení otisku prstu vypršel. Zkuste to znovu."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Operace otisku prstu byla zrušena."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Uživatel operaci s otiskem prstu zrušil."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Operace otisku prstu byla zrušena"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Uživatel operaci s otiskem prstu zrušil"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Příliš mnoho pokusů. Použijte zámek obrazovky."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Příliš mnoho pokusů. Použijte zámek obrazovky."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Otisk prstu nelze zpracovat. Zkuste to znovu."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nejsou zaregistrovány žádné otisky prstů."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Toto zařízení nemá snímač otisků prstů."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je dočasně deaktivován."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Snímač otisků prstů nelze použít. Navštivte servis"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Nejsou zaregistrovány žádné otisky prstů"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Toto zařízení nemá snímač otisků prstů"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Senzor je dočasně deaktivován"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Snímač otisků prstů nelze použít. Navštivte servis"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Bylo stisknut vypínač"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Použít otisk prstu"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 65f4c6d9ad58..3010dc7ec883 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingeraftrykket blev godkendt"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Ansigtet er godkendt"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Ansigtet er godkendt. Tryk på Bekræft."</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardwaren til fingeraftryk er ikke tilgængelig."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Hardware til aflæsning af fingeraftryk er ikke tilgængelig"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Fingeraftrykket kan ikke gemmes"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Konfigurationen af fingeraftryk fik timeout. Prøv igen."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingeraftrykshandlingen blev annulleret."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Fingeraftrykshandlingen blev annulleret af brugeren."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Fingeraftrykshandlingen er annulleret"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Fingeraftrykshandlingen er annulleret af brugeren"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Du har brugt for mange forsøg. Brug skærmlåsen i stedet."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Du har brugt for mange forsøg. Brug skærmlåsen i stedet."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Fingeraftrykket kan ikke behandles. Prøv igen."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Der er ikke registreret nogen fingeraftryk."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Denne enhed har ingen fingeraftrykssensor."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensoren er midlertidigt deaktiveret."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Fingeraftrykssensoren kan ikke bruges. Få den repareret"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Der er ikke registreret et fingeraftryk"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Denne enhed har ingen fingeraftrykslæser"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensoren er midlertidigt deaktiveret"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Fingeraftrykssensoren kan ikke bruges. Få den repareret."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Der blev trykket på afbryderknappen"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Fingeraftryk <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Brug fingeraftryk"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 286c1d6be80b..803b96720a01 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingerabdruck wurde authentifiziert"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Gesicht authentifiziert"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Gesicht authentifiziert, bitte bestätigen"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Fingerabdruckhardware nicht verfügbar"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Fingerabdruck-Hardware nicht verfügbar"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Fingerabdruck konnte nicht eingerichtet werden"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Zeitüberschreitung bei Fingerabdruckeinrichtung. Versuch es noch einmal."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingerabdruckvorgang abgebrochen"</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Vorgang der Fingerabdruckauthentifizierung vom Nutzer abgebrochen."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Fingerabdruckvorgang abgebrochen"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Fingerabdruckvorgang vom Nutzer abgebrochen"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Zu viele Versuche. Verwende stattdessen die Displaysperre."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Zu viele Versuche. Verwende stattdessen die Displaysperre."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Fingerabdruck kann nicht verarbeitet werden. Versuch es noch einmal."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Keine Fingerabdrücke erfasst."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Dieses Gerät hat keinen Fingerabdrucksensor."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Der Sensor ist vorübergehend deaktiviert."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Der Fingerabdrucksensor kann nicht verwendet werden. Suche einen Reparaturdienstleister auf."</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Keine Fingerabdrücke registriert"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Dieses Gerät hat keinen Fingerabdrucksensor"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Der Sensor ist vorübergehend deaktiviert"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Fingerabdrucksensor kann nicht verwendet werden. Suche einen Reparaturdienstleister auf."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Ein-/Aus-Taste wurde gedrückt"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Fingerabdruck verwenden"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 1de2bc6b9b39..c030d25ecada 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Η ταυτότητα του δακτυλικού αποτυπώματος ελέγχθηκε"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Έγινε έλεγχος ταυτότητας προσώπου"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Έγινε έλεγχος ταυτότητας προσώπου, πατήστε \"Επιβεβαίωση\""</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Ο εξοπλισμός δακτυλικού αποτυπώματος δεν είναι διαθέσιμος."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Ο εξοπλισμός δακτυλικού αποτυπώματος δεν είναι διαθέσιμος"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Δεν είναι δυνατή η ρύθμιση του δακτυλικού αποτυπώματος"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Λήξη χρονικού ορίου ρύθμισης δακτυλικού αποτυπώματος. Δοκιμάστε ξανά."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Η λειτουργία δακτυλικού αποτυπώματος ακυρώθηκε."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Η λειτουργία δακτυλικού αποτυπώματος ακυρώθηκε από τον χρήστη."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Η λειτουργία δακτυλικού αποτυπώματος ακυρώθηκε"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Η λειτουργία δακτυλικού αποτυπώματος ακυρώθηκε από τον χρήστη"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Υπερβολικά πολλές προσπάθειες. Χρησιμοποιήστε εναλλακτικά το κλείδωμα οθόνης."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Υπερβολικά πολλές προσπάθειες. Χρησιμοποιήστε εναλλακτικά το κλείδωμα οθόνης."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Δεν είναι δυνατή η επεξεργασία του δακτυλικού αποτυπώματος. Δοκιμάστε ξανά."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Δεν έχουν καταχωριστεί δακτυλικά αποτυπώματα."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Αυτή η συσκευή δεν διαθέτει αισθητήρα δακτυλικού αποτυπώματος."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Ο αισθητήρας απενεργοποιήθηκε προσωρινά."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Δεν είναι δυνατή η χρήση του αισθητήρα δακτυλικών αποτυπωμάτων. Επισκεφτείτε έναν πάροχο υπηρεσιών επισκευής."</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Δεν έχουν καταχωριστεί δακτυλικά αποτυπώματα"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Αυτή η συσκευή δεν διαθέτει αισθητήρα δακτυλικού αποτυπώματος"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Ο αισθητήρας απενεργοποιήθηκε προσωρινά"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Δεν είναι δυνατή η χρήση του αισθητήρα δακτυλικών αποτυπωμάτων. Επισκεφτείτε έναν πάροχο υπηρεσιών επισκευής."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Το κουμπί λειτουργίας πατήθηκε"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Δάχτυλο <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Χρήση δακτυλικού αποτυπώματος"</string>
@@ -1756,7 +1756,7 @@
<string name="user_switched" msgid="7249833311585228097">"Τρέχων χρήστης <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"Εναλλαγή σε <xliff:g id="NAME">%1$s</xliff:g>…"</string>
<string name="user_logging_out_message" msgid="7216437629179710359">"Αποσύνδεση <xliff:g id="NAME">%1$s</xliff:g>…"</string>
- <string name="owner_name" msgid="8713560351570795743">"Κάτοχος"</string>
+ <string name="owner_name" msgid="8713560351570795743">"Κάτοχο"</string>
<string name="guest_name" msgid="8502103277839834324">"Επισκέπτης"</string>
<string name="error_message_title" msgid="4082495589294631966">"Σφάλμα"</string>
<string name="error_message_change_not_allowed" msgid="843159705042381454">"Αυτή η αλλαγή δεν επιτρέπεται από το διαχειριστή σας"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 0b752f69b619..cecdce61f3e2 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingerprint authenticated"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Face authenticated"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Face authenticated. Please press confirm"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Fingerprint hardware not available."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Fingerprint hardware not available"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Can’t set up fingerprint"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Fingerprint setup timed out. Try again."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingerprint operation cancelled."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Fingerprint operation cancelled by user."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Fingerprint operation cancelled"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Fingerprint operation cancelled by user"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Too many attempts. Use screen lock instead."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Too many attempts. Use screen lock instead."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Can’t process fingerprint. Try again."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No fingerprints enrolled."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Can’t use fingerprint sensor. Visit a repair provider"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"No fingerprints enrolled"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"This device does not have a fingerprint sensor"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensor temporarily disabled"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Can\'t use fingerprint sensor. Visit a repair provider."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Power button pressed"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Use fingerprint"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 0dbb2ef21795..c3cf40402b42 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingerprint authenticated"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Face authenticated"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Face authenticated, please press confirm"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Fingerprint hardware not available."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Fingerprint hardware not available"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Can’t set up fingerprint"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Fingerprint setup timed out. Try again."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingerprint operation canceled."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Fingerprint operation canceled by user."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Fingerprint operation canceled"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Fingerprint operation canceled by user"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Too many attempts. Use screen lock instead."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Too many attempts. Use screen lock instead."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Can’t process fingerprint. Try again."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No fingerprints enrolled."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Can’t use fingerprint sensor. Visit a repair provider"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"No fingerprints enrolled"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"This device does not have a fingerprint sensor"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensor temporarily disabled"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Can’t use fingerprint sensor. Visit a repair provider."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Power button pressed"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Use fingerprint"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 4a7ba92244f2..cda0168b4600 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingerprint authenticated"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Face authenticated"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Face authenticated. Please press confirm"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Fingerprint hardware not available."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Fingerprint hardware not available"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Can’t set up fingerprint"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Fingerprint setup timed out. Try again."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingerprint operation cancelled."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Fingerprint operation cancelled by user."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Fingerprint operation cancelled"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Fingerprint operation cancelled by user"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Too many attempts. Use screen lock instead."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Too many attempts. Use screen lock instead."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Can’t process fingerprint. Try again."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No fingerprints enrolled."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Can’t use fingerprint sensor. Visit a repair provider"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"No fingerprints enrolled"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"This device does not have a fingerprint sensor"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensor temporarily disabled"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Can\'t use fingerprint sensor. Visit a repair provider."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Power button pressed"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Use fingerprint"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index a7332c66878d..f979e270448c 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingerprint authenticated"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Face authenticated"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Face authenticated. Please press confirm"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Fingerprint hardware not available."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Fingerprint hardware not available"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Can’t set up fingerprint"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Fingerprint setup timed out. Try again."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingerprint operation cancelled."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Fingerprint operation cancelled by user."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Fingerprint operation cancelled"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Fingerprint operation cancelled by user"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Too many attempts. Use screen lock instead."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Too many attempts. Use screen lock instead."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Can’t process fingerprint. Try again."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No fingerprints enrolled."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Can’t use fingerprint sensor. Visit a repair provider"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"No fingerprints enrolled"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"This device does not have a fingerprint sensor"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensor temporarily disabled"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Can\'t use fingerprint sensor. Visit a repair provider."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Power button pressed"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Use fingerprint"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 8f709048d25a..e620c35f89ad 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‎‎‎‏‏‎‎‏‏‏‎‎‎‎‏‎‎‎‎‎‏‏‎‎‏‎‎‏‏‎‏‎‎‎‎‏‎‏‏‎‎‎‎‏‎‏‎‎‏‏‎‏‏‏‎‎‎Fingerprint authenticated‎‏‎‎‏‎"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‎‏‎‎‎‏‎‎‏‎‎‎‏‎‏‎‏‎‏‎‎‎‎‏‎‏‏‏‎‏‎‎‏‏‎‎‎‏‏‎‏‎‎‎‎‎‎‏‏‏‎Face authenticated‎‏‎‎‏‎"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‏‎‎‎‎‎‎‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‏‎‏‏‎‏‎‎‏‏‎‏‏‏‎‎‏‏‎‎‎‏‎‎‏‎‏‏‏‎‏‏‎Face authenticated, please press confirm‎‏‎‎‏‎"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‎‎‎‏‏‏‏‏‎‎‎‏‏‎‏‏‏‏‏‎‎‏‏‎‏‏‎‎‏‏‎‏‎‎‏‎‎‎‏‏‏‎‏‎‎‏‏‏‎‎‏‎‎Fingerprint hardware not available.‎‏‎‎‏‎"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‏‎‎‎‎‏‏‏‏‎‎‎‏‎‏‏‎‏‏‏‏‏‏‏‎‏‎‏‏‏‏‏‎‎‎‎‎‎‎‏‏‎‏‎‏‏‏‏‏‏‎‎‎‏‎Fingerprint hardware not available‎‏‎‎‏‎"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‏‏‎‏‏‎‎‏‏‏‎‏‎‏‏‏‎‎‏‏‎‎‎‎‎‏‎‏‏‎‎‏‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‏‏‎‎‎Can’t set up fingerprint‎‏‎‎‏‎"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‏‎‏‎‎‎‎‎‏‏‎‏‎‏‎‏‎‏‏‏‎‏‎‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‎‏‎‎‎Fingerprint setup timed out. Try again.‎‏‎‎‏‎"</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‎‏‏‏‏‏‏‎‏‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‎‏‏‎‎‎‎‏‏‏‎‏‎‎‏‎‎‎‏‏‎‏‎‏‎‏‎‏‏‏‏‎‎Fingerprint operation canceled.‎‏‎‎‏‎"</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‏‎‏‎‎‏‎‎‎‎‎‎‎‏‏‏‎‎‏‏‏‏‎‏‏‎‎‎‎‎‏‏‏‏‏‎‎‎‏‎‏‏‎‎‏‎‏‏‏‏‏‏‏‎‎Fingerprint operation canceled by user.‎‏‎‎‏‎"</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‏‏‎‏‎‎‎‎‏‎‏‎‎‏‏‏‏‎‎‏‎‎‎‏‏‎‎‎‎‎‎‎‎‎‎‏‎‎‎‎‏‎‎‎‏‎‏‏‎‎‏‏‎‎‏‎Fingerprint operation canceled‎‏‎‎‏‎"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‎‎‎‎‎‎‎‏‎‎‏‎‏‎‏‏‎‏‎‏‏‎‎‏‏‏‏‏‏‎‎‎‏‏‏‎‎‏‎‎‏‎‎‏‎‏‏‎‎‏‏‎‏‏‏‏‎Fingerprint operation canceled by user‎‏‎‎‏‎"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‏‏‎‏‏‎‏‏‏‏‎‏‎‏‎‏‎‏‏‏‎‎‎‏‏‎‏‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‏‎‏‎‏‎‎‏‎‎‎‎Too many attempts. Use screen lock instead.‎‏‎‎‏‎"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‎‏‏‏‏‎‎‏‏‎‎‏‎‏‏‎‏‎‎‎‏‎‏‏‎‎‎‏‏‎‏‏‏‏‏‎‏‎‎‎‏‏‎‎‎‎‏‎‏‏‎Too many attempts. Use screen lock instead.‎‏‎‎‏‎"</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‏‎‏‏‏‏‎‎‏‏‏‏‎‎‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‎‏‏‎‎‏‎‏‏‎‎‏‎‏‎‎‎‎Can’t process fingerprint. Try again.‎‏‎‎‏‎"</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‏‎‎‎‎‏‏‏‎‏‏‎‏‏‎‏‎‎‎‎‎‏‎‏‎‏‏‏‏‎‏‏‏‎‏‏‏‎‎‎‎‏‎‏‏‎‎‏‏‎‏‏‎No fingerprints enrolled.‎‏‎‎‏‎"</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‎‎‎‏‎‎‎‏‎‏‏‎‏‏‏‏‎‎‎‏‎‎‎‏‏‏‏‎‏‎‎‏‏‎‎‏‎‏‎‎‎‎‎‎‎‎‏‏‎‎‏‎‏‏‎‎This device does not have a fingerprint sensor.‎‏‎‎‏‎"</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‎‎‏‏‏‎‎‎‏‏‎‎‏‎‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎‏‏‎‎‎‏‎‎‎‏‏‏‎‎‎‎‏‎‏‏‏‎‎‎‏‎Sensor temporarily disabled.‎‏‎‎‏‎"</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‏‏‎‏‏‏‎‎‎‏‏‏‏‎‎‎‎‏‏‏‏‎‎‎‏‏‏‎‎‎‏‎‎‏‎‎‏‏‎‏‏‎‏‎‎‎‏‏‏‎‏‏‏‎‎‎‎Can’t use fingerprint sensor. Visit a repair provider‎‏‎‎‏‎"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‏‎‎‏‎‎‏‎‎‏‏‎‎‎‏‏‏‎‎‏‏‎‏‎‏‎‎‏‎‏‏‏‎‏‏‏‎‎‏‏‎‏‎‏‎‎‏‎‏‎‎‏‏‎‎No fingerprints enrolled‎‏‎‎‏‎"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‎‏‏‏‎‎‏‏‎‏‏‎‎‎‎‎‏‎‎‎‏‏‏‎‎‏‏‏‎‏‎‎‎‏‎‏‎‏‎‎‎‏‎‎‏‏‏‏‎‏‎‏‏‏‎This device does not have a fingerprint sensor‎‏‎‎‏‎"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‎‎‏‎‎‎‏‎‎‏‎‎‏‏‎‎‏‎‏‏‏‎‎‎‎‏‎‎‏‎‏‏‏‏‎‎‏‏‎‏‏‏‎‏‏‏‏‎‏‏‏‎‎Sensor temporarily disabled‎‏‎‎‏‎"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‏‏‏‎‏‏‎‎‎‎‎‏‏‏‎‎‏‏‎‎‏‏‏‎‎‎‏‎‏‏‏‎‏‏‏‎‏‏‎‏‎‎‎‎‏‏‎‏‏‏‎‏‎‎‎‎Can’t use fingerprint sensor. Visit a repair provider.‎‏‎‎‏‎"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‎‎‎‏‎‏‏‎‎‏‎‏‏‏‎‏‎‎‎‎‎‎‎‎‏‏‎‎‎‎‎‏‏‎‏‏‏‎‏‎‎‎‎‎‏‎‏‎‎‎‎‎‏‏‎‎Power button pressed‎‏‎‎‏‎"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‏‏‏‎‎‏‎‏‎‏‎‎‏‎‎‏‎‏‏‎‏‎‏‎‎‏‎‎‏‎‏‏‏‏‏‎‎‏‎‎‎‏‏‏‏‏‏‎‎‏‎‎Finger ‎‏‎‎‏‏‎<xliff:g id="FINGERID">%d</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‏‎‎‎‎‏‎‎‎‎‏‏‎‏‎‏‏‎‏‎‏‏‎‎‎‏‏‎‎‎‏‏‏‎‎‏‏‏‏‏‎‏‎‎‏‏‎‎‎‏‎‏‎‎‏‎‎‎Use fingerprint‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index b6edfd7e852d..da500d7d50eb 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -667,18 +667,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Se autenticó la huella dactilar"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Se autenticó el rostro"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Se autenticó el rostro; presiona Confirmar"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"El hardware para detectar huellas dactilares no está disponible."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"El hardware de huella dactilar no está disponible"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"No se puede configurar la huella dactilar"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Se agotó el tiempo de espera para configurar la huella dactilar. Vuelve a intentarlo."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Se canceló la operación de huella dactilar."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"El usuario canceló la operación de huella dactilar."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Se canceló la operación de huella dactilar"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"El usuario canceló la operación de huella dactilar"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Demasiados intentos. Utiliza el bloqueo de pantalla en su lugar."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Demasiados intentos. Utiliza el bloqueo de pantalla en su lugar."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"No se puede procesar la huella dactilar. Vuelve a intentarlo."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No se registraron huellas digitales."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo no tiene sensor de huellas dactilares."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Se inhabilitó temporalmente el sensor."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"No se puede usar el sensor de huellas dactilares. Consulta a un proveedor de reparaciones."</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"No se inscribieron huellas dactilares"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Este dispositivo no tiene sensor de huellas dactilares"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Se inhabilitó temporalmente el sensor"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"No se puede usar el sensor de huellas dactilares. Consulta a un proveedor de reparaciones."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Se presionó el botón de encendido"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usar huella dactilar"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index baa445e1eec3..35cd1ebe784f 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -667,18 +667,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Se ha autenticado la huella digital"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Cara autenticada"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Se ha autenticado la cara, pulsa para confirmar"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"El hardware de huella digital no está disponible."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Lector de huellas digitales no disponible"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"No se puede configurar la huella digital"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Tiempo de espera para configurar la huella digital agotado. Inténtalo de nuevo."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Se ha cancelado la operación de huella digital."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"El usuario ha cancelado la operación de huella digital."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Operación de huella digital cancelada"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"El usuario ha cancelado la operación de huella digital"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Demasiados intentos. Usa el bloqueo de pantalla."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Demasiados intentos. Usa el bloqueo de pantalla."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"No se puede procesar la huella digital. Inténtalo de nuevo."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No se ha registrado ninguna huella digital."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo no tiene sensor de huellas digitales."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"El sensor está inhabilitado en estos momentos."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"No se puede usar el sensor de huellas digitales. Visita un proveedor de reparaciones."</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"No se ha registrado ninguna huella digital"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"El dispositivo no tiene ningún sensor de huellas digitales"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensor inhabilitado en estos momentos"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"No se puede usar el sensor de huellas digitales. Visita un proveedor de reparaciones."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Se ha pulsado el botón de encendido"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usar huella digital"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 3cb3605403c8..24ee88437930 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Sõrmejälg autenditi"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Nägu on autenditud"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Nägu on autenditud, vajutage käsku Kinnita"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Sõrmejälje riistvara pole saadaval."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Sõrmejälje riistvara pole saadaval"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Sõrmejälge ei saa seadistada"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Sõrmejälje seadistamine aegus. Proovige uuesti."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Sõrmejälje toiming tühistati."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Kasutaja tühistas sõrmejälje kasutamise."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Sõrmejälje toiming tühistati"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Kasutaja tühistas sõrmejälje kasutamise"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Liiga palju katseid. Kasutage selle asemel ekraanilukku."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Liiga palju katseid. Kasutage selle asemel ekraanilukku."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Sõrmejälge ei õnnestu töödelda. Proovige uuesti."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Ühtegi sõrmejälge pole registreeritud."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Selles seadmes pole sõrmejäljeandurit."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Andur on ajutiselt keelatud."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Sõrmejäljeandurit ei saa kasutada. Külastage remonditeenuse pakkujat"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Ühtegi sõrmejälge pole registreeritud"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Selles seadmes pole sõrmejäljeandurit"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Andur on ajutiselt keelatud"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Sõrmejäljeandurit ei saa kasutada. Külastage remonditeenuse pakkujat."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Vajutati toitenuppu"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Sõrmejälg <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Sõrmejälje kasutamine"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 8e1e1864bc6f..583f8a995d43 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Autentifikatu da hatz-marka"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Autentifikatu da aurpegia"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Autentifikatu da aurpegia; sakatu Berretsi"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hatz-marken hardwarea ez dago erabilgarri."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Hatz-marken hardwarea ez dago erabilgarri"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Ezin da konfiguratu hatz-marka"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Hatz-marka konfiguratzeko denbora-muga gainditu da. Saiatu berriro."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Hatz-markaren eragiketa bertan behera utzi da."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Erabiltzaileak bertan behera utzi du hatz-marka bidezko eragiketa."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Bertan behera utzi da hatz-marka bidezko eragiketa"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Erabiltzaileak bertan behera utzi du hatz-marka bidezko eragiketa"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Saiakera gehiegi egin dira. Erabili pantailaren blokeoa."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Saiakera gehiegi egin dira. Erabili pantailaren blokeoa."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Ezin da prozesatu hatz-marka. Saiatu berriro."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Ez da erregistratu hatz-markarik."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Gailu honek ez du hatz-marken sentsorerik."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sentsorea aldi baterako desgaitu da."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Ezin da erabili hatz-marken sentsorea. Jarri harremanetan konponketak egiten dituen hornitzaile batekin."</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Ez dago hatz-markarik erregistratuta"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Gailu honek ez du hatz-marken sentsorerik"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sentsorea aldi baterako desgaitu da"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Ezin da erabili hatz-marken sentsorea. Joan konponketak egiten dituen hornitzaile baten webgunera edo dendara."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Etengailua sakatu da"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>. hatza"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Erabili hatz-marka"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index bba6a1b86db5..52de6dac5f28 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"اثر انگشت اصالت‌سنجی شد"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"چهره اصالت‌سنجی شد"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"چهره اصالت‌سنجی شد، لطفاً تأیید را فشار دهید"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"سخت‌افزار اثرانگشت در دسترس نیست."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"سخت‌افزار اثر انگشت دردسترس نیست"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"اثر انگشت راه‌اندازی نشد"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"مهلت تنظیم اثر انگشت به‌پایان رسید. دوباره امتحان کنید."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"عملکرد اثر انگشت لغو شد."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"کاربر عملیات اثر انگشت را لغو کرد"</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"عملکرد اثر انگشت لغو شد"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"کاربر عملیات اثر انگشت را لغو کرد"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"تلاش‌ها از حد مجاز بیشتر شده است. به‌جای آن از قفل صفحه استفاده کنید."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"تلاش‌های بیش‌ازحد. حالا از قفل صفحه استفاده کنید."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"اثر انگشت پردازش نشد. دوباره امتحان کنید."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"اثر انگشتی ثبت نشده است."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"این دستگاه حسگر اثر انگشت ندارد."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"حسگر به‌طور موقت غیرفعال است."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"امکان استفاده از حسگر اثر انگشت وجود ندارد. به ارائه‌دهنده خدمات تعمیر مراجعه کنید"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"اثر انگشتی ثبت نشده است"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"این دستگاه حسگر اثر انگشت ندارد"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"حسگر موقتاً غیرفعال شده است"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"نمی‌توان از حسگر اثر انگشت استفاده کرد. به ارائه‌دهنده خدمات تعمیر مراجعه کنید."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"دکمه روشن/خاموش فشار داده شد"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"انگشت <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"استفاده از اثر انگشت"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 6650570af968..20d4c14c8c86 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Sormenjälki tunnistettu"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Kasvot tunnistettu"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Kasvot tunnistettu, valitse Vahvista"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Sormenjälkilaitteisto ei ole käytettävissä."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Sormenjälkilaitteisto ei käytettävissä"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Sormenjälkeä ei voi ottaa käyttöön"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Sormenjäljen käyttöönotto aikakatkaistu. Yritä uudelleen."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Sormenjälkitoiminto peruutettiin."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Käyttäjä peruutti sormenjälkitoiminnon."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Sormenjälkitoiminto peruttu"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Käyttäjä on perunut sormenjälkitoiminnon"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Liian monta yritystä. Käytä näytön lukituksen avaustapaa."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Liian monta yritystä. Käytä näytön lukituksen avaustapaa."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Sormenjälkeä ei voida käsitellä. Yritä uudelleen."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Sormenjälkiä ei ole otettu käyttöön."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Laitteessa ei ole sormenjälkitunnistinta."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Tunnistin poistettu väliaikaisesti käytöstä."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Sormenjälkitunnistinta ei voi käyttää. Ota yhteys korjauspalveluun"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Sormenjälkiä ei ole lisätty"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Laitteessa ei ole sormenjälkitunnistinta."</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Tunnistin väliaikaisesti pois käytöstä"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Sormenjälkitunnistinta ei voi käyttää. Ota yhteys korjauspalveluun."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Virtapainiketta on painettu"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Sormi <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Käytä sormenjälkeä"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index c6cda6598ee3..b6349427b89c 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -667,18 +667,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Empreinte digitale authentifiée"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Visage authentifié"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Visage authentifié, veuillez appuyer sur le bouton Confirmer"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Lecteur d\'empreintes digitales indisponible."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Lecteur d\'empreintes digitales non accessible"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Impossible de configurer l\'empreinte digitale"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Délai dépassé pour configurer l\'empreinte digitale. Réessayez."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Opération d\'empreinte digitale numérique annulée."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"L\'opération d\'empreinte digitale a été annulée par l\'utilisateur."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Opération d\'empreinte digitale annulée"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Opération d\'authentification par empreinte digitale annulée par l\'utilisateur"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Trop de tentatives. Utilisez plutôt le verrouillage de l\'écran."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Trop de tentatives. Utilisez plutôt le verrouillage de l\'écran."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Empreinte digitale non traitable. Réessayez."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Aucune empreinte digitale enregistrée."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Cet appareil ne possède pas de capteur d\'empreintes digitales."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Le capteur a été désactivé temporairement."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Impossible d\'utiliser le capteur d\'empreintes digitales. Consultez un fournisseur de services de réparation"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Aucune empreinte digitale enregistrée"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Cet appareil ne possède pas de capteur d\'empreintes digitales"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Capteur désactivé temporairement"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Impossible d\'utiliser le capteur d\'empreintes digitales. Visitez un fournisseur de services de réparation."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Vous avez appuyé sur l\'interrupteur"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Doigt <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Utiliser l\'empreinte digitale"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index b15ca166b1b2..f0ed3d6290b5 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -201,7 +201,7 @@
<string name="device_ownership_relinquished" msgid="4080886992183195724">"L\'administrateur a mis l\'appareil à disposition pour un usage personnel"</string>
<string name="network_logging_notification_title" msgid="554983187553845004">"L\'appareil est géré"</string>
<string name="network_logging_notification_text" msgid="1327373071132562512">"Votre organisation gère cet appareil et peut surveiller le trafic réseau. Appuyez ici pour obtenir plus d\'informations."</string>
- <string name="location_changed_notification_title" msgid="3620158742816699316">"Des application peuvent accéder à votre position"</string>
+ <string name="location_changed_notification_title" msgid="3620158742816699316">"Des applications peuvent accéder à votre position"</string>
<string name="location_changed_notification_text" msgid="7158423339982706912">"Contactez votre administrateur pour en savoir plus"</string>
<string name="geofencing_service" msgid="3826902410740315456">"Service de géorepérage"</string>
<string name="country_detector" msgid="7023275114706088854">"Détecteur de pays"</string>
@@ -667,18 +667,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Empreinte digitale authentifiée"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Visage authentifié"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Visage authentifié, veuillez appuyer sur \"Confirmer\""</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Matériel d\'empreinte digitale indisponible."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Lecteur d\'empreintes digitales indisponible"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Impossible de configurer l\'empreinte"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Délai de configuration de l\'empreinte dépassé. Réessayez."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Opération d\'empreinte digitale annulée."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Opération d\'authentification par empreinte digitale annulée par l\'utilisateur."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Opération d\'authentification par empreinte digitale annulée"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Opération d\'authentification par empreinte digitale annulée par l\'utilisateur"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Trop de tentatives. Utilisez plutôt le verrouillage de l\'écran."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Trop de tentatives. Utilisez plutôt le verrouillage de l\'écran."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Impossible de reconnaître l\'empreinte digitale. Réessayez."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Aucune empreinte digitale enregistrée."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Aucun lecteur d\'empreinte digitale n\'est installé sur cet appareil."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Capteur temporairement désactivé."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Impossible d\'utiliser le lecteur d\'empreinte digitale. Contactez un réparateur"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Aucune empreinte digitale enregistrée"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Aucun lecteur d\'empreinte digitale n\'est installé sur cet appareil"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Lecteur d\'empreintes temporairement désactivé"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Impossible d\'utiliser le lecteur d\'empreintes digitales. Contactez un réparateur."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Bouton Marche/Arrêt appuyé"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Doigt <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Utiliser l\'empreinte digitale"</string>
@@ -796,8 +796,8 @@
<string name="permdesc_bindConditionProviderService" msgid="6106018791256120258">"Permet à l\'application de s\'associer à l\'interface de niveau supérieur d\'un service de fournisseur de conditions. Ne devrait pas être nécessaire pour les applications standards."</string>
<string name="permlab_bindDreamService" msgid="4776175992848982706">"associer à un service d\'écran de veille interactif"</string>
<string name="permdesc_bindDreamService" msgid="9129615743300572973">"Permet à l\'application autorisée de s\'associer à l\'interface de plus haut niveau d\'un service d\'écran de veille interactif. Cette autorisation ne devrait jamais être nécessaire pour les applications standards."</string>
- <string name="permlab_invokeCarrierSetup" msgid="5098810760209818140">"faire appel à l\'application de configuration fournie par l\'opérateur"</string>
- <string name="permdesc_invokeCarrierSetup" msgid="4790845896063237887">"Permet à l\'application autorisée de faire appel à l\'application de configuration fournie par l\'opérateur. Cette fonctionnalité ne devrait pas être nécessaire pour les applications standards."</string>
+ <string name="permlab_invokeCarrierSetup" msgid="5098810760209818140">"invoquer l\'appli de configuration fournie par l\'opérateur"</string>
+ <string name="permdesc_invokeCarrierSetup" msgid="4790845896063237887">"Permet au titulaire d\'invoquer l\'appli de configuration fournie par l\'opérateur. Ne devrait pas être nécessaire pour les applis standards."</string>
<string name="permlab_accessNetworkConditions" msgid="1270732533356286514">"détecter des observations sur les conditions du réseau"</string>
<string name="permdesc_accessNetworkConditions" msgid="2959269186741956109">"Permet à une application de détecter des observations sur les conditions du réseau. Les applications standards ne devraient pas nécessiter cette autorisation."</string>
<string name="permlab_setInputCalibration" msgid="932069700285223434">"modifier le calibrage du périphérique d\'entrée"</string>
@@ -833,7 +833,7 @@
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Contrôle le nombre de fois qu\'un mot de passe incorrect est saisi lors du déverrouillage de l\'écran, et verrouille votre appareil Android TV ou en efface toutes les données si le nombre maximal de mots de passe incorrects autorisé est dépassé."</string>
<string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Contrôler le nombre de mots de passe incorrects saisis pour le déverrouillage de l\'écran, puis verrouiller le système d\'infoloisirs ou effacer toutes ses données si le nombre maximal de tentatives de saisie du mot de passe est atteint."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Contrôler le nombre de mots de passe incorrects saisis pour le déverrouillage de l\'écran, puis verrouiller le téléphone ou effacer toutes ses données si le nombre maximal de tentatives de saisie du mot de passe est atteint"</string>
- <string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Contrôlez le nombre de fois qu\'un mot de passe incorrect est saisi lors du déverrouillage de l\'écran, et verrouillez la tablette ou effacez toutes les informations sur l\'utilisateur si le nombre maximal de mots de passe incorrects autorisés est dépassé."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Contrôlez le nombre de mots de passe incorrects saisis pour déverrouiller l\'écran, et verrouillez la tablette ou effacez toutes les données de cet utilisateur si trop de mots de passe incorrects sont saisis."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Contrôle le nombre de fois qu\'un mot de passe incorrect est saisi lors du déverrouillage de l\'écran, et verrouille votre appareil Android TV ou efface toutes les données de cet utilisateur si le nombre maximal de mots de passe incorrects autorisé est dépassé."</string>
<string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Contrôler le nombre de mots de passe incorrects saisis pour le déverrouillage de l\'écran, puis verrouiller le système d\'infoloisirs ou effacer toutes les données de ce profil si le nombre maximal de tentatives de saisie du mot de passe est atteint."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Contrôlez le nombre de fois qu\'un mot de passe incorrect est saisi lors du déverrouillage de l\'écran, et verrouillez le téléphone ou effacez toutes les informations sur l\'utilisateur si le nombre maximal de mots de passe incorrects autorisés est dépassé."</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index ec2833dec526..da791af2ec3a 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Autenticouse a impresión dixital"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Autenticouse a cara"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Autenticouse a cara, preme Confirmar"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardware de impresión dixital non dispoñible."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"O hardware de impresión dixital non está dispoñible"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Non se puido configurar a impresión dixital"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Esgotouse o tempo para configurar a impresión dixital. Téntao de novo."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Cancelouse a operación da impresión dixital."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"O usuario cancelou a operación da impresión dixital."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Cancelouse a operación de impresión dixital"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"O usuario cancelou a operación de impresión dixital"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Houbo demasiados intentos. Mellor usa o bloqueo de pantalla."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Demasiados intentos. Mellor usa o bloqueo de pantalla."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Non se pode procesar a impresión dixital Téntao de novo."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Non se rexistraron impresións dixitais."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo non ten sensor de impresión dixital."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Desactivouse o sensor temporalmente."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Non se puido usar o sensor de impresión dixital. Visita un provedor de reparacións"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Non se rexistrou ningunha impresión dixital"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Este dispositivo non ten sensor de impresión dixital"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"O sensor está desactivado temporalmente"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Non se pode usar o sensor de impresión dixital. Vai a un servizo de reparación."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Premeuse o botón de acendido"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Utilizar impresión dixital"</string>
@@ -997,7 +997,7 @@
<string name="lockscreen_password_wrong" msgid="8605355913868947490">"Téntao de novo"</string>
<string name="lockscreen_storage_locked" msgid="634993789186443380">"Desbloquea para gozar todas as funcións e datos"</string>
<string name="faceunlock_multiple_failures" msgid="681991538434031708">"Superouse o número máximo de intentos de desbloqueo facial"</string>
- <string name="lockscreen_missing_sim_message_short" msgid="1229301273156907613">"Non hai ningunha SIM"</string>
+ <string name="lockscreen_missing_sim_message_short" msgid="1229301273156907613">"Non hai SIM"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="3986843848305639161">"Non hai ningunha SIM na tableta."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="3903140876952198273">"Non hai ningunha SIM no dispositivo Android TV."</string>
<string name="lockscreen_missing_sim_message" product="default" msgid="6184187634180854181">"Non hai ningunha SIM no teléfono."</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 5cb77d8fdfed..8f6affa7a91e 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"ફિંગરપ્રિન્ટ પ્રમાણિત કરી"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ચહેરા પ્રમાણિત"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ચહેરા પ્રમાણિત, કૃપા કરીને કન્ફર્મ કરો"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"ફિંગરપ્રિન્ટ હાર્ડવેર ઉપલબ્ધ નથી."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"ફિંગરપ્રિન્ટ હાર્ડવેર ઉપલબ્ધ નથી"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"ફિંગરપ્રિન્ટનું સેટઅપ કરી શકતા નથી"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"ફિંગરપ્રિન્ટનું સેટઅપ કરવાનો સમય સમાપ્ત થઈ ગયો. ફરી પ્રયાસ કરો."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"ફિંગરપ્રિન્ટ ઓપરેશન રદ કર્યું."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ફિંગરપ્રિન્ટ ચકાસવાની પ્રક્રિયા વપરાશકર્તાએ રદ કરી."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"ફિંગરપ્રિન્ટ ઓપરેશન રદ કર્યું"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"વપરાશકર્તાએ ફિંગરપ્રિન્ટ ચકાસવાની પ્રક્રિયા રદ કરી"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"ઘણા બધા પ્રયાસો. તેને બદલે સ્ક્રીન લૉકનો ઉપયોગ કરો."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ઘણા બધા પ્રયાસો. વિકલ્પ તરીકે સ્ક્રીન લૉકનો ઉપયોગ કરો."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ફિંગરપ્રિન્ટની પ્રક્રિયા કરી શકતા નથી. ફરી પ્રયાસ કરો."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"કોઈ ફિંગરપ્રિન્ટની નોંધણી કરવામાં આવી નથી."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"આ ડિવાઇસમાં કોઈ ફિંગરપ્રિન્ટ સેન્સર નથી."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"સેન્સર હંગામી રૂપે બંધ કર્યું છે."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"ફિંગરપ્રિન્ટ સેન્સરનો ઉપયોગ કરી શકાતો નથી. રિપેર કરવાની સેવા આપતા પ્રદાતાની મુલાકાત લો"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"કોઈ ફિંગરપ્રિન્ટની નોંધણી કરવામાં આવી નથી"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"આ ડિવાઇસમાં કોઈ ફિંગરપ્રિન્ટ સેન્સર નથી"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"સેન્સર હંગામી રીતે બંધ કર્યું છે"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"ફિંગરપ્રિન્ટ સેન્સરનો ઉપયોગ કરી શકાતો નથી. રિપેર કરવાની સેવા આપતા પ્રદાતાની મુલાકાત લો."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"પાવર બટન દબાવવામાં આવ્યું"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"આંગળી <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ફિંગરપ્રિન્ટનો ઉપયોગ કરો"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index d51146933785..53b39ffed09b 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"फ़िंगरप्रिंट की पुष्टि हो गई"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"चेहरे की पहचान की गई"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"चेहरे की पहचान की गई, कृपया पुष्टि बटन दबाएं"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"फ़िंगरप्रिंट हार्डवेयर उपलब्ध नहीं है."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"फ़िंगरप्रिंट को पहचानने वाला हार्डवेयर मौजूद नहीं है"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"फ़िंगरप्रिंट सेट अप नहीं किया जा सका"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"फ़िंगरप्रिंट सेटअप करने का समय खत्म हो गया. फिर से कोशिश करें."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"फ़िंगरप्रिंट ऑपरेशन रोक दिया गया."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"उपयोगकर्ता ने फिंगरप्रिंट की पुष्टि की कार्रवाई रद्द कर दी है."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"फिंगरप्रिंट की पुष्टि से जुड़ी कार्रवाई रद्द कर दी गई है"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"उपयोगकर्ता ने फिंगरप्रिंट की पुष्टि से जुड़ी कार्रवाई रद्द कर दी है"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"कई बार कोशिश की जा चुकी है. इसके बजाय, स्क्रीन लॉक का इस्तेमाल करें."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"इससे ज़्यादा बार कोशिश नहीं की जा सकती. इसके बजाय, स्क्रीन लॉक का इस्तेमाल करें."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"फ़िंगरप्रिंट की पहचान नहीं की जा सकी. फिर से कोशिश करें."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"कोई फ़िंगरप्रिंट रजिस्टर नहीं किया गया है."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"इस डिवाइस में फ़िंगरप्रिंट सेंसर नहीं है."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"सेंसर कुछ समय के लिए बंद कर दिया गया है."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"फ़िंगरप्रिंट सेंसर इस्तेमाल नहीं किया जा सकता. रिपेयर की सेवा देने वाली कंपनी से संपर्क करें"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"कोई फ़िंगरप्रिंट रजिस्टर नहीं किया गया है"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"इस डिवाइस में फ़िंगरप्रिंट सेंसर नहीं है"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"सेंसर कुछ समय के लिए बंद कर दिया गया है"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"फ़िंगरप्रिंट सेंसर इस्तेमाल नहीं किया जा सकता. फ़िंगरप्रिंट सेंसर को रिपेयर करने की सेवा देने वाली कंपनी से संपर्क करें."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"पावर बटन दबाने की वजह से गड़बड़ी हुई"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"फ़िंगरप्रिंट <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"फ़िंगरप्रिंट इस्तेमाल करें"</string>
@@ -1165,7 +1165,7 @@
<string name="Midnight" msgid="8176019203622191377">"मध्‍यरात्रि"</string>
<string name="elapsed_time_short_format_mm_ss" msgid="8689459651807876423">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
<string name="elapsed_time_short_format_h_mm_ss" msgid="2302144714803345056">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
- <string name="selectAll" msgid="1532369154488982046">"सभी को चुनें"</string>
+ <string name="selectAll" msgid="1532369154488982046">"पूरा टेक्स्ट चुनें"</string>
<string name="cut" msgid="2561199725874745819">"काटें"</string>
<string name="copy" msgid="5472512047143665218">"कॉपी करें"</string>
<string name="failed_to_copy_to_clipboard" msgid="725919885138539875">"क्लिपबोर्ड पर कॉपी नहीं हो सका"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 636baa0be42b..9af2496fadf1 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -667,18 +667,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Autentificirano otiskom prsta"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Lice je autentificirano"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Lice je autentificirano, pritisnite Potvrdi"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardver za otisak prsta nije dostupan."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Hardver za otisak prsta nije dostupan"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Postavljanje otiska prsta nije uspjelo"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Vrijeme za postavljanje otiska prsta je isteklo. Pokušajte ponovo."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Radnja otiska prsta otkazana je."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Radnju s otiskom prsta otkazao je korisnik."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Radnja otiska prsta je otkazana"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Radnju s otiskom prsta otkazao je korisnik"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Previše pokušaja. Umjesto toga upotrijebite zaključavanje zaslona."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Previše pokušaja. Umjesto toga upotrijebite zaključavanje zaslona."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Obrada otiska prsta nije uspjela. Pokušajte ponovo."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nije registriran nijedan otisak prsta."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ovaj uređaj nema senzor otiska prsta."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je privremeno onemogućen."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Senzor otiska prsta ne može se koristiti. Posjetite davatelja usluga popravaka"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Nije registriran nijedan otisak prsta"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Ovaj uređaj nema senzor otiska prsta"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Senzor je privremeno onemogućen"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Senzor otiska prsta ne može se koristiti. Posjetite servisera."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Pritisnuta je tipka za uključivanje/isključivanje"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Upotreba otiska prsta"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 2c9f7cce21c6..17f80d12a1fe 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Ujjlenyomat hitelesítve"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Arc hitelesítve"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Arc hitelesítve; nyomja meg a Megerősítés lehetőséget"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Az ujjlenyomathoz szükséges hardver nem érhető el."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Az ujjlenyomat-olvasó hardverhez nem lehet hozzáférni."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Nem sikerült beállítani az ujjlenyomatot"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Lejárt az ujjlenyomat-beállítás időkorlátja. Próbálkozzon újra."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Ujjlenyomattal kapcsolatos művelet megszakítva"</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Az ujjlenyomattal kapcsolatos műveletet a felhasználó megszakította."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Ujjlenyomattal kapcsolatos művelet megszakítva."</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Az ujjlenyomattal kapcsolatos műveletet a felhasználó megszakította."</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Túl sokszor próbálkozott. Használja inkább a képernyőzárat."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Túl sok próbálkozás. Használja inkább a képernyőzárat."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Nem sikerült feldolgozni az ujjlenyomatot. Próbálkozzon újra."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nincsenek regisztrált ujjlenyomatok."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ez az eszköz nem rendelkezik ujjlenyomat-érzékelővel."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Az érzékelő átmenetileg le van tiltva."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Nem lehet használni az ujjlenyomat-érzékelőt. Keresse fel a szervizt."</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Nincsenek regisztrált ujjlenyomatok."</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Ez az eszköz nem rendelkezik ujjlenyomat-érzékelővel"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Az érzékelő átmenetileg le van tiltva."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Nem lehet használni az ujjlenyomat-érzékelőt. Keresse fel a szervizt."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Bekapcsológomb megnyomva"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>. ujj"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Ujjlenyomat használata"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 904f47c0246d..51d0dedbef5a 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Մատնահետքը նույնականացվեց"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Դեմքը ճանաչվեց"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Դեմքը ճանաչվեց: Սեղմեք «Հաստատել»:"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Մատնահետքի սարքն անհասանելի է:"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Մատնահետքերի սկաներն անհասանելի է"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Հնարավոր չէ կարգավորել մատնահետքը"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Մատնահետքի կարգավորման ժամանակը սպառվել է։ Նորից փորձեք։"</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Իսկորոշումը մատնահետքի միջոցով չեղարկվեց:"</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Մատնահետքով նույնականացման գործողությունը չեղարկվել է օգտատիրոջ կողմից:"</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Մատնահետքի օգտագործմամբ գործողությունը չեղարկվել է"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Մատնահետքի օգտագործմամբ գործողությունը չեղարկվել է օգտատիրոջ կողմից"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Չափազանց շատ փորձեր են արվել։ Օգտագործեք էկրանի կողպումը։"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Չափազանց շատ փորձեր են արվել։ Օգտագործեք էկրանի կողպումը։"</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Չի հաջողվում մշակել մատնահետքը։ Նորից փորձեք։"</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Գրանցված մատնահետք չկա:"</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Այս սարքը չունի մատնահետքերի սկաներ։"</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Տվիչը ժամանակավորապես անջատված է:"</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Մատնահետքերի սկաները հնարավոր չէ օգտագործել։ Այցելեք սպասարկման կենտրոն։"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Գրանցված մատնահետքեր չկան"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Սարքը չունի մատնահետքի սկաներ"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Տվիչը ժամանակավորապես անջատված է"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Մատնահետքերի սկաները հնարավոր չէ օգտագործել։ Այցելեք սպասարկման կենտրոն։"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Սեղմվել է սնուցման կոճակը"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Մատնահետք <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Օգտագործել մատնահետք"</string>
@@ -1601,8 +1601,8 @@
<string name="storage_internal" msgid="8490227947584914460">"Ներքին ընդհանուր կրիչ"</string>
<string name="storage_sd_card" msgid="3404740277075331881">"SD քարտ"</string>
<string name="storage_sd_card_label" msgid="7526153141147470509">"SD քարտ <xliff:g id="MANUFACTURER">%s</xliff:g>-ից"</string>
- <string name="storage_usb_drive" msgid="448030813201444573">"USB սարքավար"</string>
- <string name="storage_usb_drive_label" msgid="6631740655876540521">"USB սարքավար <xliff:g id="MANUFACTURER">%s</xliff:g>-ից"</string>
+ <string name="storage_usb_drive" msgid="448030813201444573">"USB կրիչ"</string>
+ <string name="storage_usb_drive_label" msgid="6631740655876540521">"USB կրիչ <xliff:g id="MANUFACTURER">%s</xliff:g>-ից"</string>
<string name="storage_usb" msgid="2391213347883616886">"USB կրիչ"</string>
<string name="extract_edit_menu_button" msgid="63954536535863040">"Խմբագրել"</string>
<string name="data_usage_warning_title" msgid="9034893717078325845">"Զգուշացում թրաֆիկի օգտագործման մասին"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 9df1d3aaa9ec..29dc27307f36 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Sidik jari diautentikasi"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Wajah diautentikasi"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Wajah diautentikasi, silakan tekan konfirmasi"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardware sidik jari tidak tersedia."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Hardware sidik jari tidak tersedia"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Tidak dapat menyiapkan sidik jari"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Waktu penyiapan sidik jari habis. Coba lagi."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Operasi sidik jari dibatalkan."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Operasi sidik jari dibatalkan oleh pengguna."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Operasi sidik jari dibatalkan"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Operasi sidik jari dibatalkan oleh pengguna"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Terlalu banyak upaya gagal. Gunakan kunci layar."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Terlalu banyak upaya gagal. Gunakan kunci layar."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Tidak dapat memproses sidik jari. Coba lagi."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Tidak ada sidik jari yang terdaftar."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Perangkat ini tidak memiliki sensor sidik jari."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor dinonaktifkan untuk sementara."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Tidak dapat menggunakan sensor sidik jari. Kunjungi penyedia reparasi"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Tidak ada sidik jari yang terdaftar"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Perangkat ini tidak memiliki sensor sidik jari"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensor dinonaktifkan untuk sementara"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Tidak dapat menggunakan sensor sidik jari. Kunjungi penyedia reparasi."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Tombol daya ditekan"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Jari <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Gunakan sidik jari"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 5f0e0ed43c82..ca702fa3e68c 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingrafar staðfest"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Andlit staðfest"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Andlit staðfest, ýttu til að staðfesta"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Fingrafarsvélbúnaður ekki til staðar."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Fingrafarabúnaður er ekki til staðar"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Ekki er hægt að setja upp fingrafar"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Fingrafarsuppsetning rann út á tíma. Reyndu aftur."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Hætt við fingrafarsaðgerð."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Notandi hætti við að nota fingrafar."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Hætt var við að nota fingrafar"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Notandi hætti við að nota fingrafar"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Of margar tilraunir. Notaðu skjálás í staðinn."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Of margar tilraunir. Notaðu skjálás í staðinn."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Ekki tekst að vinna úr fingrafari. Reyndu aftur."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Engin fingraför hafa verið skráð."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Þetta tæki er ekki með fingrafaralesara."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Slökkt tímabundið á skynjara."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Ekki er hægt að nota fingrafaralesara. Þú verður að fara með hann á verkstæði"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Engin fingraför hafa verið skráð"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Þetta tæki er ekki með fingrafaralesara"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Slökkt var á lesara tímabundið"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Ekki er hægt að nota fingrafaralesara. Þú verður að fara á verkstæði."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Ýtt á aflrofa"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Fingur <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Nota fingrafar"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 014ac499a9d5..2cb3d3d9826d 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -667,18 +667,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Impronta autenticata"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Volto autenticato"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Volto autenticato, premi Conferma"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardware per l\'impronta non disponibile."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Lettore di impronte digitali non disponibile"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Impossibile configurare l\'impronta"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Timeout configurazione impronta. Riprova."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Operazione associata all\'impronta annullata."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Operazione di autenticazione dell\'impronta annullata dall\'utente."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Operazione associata all\'impronta annullata"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Operazione di autenticazione dell\'impronta annullata dall\'utente"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Troppi tentativi. Usa il blocco schermo."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Troppi tentativi. Usa il blocco schermo."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Impossibile elaborare l\'impronta. Riprova."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nessuna impronta digitale registrata."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Questo dispositivo non dispone di sensore di impronte."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensore temporaneamente disattivato."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Impossibile usare il sensore di impronte digitali. Contatta un fornitore di servizi di riparazione"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Nessuna impronta registrata"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Questo dispositivo non è dotato di sensore di impronte"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensore temporaneamente disattivato"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Impossibile usare il sensore di impronte digitali. Contatta un fornitore di servizi di riparazione."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Tasto di accensione premuto"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Dito <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usa l\'impronta"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index daac1ca5f0bc..3e7c79e050de 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -667,18 +667,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"טביעת האצבע אומתה"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"זיהוי הפנים בוצע"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"זיהוי הפנים בוצע. יש ללחוץ על אישור"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"החומרה לזיהוי טביעות אצבעות אינה זמינה."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"החומרה לזיהוי טביעות אצבע לא זמינה"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"לא ניתן להגדיר טביעת אצבע"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"הזמן שהוקצב להגדרה של טביעת האצבע פג. יש לנסות שוב."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"פעולת טביעת האצבע בוטלה."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"פעולת טביעת האצבע בוטלה על ידי המשתמש."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"הפעולה של טביעת האצבע בוטלה"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"הפעולה של טביעת האצבע בוטלה על ידי המשתמש"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"בוצעו יותר מדי ניסיונות. יש להשתמש בנעילת המסך במקום זאת."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"בוצעו יותר מדי ניסיונות. יש להשתמש בנעילת המסך במקום זאת."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"לא ניתן לעבד את טביעת האצבע. יש לנסות שוב."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"לא נסרקו טביעות אצבע."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"במכשיר הזה אין חיישן טביעות אצבע."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"החיישן מושבת באופן זמני."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"לא ניתן להשתמש בחיישן טביעות האצבע. צריך ליצור קשר עם ספק תיקונים"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"לא נסרקו טביעות אצבע"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"במכשיר הזה אין חיישן טביעות אצבע"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"החיישן מושבת באופן זמני"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"לא ניתן להשתמש בחיישן טביעות האצבע. צריך ליצור קשר עם ספק תיקונים."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"לחצן ההפעלה נלחץ"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"אצבע <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"שימוש בטביעת אצבע"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index c9005dc286d9..576a495fbdb9 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"指紋認証を完了しました"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"顔を認証しました"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"顔を認証しました。[確認] を押してください"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"指紋認証ハードウェアは使用できません。"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"指紋認証ハードウェアは使用できません"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"指紋を設定できません"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"指紋の設定がタイムアウトしました。もう一度お試しください。"</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"指紋の操作をキャンセルしました。"</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"指紋の操作がユーザーによりキャンセルされました。"</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"指紋認証操作がキャンセルされました"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"指紋認証操作がユーザーによりキャンセルされました"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"試行回数が上限を超えました。代わりに画面ロックを使用してください。"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"試行回数が上限を超えました。代わりに画面ロックを使用してください。"</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"指紋を処理できません。もう一度お試しください。"</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"指紋が登録されていません。"</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"このデバイスには指紋認証センサーがありません。"</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"センサーが一時的に無効になっています。"</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"指紋認証センサーを使用できません。修理業者に調整を依頼してください"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"指紋が登録されていません"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"このデバイスには指紋認証センサーがありません"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"センサーが一時的に無効になっています"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"指紋認証センサーを使用できません。修理業者に調整を依頼してください。"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"電源ボタンが押されました"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"指紋 <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"指紋の使用"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 8cd3c941e06c..571ae42169c5 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"თითის ანაბეჭდი ავტორიზებულია"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"სახე ავტორიზებულია"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"სახე ავტორიზებულია, დააჭირეთ დადასტურებას"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"თითის ანაბეჭდის აპარატურა არ არის ხელმისაწვდომი."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"თითის ანაბეჭდის ამომცნობი მოწყობილობა მიუწვდომელია"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"თითის ანაბეჭდის დაყენება ვერ ხერხდება"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"თითის ანაბეჭდის დაყენების დრო ამოიწურა. ცადეთ ხელახლა."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"თითის ანაბეჭდის აღების ოპერაცია გაუქმდა."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"თითის ანაბეჭდის ოპერაცია გააუქმა მომხმარებელმა."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"თითის ანაბეჭდის ოპერაცია გაუქმდა"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"თითის ანაბეჭდის ოპერაცია გააუქმა მომხმარებელმა"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"მეტისმეტად ბევრი მცდელობა იყო. სანაცვლოდ გამოიყენეთ ეკრანის დაბლოკვა."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"მეტისმეტად ბევრი მცდელობა იყო. სანაცვლოდ გამოიყენეთ ეკრანის დაბლოკვა."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"თითის ანაბეჭდის დამუშავება შეუძლებელია. ცადეთ ხელახლა."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"თითის ანაბეჭდები რეგისტრირებული არ არის."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ამ მოწყობილობას არ აქვს თითის ანაბეჭდის სენსორი."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"სენსორი დროებით გათიშულია."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"თითის ანაბეჭდის სენსორის გამოყენება ვერ ხერხდება. ეწვიეთ შეკეთების სერვისის პროვაიდერს"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"თითის ანაბეჭდები არ არის რეგისტრირებული"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"ამ მოწყობილობას არ აქვს თითის ანაბეჭდის სენსორი"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"სენსორი დროებით გათიშულია"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"თითის ანაბეჭდის სენსორის გამოყენება შეუძლებელია. ეწვიეთ შეკეთების სერვისის პროვაიდერს."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"ჩართვის ღილაკზე დაეჭირა"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"თითი <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"გამოიყენეთ თითის ანაბეჭდი"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index a8299b7cd6ab..4f50140b34e6 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -153,26 +153,16 @@
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> секундтан кейін"</string>
<string name="cfTemplateRegistered" msgid="5619930473441550596">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Басқа нөмірге бағытталмады"</string>
<string name="cfTemplateRegisteredTime" msgid="5222794399642525045">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: қайта бағытталған жоқ."</string>
- <!-- no translation found for scCellularNetworkSecurityTitle (90330018476923559) -->
- <skip />
- <!-- no translation found for scCellularNetworkSecuritySummary (8659128412709908263) -->
- <skip />
- <!-- no translation found for scIdentifierDisclosureIssueTitle (3737384845335568193) -->
- <skip />
- <!-- no translation found for scIdentifierDisclosureIssueSummary (3870743771498510600) -->
- <skip />
- <!-- no translation found for scNullCipherIssueEncryptedTitle (8426373579673205292) -->
- <skip />
- <!-- no translation found for scNullCipherIssueEncryptedSummary (6437468449554283998) -->
- <skip />
- <!-- no translation found for scNullCipherIssueNonEncryptedTitle (2069674849204163569) -->
- <skip />
- <!-- no translation found for scNullCipherIssueNonEncryptedSummary (3577092996366374833) -->
- <skip />
- <!-- no translation found for scNullCipherIssueActionSettings (8378372959891478470) -->
- <skip />
- <!-- no translation found for scNullCipherIssueActionLearnMore (7896642417214757769) -->
- <skip />
+ <string name="scCellularNetworkSecurityTitle" msgid="90330018476923559">"Ұялы желі қауіпсіздігі"</string>
+ <string name="scCellularNetworkSecuritySummary" msgid="8659128412709908263">"Параметрлерді қарап шығу"</string>
+ <string name="scIdentifierDisclosureIssueTitle" msgid="3737384845335568193">"Құрылғы идентификаторы пайдаланылды"</string>
+ <string name="scIdentifierDisclosureIssueSummary" msgid="3870743771498510600">"Желі (<xliff:g id="DISCLOSURE_NETWORK">%4$s</xliff:g> байланысы) құрылғының бірегей идентификаторын (IMSI) <xliff:g id="DISCLOSURE_WINDOW_START_TIME">%2$tr</xliff:g> және <xliff:g id="DISCLOSURE_WINDOW_END_TIME">%3$tr</xliff:g> аралығында <xliff:g id="DISCLOSURE_COUNT">%1$d</xliff:g> рет жазып алды."</string>
+ <string name="scNullCipherIssueEncryptedTitle" msgid="8426373579673205292">"\"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>\" желісіне қосылу шифрланды"</string>
+ <string name="scNullCipherIssueEncryptedSummary" msgid="6437468449554283998">"Қазір қауіпсіздеу ұялы желіге қосылып тұрсыз."</string>
+ <string name="scNullCipherIssueNonEncryptedTitle" msgid="2069674849204163569">"\"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>\" желісіне қосылу шифрланбаған"</string>
+ <string name="scNullCipherIssueNonEncryptedSummary" msgid="3577092996366374833">"Шифрланбаған ұялы желіге қосылғансыз. Қоңырауларды, хабарлар мен деректерді басқалар қолға түсіруі мүмкін."</string>
+ <string name="scNullCipherIssueActionSettings" msgid="8378372959891478470">"Ұялы желі қауіпсіздігінің параметрлері"</string>
+ <string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"Толық ақпарат"</string>
<string name="fcComplete" msgid="1080909484660507044">"Функция коды толық."</string>
<string name="fcError" msgid="5325116502080221346">"Байланыс мәселесі немесе функция коды жарамсыз."</string>
<string name="httpErrorOk" msgid="6206751415788256357">"Жарайды"</string>
@@ -390,10 +380,8 @@
<string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Ұялы таратылым хабарлары алынғаннан кейін, олардың бағытын өзгерту үшін қолданбаға ұялы таратылым модулімен байланыстыруға мүмкіндік береді. Ұялы таратылым ескертулері кей аймақтарда төтенше жағдайлар туралы хабарлау үшін беріледі. Төтенше жағдай туралы ұялы таратылым хабары алынғаннан кейін, зиянды қолданбалар құрылғы жұмысына кедергі келтіруі мүмкін."</string>
<string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Қазіргі қоңырауларды басқару"</string>
<string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Қолданбаға құрылғыдағы қазіргі қоңыраулар туралы мәліметтерді көруге және басқаруға мүмкіндік береді."</string>
- <!-- no translation found for permlab_accessLastKnownCellId (7638226620825665130) -->
- <skip />
- <!-- no translation found for permdesc_accessLastKnownCellId (6664621339249308857) -->
- <skip />
+ <string name="permlab_accessLastKnownCellId" msgid="7638226620825665130">"Белгілі соңғы ұялы байланыс идентификаторын пайдалану."</string>
+ <string name="permdesc_accessLastKnownCellId" msgid="6664621339249308857">"Қолданба телефония қызметі берген белгілі соңғы ұялы байланыс идентификаторын пайдалануға рұқсат алады."</string>
<string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"ұялы хабар тарату хабарларын оқу"</string>
<string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Қолданбаға ұялы таратылым хабарларын оқу мүмкіндігін береді. Ұялы таратылым дабылдары кейбір аймақтарда төтенше жағдай туралы ескерту үшін қолданылады. Төтенше ұялы хабарлар келгенде залалды қолданбалар құрылғының жұмысына кедергі жасауы мүмкін."</string>
<string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"жазылған ағындарды оқу"</string>
@@ -565,10 +553,8 @@
<string name="permdesc_transmitIr" product="default" msgid="8484193849295581808">"Қолданбаға телефонның инфрақызыл қабылдағышын қолдану мүмкіндігін береді."</string>
<string name="permlab_setWallpaper" msgid="6959514622698794511">"артқы фонды орнату"</string>
<string name="permdesc_setWallpaper" msgid="2973996714129021397">"Қолданбаға жүйелік экранның артқы фонын орнатуға рұқсат береді."</string>
- <!-- no translation found for permlab_accessHiddenProfile (8607094418491556823) -->
- <skip />
- <!-- no translation found for permdesc_accessHiddenProfile (1543153202481009676) -->
- <skip />
+ <string name="permlab_accessHiddenProfile" msgid="8607094418491556823">"Жасырын профильдерге кіру"</string>
+ <string name="permdesc_accessHiddenProfile" msgid="1543153202481009676">"Қолданбаға жасырын профильдерге кіру рұқсатын береді."</string>
<string name="permlab_setWallpaperHints" msgid="1153485176642032714">"артқы фон өлшемін реттеу"</string>
<string name="permdesc_setWallpaperHints" msgid="6257053376990044668">"Қолданбаға жүйелік экранның артқы фонының өлшемі туралы кеңестерді орнатуға рұқсат береді."</string>
<string name="permlab_setTimeZone" msgid="7922618798611542432">"уақыт аймағын реттеу"</string>
@@ -680,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Саусақ ізі аутентификацияланды"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Бет танылды"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Бет танылды, \"Растау\" түймесін басыңыз"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Саусақ ізі жабдығы қолжетімді емес."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Саусақ ізін тану жабдығы қолжетімді емес."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Саусақ ізін орнату мүмкін емес."</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Саусақ ізін реттеу уақыты өтіп кетті. Қайталап көріңіз."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Саусақ ізі операциясынан бас тартылған."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Пайдаланушы саусақ ізі операциясынан бас тартты."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Саусақ ізі операциясынан бас тартылды."</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Пайдаланушы саусақ ізі операциясынан бас тартты."</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Тым көп әрекет жасалды. Орнына экран құлпын пайдаланыңыз."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Тым көп әрекет жасалды. Орнына экран құлпын пайдаланыңыз."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Саусақ ізін өңдеу мүмкін емес. Қайталап көріңіз."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Саусақ іздері тіркелмеген."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Бұл құрылғыда саусақ ізін оқу сканері жоқ."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Датчик уақытша өшірулі."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Саусақ ізін оқу сканерін пайдалану мүмкін емес. Жөндеу қызметіне барыңыз."</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Ешқандай саусақ іздері тіркелмеді."</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Бұл құрылғыда саусақ ізін оқу сканері жоқ"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Датчик уақытша өшірулі."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Саусақ ізін оқу сканерін пайдалану мүмкін емес. Жөндеу қызметіне барыңыз."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Қуат түймесі басулы."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>-саусақ"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Саусақ ізін пайдалану"</string>
@@ -837,10 +823,8 @@
<string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Қолданбаға жиілігі 200 Гц-тен жоғары датчик деректерінің үлгісін таңдауға рұқсат береді."</string>
<string name="permlab_updatePackagesWithoutUserAction" msgid="3363272609642618551">"қолданбаны автоматты түрде жаңарту"</string>
<string name="permdesc_updatePackagesWithoutUserAction" msgid="4567739631260526366">"Бұған дейін орнатылған қолданбаның автоматты түрде жаңартылуына мүмкіндік береді."</string>
- <!-- no translation found for permlab_writeVerificationStateE2eeContactKeys (3990742344778360457) -->
- <skip />
- <!-- no translation found for permdesc_writeVerificationStateE2eeContactKeys (8453156829747427041) -->
- <skip />
+ <string name="permlab_writeVerificationStateE2eeContactKeys" msgid="3990742344778360457">"тура шифрлаумен қорғалған және басқа қолданбаларға тиесілі контакт кілттерін тексеру күйлерін жаңарту"</string>
+ <string name="permdesc_writeVerificationStateE2eeContactKeys" msgid="8453156829747427041">"Қолданбаға тура шифрлаумен қорғалған және басқа қолданбаларға тиесілі контакт кілттерін тексеру күйлерін жаңартуға рұқсат береді."</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"Құпия сөз ережелерін тағайындау"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"Экран бекітпесінің құпия сөздерінің және PIN кодтарының ұзындығын және оларда рұқсат етілген таңбаларды басқару."</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"Экран құлпын ашу әрекеттерін бақылау"</string>
@@ -2402,12 +2386,8 @@
<string name="profile_label_test" msgid="9168641926186071947">"Сынақ"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Жалпы"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <!-- no translation found for satellite_notification_title (4026338973463121526) -->
- <skip />
- <!-- no translation found for satellite_notification_summary (5207364139430767162) -->
- <skip />
- <!-- no translation found for satellite_notification_open_message (4149234979688273729) -->
- <skip />
- <!-- no translation found for satellite_notification_how_it_works (3132069321977520519) -->
- <skip />
+ <string name="satellite_notification_title" msgid="4026338973463121526">"Жерсерік қызметіне автоматты түрде қосылды"</string>
+ <string name="satellite_notification_summary" msgid="5207364139430767162">"Мобильдік не Wi-Fi желісіне қосылмастан хабар жібере аласыз және ала аласыз."</string>
+ <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages қолданбасын ашу"</string>
+ <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Бұл қалай орындалады?"</string>
</resources>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 27d9a06bc731..8c33299df473 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"បាន​ផ្ទៀង​ផ្ទាត់​ស្នាម​ម្រាមដៃ"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"បានផ្ទៀងផ្ទាត់​មុខ"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"បានផ្ទៀងផ្ទាត់​មុខ សូម​ចុច​បញ្ជាក់"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"ផ្នែករឹងស្នាមម្រាមដៃមិនមានទេ។"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"មិនអាចប្រើ​ហាតវែរស្កេនស្នាមម្រាមដៃ​បានទេ"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"មិនអាចរៀបចំ​ស្នាមម្រាមដៃបានទេ"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"កា​ររៀបចំ​ស្នាមម្រាមដៃបានអស់ម៉ោង។ សូមព្យាយាមម្ដងទៀត។"</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"បានបោះបង់ប្រតិបត្តិការស្នាមម្រាមដៃ។"</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ប្រតិបត្តិការ​ស្នាម​ម្រាម​ដៃ​ត្រូវ​បាន​បោះ​បង់​ដោយ​អ្នក​ប្រើប្រាស់។"</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"បានបោះបង់ប្រតិបត្តិការស្នាមម្រាមដៃ"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"ប្រតិបត្តិការ​ស្នាម​ម្រាម​ដៃ​ត្រូវ​បាន​បោះ​បង់​ដោយ​អ្នក​ប្រើប្រាស់"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"ព្យាយាម​ច្រើនដងពេក។ សូមប្រើការចាក់សោ​អេក្រង់ជំនួសវិញ។"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ព្យាយាម​ច្រើនដងពេក។ សូមប្រើការចាក់សោ​អេក្រង់ជំនួសវិញ។"</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"មិនអាចដំណើរការស្នាមម្រាមដៃបានទេ។ សូមព្យាយាមម្ដងទៀត។"</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"មិន​មាន​ការ​ចុះឈ្មោះស្នាម​ម្រាមដៃទេ។"</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ឧបករណ៍នេះ​មិនមាន​ឧបករណ៍ចាប់​ស្នាមម្រាមដៃទេ។"</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"បានបិទ​ឧបករណ៍​ចាប់សញ្ញាជា​បណ្តោះអាសន្ន។"</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"មិនអាចប្រើ​ឧបករណ៍ចាប់ស្នាមម្រាមដៃ​បានទេ។ សូមទាក់ទង​ក្រុមហ៊ុនផ្ដល់​ការជួសជុល"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"មិន​មាន​ការ​ថតបញ្ចូល​ស្នាម​ម្រាមដៃទេ"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"ឧបករណ៍នេះ​មិនមាន​ឧបករណ៍ចាប់​ស្នាមម្រាមដៃទេ"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"បានបិទ​សេនស័រ​ជាបណ្តោះអាសន្ន"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"មិនអាចប្រើ​សេនស័រចាប់ស្នាមម្រាមដៃបានទេ។ សូមទាក់ទងក្រុមហ៊ុន​ផ្ដល់ការជួសជុល។"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"បាន​ចុច​ប៊ូតុង​ថាមពល"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"ម្រាមដៃទី <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ប្រើស្នាមម្រាមដៃ"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index b89a1868067e..e106f1692a1f 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಅನ್ನು ಪ್ರಮಾಣೀಕರಣ ಮಾಡಲಾಗಿದೆ"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ಮುಖವನ್ನು ದೃಢೀಕರಿಸಲಾಗಿದೆ"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ಮುಖವನ್ನು ದೃಢೀಕರಿಸಲಾಗಿದೆ, ದೃಢೀಕರಣವನ್ನು ಒತ್ತಿ"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಹಾರ್ಡ್‌ವೇರ್‌ ಲಭ್ಯವಿಲ್ಲ."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಹಾರ್ಡ್‌ವೇರ್ ಲಭ್ಯವಿಲ್ಲ"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಅನ್ನು ಸೆಟಪ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಸೆಟಪ್ ಮಾಡುವ ಅವಧಿ ಮುಗಿದಿದೆ. ಪುನಃ ಪ್ರಯತ್ನಿಸಿ."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಕಾರ್ಯಾಚರಣೆಯನ್ನು ರದ್ದುಮಾಡಲಾಗಿದೆ."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ಬಳಕೆದಾರರು ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಕಾರ್ಯಾಚರಣೆಯನ್ನು ರದ್ದುಪಡಿಸಿದ್ದಾರೆ."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಕಾರ್ಯಾಚರಣೆಯನ್ನು ರದ್ದುಗೊಳಿಸಲಾಗಿದೆ"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"ಬಳಕೆದಾರರು ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಕಾರ್ಯಾಚರಣೆಯನ್ನು ರದ್ದುಗೊಳಿಸಿದ್ದಾರೆ"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"ಹಲವು ಬಾರಿ ಪ್ರಯತ್ನಿಸಿದ್ದೀರಿ. ಬದಲಾಗಿ ಸ್ಕ್ರೀನ್‌ಲಾಕ್ ಬಳಸಿ."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ಹಲವು ಬಾರಿ ಪ್ರಯತ್ನಿಸಿದ್ದೀರಿ. ಬದಲಾಗಿ ಪರದೆಲಾಕ್ ಬಳಸಿ."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ಪುನಃ ಪ್ರಯತ್ನಿಸಿ."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ಯಾವುದೇ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್‌ ಅನ್ನು ನೋಂದಣಿ ಮಾಡಿಲ್ಲ."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ಈ ಸಾಧನವು ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್‌‌ ಅನ್ನು ಹೊಂದಿಲ್ಲ."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ಸೆನ್ಸಾರ್ ಅನ್ನು ತಾತ್ಕಾಲಿಕವಾಗಿ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್ ಅನ್ನು ಬಳಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ರಿಪೇರಿ ಮಾಡುವವರನ್ನು ಸಂಪರ್ಕಿಸಿ"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"ಯಾವುದೇ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್‌ಗಳನ್ನು ನೋಂದಣಿ ಮಾಡಿಲ್ಲ"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"ಈ ಸಾಧನವು ಫಿಂಗರ್‌ಪ್ರಿಂಟ್‌ ಸೆನ್ಸರ್ ಅನ್ನು ಹೊಂದಿಲ್ಲ"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"ಸೆನ್ಸರ್ ಅನ್ನು ತಾತ್ಕಾಲಿಕವಾಗಿ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್ ಅನ್ನು ಬಳಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ. ರಿಪೇರಿ ಮಾಡುವವರನ್ನು ಸಂಪರ್ಕಿಸಿ."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"ಪವರ್ ಬಟನ್ ಒತ್ತಲಾಗಿದೆ"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"ಫಿಂಗರ್ <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ಫಿಂಗರ್ ಪ್ರಿಂಟ್ ಬಳಸಿ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index e7a04242eebe..ff480c5428b4 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"지문이 인증됨"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"얼굴이 인증되었습니다"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"얼굴이 인증되었습니다. 확인을 누르세요"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"지문 인식 하드웨어를 사용할 수 없습니다."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"지문 인식 하드웨어를 사용할 수 없습니다."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"지문을 설정할 수 없음"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"지문 설정 시간이 초과되었습니다. 다시 시도해 주세요."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"지문 인식 작업이 취소되었습니다."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"사용자가 지문 인식 작업을 취소했습니다."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"지문 인식 작업이 취소되었습니다."</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"사용자가 지문 인식 작업을 취소했습니다."</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"시도 횟수가 너무 많습니다. 화면 잠금을 대신 사용하세요."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"시도 횟수가 너무 많습니다. 화면 잠금을 대신 사용하세요."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"지문을 처리할 수 없습니다. 다시 시도해 주세요."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"등록된 지문이 없습니다."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"기기에 지문 센서가 없습니다."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"센서가 일시적으로 사용 중지되었습니다."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"지문 센서를 사용할 수 없습니다. 수리업체에 방문하세요."</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"등록된 지문이 없습니다."</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"기기에 지문 센서가 없습니다."</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"센서가 일시적으로 사용 중지되었습니다."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"지문 센서를 사용할 수 없습니다. 수리업체를 방문하세요."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"전원 버튼을 눌렀습니다."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"손가락 <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"지문 사용"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 91be9e419b96..e44689d839cc 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Манжа изи текшерилди"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Жүздүн аныктыгы текшерилди"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Жүздүн аныктыгы текшерилди, эми \"Ырастоону\" басыңыз"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Манжа изинин аппараттык камсыздоосу жеткиликтүү эмес."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Манжа издеринин сканери жеткиликтүү эмес"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Манжа изи жөндөлбөй жатат"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Манжа изин коюу убакыты бүтүп калды. Кайра аракет кылыңыз."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Манжа изи иш-аракети жокко чыгарылды."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Манжа изи операциясын колдонуучу жокко чыгарды."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Манжа изи менен аныктыгын текшерүү жокко чыгарылды"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Манжа изи менен аныктыгын текшерүүнү колдонуучу жокко чыгарды"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Өтө көп жолу аракет кылдыңыз. Экранды кулпулоо функциясын колдонуңуз."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Өтө көп жолу аракет кылдыңыз. Экранды кулпулоо функциясын колдонуңуз."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Манжа изи иштетилген жок. Кайра аракет кылыңыз."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Бир да манжа изи катталган эмес."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Бул түзмөктө манжа изинин сенсору жок."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сенсор убактылуу өчүрүлгөн."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Манжа изинин сенсорун колдонууга болбойт. Тейлөө кызматына кайрылыңыз"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Бир да манжа изи катталган эмес"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Бул түзмөктө манжа изинин сенсору жок"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Сенсор убактылуу өчүрүлгөн"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Манжа изинин сенсорун колдонууга болбойт. Тейлөө кызматына кайрылыңыз."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Кубат баскычы басылды"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>-манжа"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Манжа изин колдонуу"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 7d49b0522ab6..1f6c1a1594a9 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"ພິສູດຢືນຢັນລາຍນິ້ວມືແລ້ວ"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ພິສູດຢືນຢັນໃບໜ້າແລ້ວ"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ພິສູດຢືນຢັນໃບໜ້າແລ້ວ, ກະລຸນາກົດຢືນຢັນ"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"ບໍ່​ມີ​ຮາດ​ແວລາຍ​ນີ້ວ​ມື​ໃຫ້​ຢູ່."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"ຮາດແວລາຍນິ້ວມືບໍ່ມີໃຫ້ໃຊ້"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"ບໍ່ສາມາດຕັ້ງຄ່າລາຍນິ້ວມືໄດ້"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"ໝົດເວລາຕັ້ງຄ່າລາຍນິ້ວມື. ກະລຸນາລອງໃໝ່."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"ຍົກ​ເລີກ​ການ​ດຳ​ເນີນ​ການ​ລາຍ​ນີ້ວ​ມື​ແລ້ວ."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ຜູ້ໃຊ້ໄດ້ຍົກເລີກຄຳສັ່ງລາຍນິ້ວມືແລ້ວ."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"ຍົກເລີກການເຮັດວຽກຂອງລາຍນິ້ວມືແລ້ວ"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"ຜູ້ໃຊ້ໄດ້ຍົກເລີກການເຮັດວຽກຂອງລາຍນິ້ວມືແລ້ວ"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"ພະຍາຍາມຫຼາຍເທື່ອເກີນໄປ. ກະລຸນາໃຊ້ການລອກໜ້າຈໍແທນ."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ພະຍາຍາມຫຼາຍເທື່ອເກີນໄປ. ກະລຸນາໃຊ້ການລອກໜ້າຈໍແທນ."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ບໍ່ສາມາດປະມວນຜົນລາຍນິ້ວມືໄດ້. ກະລຸນາລອງໃໝ່."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ບໍ່ມີການລົງທະບຽນລາຍນິ້ວມື."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ອຸປະກອນນີ້ບໍ່ມີເຊັນເຊີລາຍນິ້ວມື."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ປິດການເຮັດວຽກຂອງເຊັນເຊີໄວ້ຊົ່ວຄາວແລ້ວ."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"ບໍ່ສາມາດໃຊ້ເຊັນ​ເຊີລາຍນິ້ວ​ມືໄດ້. ກະລຸນາໄປຫາຜູ້ໃຫ້ບໍລິການສ້ອມແປງ"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"ບໍ່ມີການລົງທະບຽນລາຍນິ້ວມື"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"ອຸປະກອນນີ້ບໍ່ມີເຊັນເຊີລາຍນິ້ວມື"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"ປິດການເຮັດວຽກຂອງເຊັນເຊີໄວ້ຊົ່ຄາວແລ້ວ"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"ບໍ່ສາມາດໃຊ້ເຊັນ​ເຊີລາຍນິ້ວ​ມືໄດ້. ກະລຸນາໄປຫາຜູ້ໃຫ້ບໍລິການສ້ອມແປງ."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"ກົດປຸ່ມເປີດປິດແລ້ວ"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"ນີ້ວ​ມື <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ໃຊ້ລາຍນິ້ວມື"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index bf750b9cbed7..0196351bacb4 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -668,18 +668,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Piršto antspaudas autentifikuotas"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Veidas autentifikuotas"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Veidas autentifikuotas, paspauskite patvirtinimo mygtuką"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Piršto antspaudo aparatinė įranga nepasiekiama."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Piršto antspaudo aparatinė įranga nepasiekiama"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Nepavyko nustatyti kontrolinio kodo"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Baigėsi piršto atspaudo sąrankos skirtasis laikas. Bandykite dar kartą."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Piršto antspaudo operacija atšaukta."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Piršto antspaudo operaciją atšaukė naudotojas."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Piršto antspaudo operacija atšaukta"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Piršto antspaudo operaciją atšaukė naudotojas"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Per daug bandymų. Naudokite ekrano užraktą."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Per daug bandymų. Naudokite ekrano užraktą."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Nepavyko apdoroti kontrolinio kodo. Bandykite dar kartą."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Neužregistruota jokių kontrolinių kodų."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Šiame įrenginyje nėra kontrolinio kodo jutiklio."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Jutiklis laikinai išjungtas."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Negalima naudoti kontrolinio kodo jutiklio. Apsilankykite pas taisymo paslaugos teikėją"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Neužregistruota jokių pirštų atspaudų"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Šiame įrenginyje nėra piršto antspaudo jutiklio"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Jutiklis laikinai išjungtas"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Negalima naudoti piršto atspaudo jutiklio. Apsilankykite pas taisymo paslaugos teikėją."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Paspaustas maitinimo mygtukas"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g> pirštas"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Naudoti kontrolinį kodą"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index b554040891c2..46aae3f62458 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -667,18 +667,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Pirksta nospiedums tika autentificēts."</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Seja autentificēta"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Seja ir autentificēta. Nospiediet pogu Apstiprināt."</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Nospieduma aparatūra nav pieejama."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Pirksta nospieduma aparatūra nav pieejama."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Nevar iestatīt pirksta nospiedumu"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Iestatot pirksta nospiedumu, iestājās noildze. Mēģiniet vēlreiz."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Nospieduma darbība neizdevās."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Lietotājs atcēla pirksta nospieduma darbību."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Nospieduma darbība neizdevās."</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Lietotājs atcēla pirksta nospieduma darbību."</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Pārāk daudz mēģinājumu. Izmantojiet ekrāna bloķēšanu."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Pārāk daudz mēģinājumu. Izmantojiet ekrāna bloķēšanu."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Nevar apstrādāt pirksta nospiedumu. Mēģiniet vēlreiz."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nav reģistrēts neviens pirksta nospiedums."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Šajā ierīcē nav pirksta nospieduma sensora."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensors ir īslaicīgi atspējots."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Nevar izmantot pirksta nospieduma sensoru. Sazinieties ar remonta pakalpojumu sniedzēju."</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Nav reģistrēts neviens pirksta nospiedums."</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Šajā ierīcē nav pirksta nospieduma sensora."</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensors ir īslaicīgi atspējots."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Nevar izmantot pirksta nospieduma sensoru. Sazinieties ar remonta pakalpojumu sniedzēju."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Tika nospiesta barošanas poga"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>. pirksts"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Pirksta nospieduma izmantošana"</string>
@@ -1296,12 +1296,12 @@
<string name="new_app_action" msgid="547772182913269801">"Atvērt <xliff:g id="NEW_APP">%1$s</xliff:g>"</string>
<string name="new_app_description" msgid="1958903080400806644">"Lietotne <xliff:g id="OLD_APP">%1$s</xliff:g> tiks aizvērta, neko nesaglabājot"</string>
<string name="dump_heap_notification" msgid="5316644945404825032">"Process <xliff:g id="PROC">%1$s</xliff:g> pārsniedza atmiņas ierobežojumu."</string>
- <string name="dump_heap_ready_notification" msgid="2302452262927390268">"<xliff:g id="PROC">%1$s</xliff:g> kaudzes izraksts ir gatavs"</string>
+ <string name="dump_heap_ready_notification" msgid="2302452262927390268">"<xliff:g id="PROC">%1$s</xliff:g> grēdas izraksts ir gatavs"</string>
<string name="dump_heap_notification_detail" msgid="8431586843001054050">"Apkopots kaudzes izraksts. Pieskarieties, lai kopīgotu."</string>
- <string name="dump_heap_title" msgid="4367128917229233901">"Vai kopīgot kaudzes izrakstu?"</string>
- <string name="dump_heap_text" msgid="1692649033835719336">"Process <xliff:g id="PROC">%1$s</xliff:g> pārsniedza atmiņas ierobežojumu (<xliff:g id="SIZE">%2$s</xliff:g>). Tika vākts kaudzes izraksts, ko varat kopīgot ar procesa izstrādātāju. Ņemiet vērā: kaudzes izrakstā var būt ietverta jūsu personas informācija, kurai var piekļūt lietojumprogramma."</string>
+ <string name="dump_heap_title" msgid="4367128917229233901">"Vai kopīgot grēdas izrakstu?"</string>
+ <string name="dump_heap_text" msgid="1692649033835719336">"Process <xliff:g id="PROC">%1$s</xliff:g> pārsniedza atmiņas ierobežojumu (<xliff:g id="SIZE">%2$s</xliff:g>). Tika vākts grēdas izraksts, ko varat kopīgot ar procesa izstrādātāju. Ņemiet vērā: grēdas izrakstā var būt ietverta jūsu personas informācija, kurai var piekļūt lietojumprogramma."</string>
<string name="dump_heap_system_text" msgid="6805155514925350849">"Process <xliff:g id="PROC">%1$s</xliff:g> pārsniedza atmiņas ierobežojumu (<xliff:g id="SIZE">%2$s</xliff:g>). Tika vākts kaudzes izraksts, ko varat kopīgot. Ievērojiet piesardzību, jo kaudzes izrakstā var būt ietverta visa sensitīvā personas informācija, kurai var piekļūt process, tostarp jūsu rakstīts teksts."</string>
- <string name="dump_heap_ready_text" msgid="5849618132123045516">"Ir pieejams procesa <xliff:g id="PROC">%1$s</xliff:g> kaudzes izraksts, ko varat kopīgot. Ievērojiet piesardzību, jo kaudzes izrakstā var būt ietverta visa sensitīvā personas informācija, kurai var piekļūt process, tostarp jūsu rakstīts teksts."</string>
+ <string name="dump_heap_ready_text" msgid="5849618132123045516">"Ir pieejams procesa <xliff:g id="PROC">%1$s</xliff:g> grēdas izraksts, ko varat kopīgot. Ievērojiet piesardzību, jo grēdas izrakstā var būt ietverta visa sensitīvā personas informācija, kurai var piekļūt process, tostarp jūsu rakstīts teksts."</string>
<string name="sendText" msgid="493003724401350724">"Izvēlieties darbību tekstam"</string>
<string name="volume_ringtone" msgid="134784084629229029">"Zvanītāja skaļums"</string>
<string name="volume_music" msgid="7727274216734955095">"Multivides skaļums"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index ad2274142ee3..1c85e473fdb9 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Отпечатокот е проверен"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Лицето е проверено"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Лицето е проверено, притиснете го копчето „Потврди“"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Хардверот за отпечатоци не е достапен."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Не е достапен хардвер за отпечаток"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Не може да се постави отпечаток"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Времето за поставување отпечаток истече. Обидете се повторно."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Операцијата со отпечаток се откажа."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Корисникот ја откажа потврдата со отпечаток."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Автентикацијата со отпечаток е откажана"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Корисникот ја откажа автентикацијата со отпечаток"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Премногу обиди. Користете заклучување екран."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Премногу обиди. Користете заклучување екран."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Не може да се обработи отпечатокот од прст. Обидете се повторно."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Не се запишани отпечатоци."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Уредов нема сензор за отпечатоци."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сензорот е привремено оневозможен."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Не може да се користи сензорот за отпечатоци. Однесете го на поправка"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Не се регистрирани отпечатоци"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Уредов нема сензор за отпечатоци"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Сензорот е привремено оневозможен"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Не може да се користи сензорот за отпечатоци. Однесете го уредот на поправка."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Притиснато е копчето за вклучување"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Прст <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Користи отпечаток"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index b91c8cfd1f24..9917448c1e0d 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"ഫിംഗർപ്രിന്റ് പരിശോധിച്ചുറപ്പിച്ചു"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"മുഖം പരിശോധിച്ചുറപ്പിച്ചു"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"മുഖം പരിശോധിച്ചുറപ്പിച്ചു, സ്ഥിരീകരിക്കുക അമർത്തുക"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"ഫിംഗർപ്രിന്റ് ഹാർഡ്‌വെയർ ലഭ്യമല്ല."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"ഫിംഗർപ്രിന്റ് ഹാർഡ്‌വെയർ ലഭ്യമല്ല"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"ഫിംഗർപ്രിന്റ് സജ്ജീകരിക്കാനാകില്ല"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"ഫിംഗർപ്രിന്റ് സജ്ജീകരണം ടൈംഔട്ടായി. വീണ്ടും ശ്രമിക്കുക."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"ഫിംഗർപ്രിന്റ് പ്രവർത്തനം റദ്ദാക്കി."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ഉപയോക്താവ് റദ്ദാക്കിയ ഫിംഗർപ്രിന്‍റ് പ്രവർത്തനം."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"ഫിംഗർപ്രിന്റ് പ്രവർത്തനം റദ്ദാക്കി"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"ഫിംഗർപ്രിന്റിന്റെ പ്രവർത്തനം ഉപയോക്താവ് റദ്ദാക്കി"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"നിരവധി ശ്രമങ്ങൾ. പകരം സ്‌ക്രീൻ ലോക്ക് ഉപയോഗിക്കുക."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"നിരവധി ശ്രമങ്ങൾ. പകരം സ്‌ക്രീൻ ലോക്ക് ഉപയോഗിക്കുക."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ഫിംഗർപ്രിന്റ് പ്രോസസ് ചെയ്യാനാകില്ല. വീണ്ടും ശ്രമിക്കുക."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"വിരലടയാളങ്ങൾ എൻറോൾ ചെയ്തിട്ടില്ല."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ഈ ഉപകരണത്തിൽ ഫിംഗർപ്രിന്റ് സെൻസറില്ല."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"സെൻസർ താൽക്കാലികമായി പ്രവർത്തനരഹിതമാക്കി."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"വിരലടയാള സെൻസർ ഉപയോഗിക്കാനാകുന്നില്ല. റിപ്പയർ കേന്ദ്രം സന്ദർശിക്കുക"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"ഫിംഗർപ്രിന്റുകളൊന്നും എൻറോൾ ചെയ്‌തിട്ടില്ല"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"ഈ ഉപകരണത്തിൽ ഫിംഗർപ്രിന്റ് സെൻസർ ഇല്ല"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"സെൻസർ താൽക്കാലികമായി പ്രവർത്തനരഹിതമാക്കി"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"ഫിംഗർപ്രിന്റ് സെൻസർ ഉപയോഗിക്കാനാകില്ല. റിപ്പയർ കേന്ദ്രം സന്ദർശിക്കുക."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"പവർ ബട്ടൺ അമർത്തി"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"ഫിംഗർ <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ഫിംഗർപ്രിന്റ് ഉപയോഗിക്കുക"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index e17684a6ff0a..48d926ccf4e2 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Хурууны хээг нотолсон"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Царайг баталгаажууллаа"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Царайг баталгаажууллаа. Баталгаажуулах товчлуурыг дарна уу"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Хурууны хээний төхөөрөмж бэлэн бус байна."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Хурууны хээ таних техник хангамж боломжгүй"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Хурууны хээ тохируулах боломжгүй"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Хурууны хээний тохируулга завсарласан. Дахин оролдоно уу."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Хурууны хээний бүртгэл амжилтгүй боллоо."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Хэрэглэгч хурууны хээний баталгаажуулалтыг цуцалсан байна."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Хурууны хээний үйл ажиллагааг цуцалсан"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Хурууны хээний үйл ажиллагааг хэрэглэгч цуцалсан"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Хэт олон удаа оролдлоо. Оронд нь дэлгэцийн түгжээ ашиглана уу."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Хэт олон удаа оролдлоо. Оронд нь дэлгэцийн түгжээ ашиглана уу."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Хурууны хээг боловсруулах боломжгүй. Дахин оролдоно уу."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Бүртгүүлсэн хурууны хээ алга."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Энэ төхөөрөмжид хурууны хээ мэдрэгч алга."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Мэдрэгчийг түр хугацаанд идэвхгүй болгосон."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Хурууны хээ мэдрэгч ашиглах боломжгүй. Засварын үйлчилгээ үзүүлэгчид зочилно уу"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Ямар ч хурууны хээ бүртгүүлээгүй"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Энэ төхөөрөмжид хурууны хээ мэдрэгч алга"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Мэдрэгчийг түр зуур идэвхгүй болгосон"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Хурууны хээ мэдрэгчийг ашиглах боломжгүй. Засварын үйлчилгээ үзүүлэгчид зочилно уу."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Асаах/Унтраах товчийг дарсан"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Хурууны хээ <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Хурууны хээ ашиглах"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index ca26b71b3b2e..8fb6b0f01960 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -453,7 +453,7 @@
<string name="permlab_getPackageSize" msgid="375391550792886641">"अ‍ॅप संचयन स्थान मोजा"</string>
<string name="permdesc_getPackageSize" msgid="742743530909966782">"अ‍ॅप ला त्याचा कोड, डेटा आणि कॅशे आकार पुनर्प्राप्त करण्यासाठी अनुमती देते"</string>
<string name="permlab_writeSettings" msgid="8057285063719277394">"सिस्टीम सेटिंग्ज सुधारित करा"</string>
- <string name="permdesc_writeSettings" msgid="8293047411196067188">"सिस्टीमचा सेटिंग्ज डेटा सुधारित करण्यासाठी अ‍ॅप ला अनुमती देते. दुर्भावनापूर्ण अ‍ॅप्स आपल्या सिस्टीमचे कॉंफिगरेशन दूषित करू शकतात."</string>
+ <string name="permdesc_writeSettings" msgid="8293047411196067188">"सिस्टीमचा सेटिंग्ज डेटा सुधारित करण्यासाठी अ‍ॅपला अनुमती देते. दुर्भावनापूर्ण अ‍ॅप्स तुमच्या सिस्टीमचे कॉन्फिगरेशन दूषित करू शकतात."</string>
<string name="permlab_receiveBootCompleted" msgid="6643339400247325379">"सुरूवातीस चालवा"</string>
<string name="permdesc_receiveBootCompleted" product="tablet" msgid="5565659082718177484">"जसे सिस्टम बूट करणे समाप्त करते तसे अ‍ॅप ला स्वतः सुरू करण्यास अनुमती देते. यामुळे टॅबलेट सुरू करण्यास वेळ लागू शकतो आणि नेहमी सुरू राहून एकंदर टॅबलेटला धीमे करण्यास अ‍ॅप ला अनुमती देते."</string>
<string name="permdesc_receiveBootCompleted" product="tv" msgid="4900842256047614307">"सिस्टम बूट होणे संपल्यावर ॲपला स्वतः सुरू होण्याची अनुमती देते. यामुळे तुमच्या Android TV डिव्हाइसला सुरू होण्यास वेळ लागू शकतो आणि नेहमी सुरू राहून एकंदर डिव्हाइसलाच धीमे करण्याची अनुमती ॲपला देते."</string>
@@ -582,9 +582,9 @@
<string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"फक्त तुमच्या Android TV डिव्हाइसलाच नाही, तर मल्टिकास्ट पत्ते वापरून एका वाय-फाय नेटवर्कवरील सर्व डिव्हाइसवर पाठविलेली पॅकेट प्राप्त करण्यासाठी ॲपला अनुमती देते. हे मल्टिकास्ट मोड नसताना वापरल्या जाणाऱ्या ऊर्जेपेक्षा अधिक उर्जा वापरते."</string>
<string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"मल्टिकास्ट पत्ते वापरून फक्त तुमच्या फोनवर नाही, तर वाय-फाय नेटवर्कवरील सर्व डीव्हाइसवर पाठविलेले पॅकेट प्राप्त करण्यासाठी अ‍ॅप ला अनुमती देते. हे मल्टिकास्टखेरिज इतर मोडसाठी अधिक पॉवर वापरते."</string>
<string name="permlab_bluetoothAdmin" msgid="6490373569441946064">"ब्लूटूथ सेटिंग्ज अ‍ॅक्सेस करा"</string>
- <string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"स्थानिक ब्लूटूथ टॅबलेट कॉंफिगर करण्याकरिता आणि दूरस्थ डिव्हाइस शोधण्यासाठी आणि त्यासह जोडण्यासाठी अ‍ॅप ला अनुमती देते."</string>
- <string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"तुमच्या Android TV डिव्हाइसवर ब्लूटूथ कॉंफिगर करण्याकरिता आणि पेअर केलेली आणि रीमोट डिव्हाइस शोधण्यासाठी ॲपला अनुमती देते."</string>
- <string name="permdesc_bluetoothAdmin" product="default" msgid="7381341743021234863">"स्थानिक ब्लूटूथ फोन कॉंफिगर करण्याकरिता आणि दूरस्थ डिव्हाइस शोधण्यासाठी आणि त्यासह जोडण्यासाठी अ‍ॅप ला अनुमती देते."</string>
+ <string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"स्थानिक ब्लूटूथ टॅबलेट कॉन्फिगर करण्याकरिता आणि दूरस्थ डिव्हाइस शोधण्यासाठी आणि त्यासह जोडण्यासाठी अ‍ॅप ला अनुमती देते."</string>
+ <string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"तुमच्या Android TV डिव्हाइसवर ब्लूटूथ कॉन्फिगर करण्याकरिता आणि पेअर केलेली आणि रीमोट डिव्हाइस शोधण्यासाठी ॲपला अनुमती देते."</string>
+ <string name="permdesc_bluetoothAdmin" product="default" msgid="7381341743021234863">"स्थानिक ब्लूटूथ फोन कॉन्फिगर करण्याकरिता आणि दूरस्थ डिव्हाइस शोधण्यासाठी आणि त्यासह जोडण्यासाठी अ‍ॅप ला अनुमती देते."</string>
<string name="permlab_accessWimaxState" msgid="7029563339012437434">"WiMAX कनेक्ट करा आणि त्यावरून डिस्कनेक्ट करा"</string>
<string name="permdesc_accessWimaxState" msgid="5372734776802067708">"WiMAX सक्षम केले आहे किंवा नाही आणि कनेक्ट केलेल्या कोणत्याही WiMAX नेटवर्क विषयीची माहिती निर्धारित करण्यासाठी अ‍ॅप ला अनुमती देते."</string>
<string name="permlab_changeWimaxState" msgid="6223305780806267462">"WiMAX स्थिती बदला"</string>
@@ -592,9 +592,9 @@
<string name="permdesc_changeWimaxState" product="tv" msgid="5373274458799425276">"ॲपला तुमच्या Android TV डिव्हाइसशी कनेक्ट करण्याची आणि तुमचे Android TV डिव्हाइस WiMAX नेटवर्कवरून डिस्कनेक्ट करण्याची परवानगी देते."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="1551666203780202101">"WiMAX नेटवर्कवर फोन कनेक्ट करण्यास आणि त्यावरून फोन डिस्कनेक्ट करण्यास अ‍ॅप ला अनुमती देते."</string>
<string name="permlab_bluetooth" msgid="586333280736937209">"ब्लूटूथ डीव्हाइससह जोडा"</string>
- <string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"टॅबलेटवर ब्लूटूथ चे कॉंफिगरेशन पाहण्यासाठी आणि पेअर केलेल्या डीव्हाइससह कनेक्शन इंस्टॉल करण्यासाठी आणि स्वीकारण्यासाठी, अ‍ॅप ला अनुमती देते."</string>
- <string name="permdesc_bluetooth" product="tv" msgid="8851534496561034998">"Android TV डिव्हाइसवर ब्लूटूथ चे कॉंफिगरेशन पाहण्यासाठी आणि पेअर केलेल्या डीव्हाइससह कनेक्शन तयार करण्यासाठी आणि स्वीकारण्यासाठी, ॲपला अनुमती देते."</string>
- <string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"फोनवर ब्लूटूथ चे कॉंफिगरेशन पाहण्यासाठी आणि पेअर केलेल्या डीव्हाइससह कनेक्शन इंस्टॉल करण्यासाठी आणि स्वीकारण्यासाठी, अ‍ॅप ला अनुमती देते."</string>
+ <string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"टॅबलेटवर ब्लूटूथचे कॉन्फिगरेशन पाहण्यासाठी आणि पेअर केलेल्या डिव्हाइससह कनेक्शन इंस्टॉल करण्यासाठी आणि स्वीकारण्यासाठी, अ‍ॅपला अनुमती देते."</string>
+ <string name="permdesc_bluetooth" product="tv" msgid="8851534496561034998">"Android TV डिव्हाइसवर ब्लूटूथ चे कॉन्फिगरेशन पाहण्यासाठी आणि पेअर केलेल्या डिव्हाइससह कनेक्शन तयार करण्यासाठी आणि स्वीकारण्यासाठी, अ‍ॅपला अनुमती देते."</string>
+ <string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"फोनवर ब्लूटूथचे कॉन्फिगरेशन पाहण्यासाठी आणि पेअर केलेल्या डिव्हाइससह कनेक्शन इंस्टॉल करण्यासाठी आणि स्वीकारण्यासाठी, अ‍ॅपला अनुमती देते."</string>
<string name="permlab_bluetooth_scan" msgid="5402587142833124594">"जवळपासची ब्लूटूथ डिव्‍हाइस शोधा आणि ती पेअर करा"</string>
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"ॲपला जवळपासची ब्लूटूथ डिव्‍हाइस शोधण्यासाठी आणि ती पेअर करण्यासाठी अनुमती देते"</string>
<string name="permlab_bluetooth_connect" msgid="6657463246355003528">"पेअर केलेल्या ब्लूटूथ डिव्‍हाइसशी कनेक्ट करा"</string>
@@ -665,19 +665,19 @@
<string name="fingerprint_dialog_use_fingerprint_instead" msgid="6226091888364083421">"चेहरा ओळखू शकत नाही. त्याऐवजी फिंगरप्रिंट वापरा."</string>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"फिंगरप्रिंट ऑथेंटिकेट केली आहे"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"चेहरा ऑथेंटिकेशन केलेला आहे"</string>
- <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"चेहरा ऑथेंटिकेशन केलेला आहे, कृपया कंफर्म प्रेस करा"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"फिंगरप्रिंट हार्डवेअर उपलब्‍ध नाही."</string>
+ <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"चेहरा ऑथेंटिकेशन केलेला आहे, कृपया कन्फर्म प्रेस करा"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"फिंगरप्रिंट हार्डवेअर उपलब्‍ध नाही"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"फिंगरप्रिंट सेट करता आली नाही"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"फिंगरप्रिंट सेट करण्याची वेळ संपली आहे. पुन्हा प्रयत्न करा."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"फिंगरप्रिंट ऑपरेशन रद्द झाले."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"वापरकर्त्याने फिंगरप्रिंट ऑपरेशन रद्द केले."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"फिंगरप्रिंट ऑपरेशन रद्द करण्यात आले आहे"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"वापरकर्त्याने फिंगरप्रिंट ऑपरेशन रद्द केले आहे"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"खूप जास्त प्रयत्न. त्याऐवजी स्क्रीन लॉक वापरा."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"खूप जास्त प्रयत्न. त्याऐवजी स्क्रीन लॉक वापरा."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"फिंगरप्रिंटवर प्रक्रिया करू शकत नाही. पुन्हा प्रयत्न करा."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"कोणत्याही फिंगरप्रिंटची नोंद झाली नाही"</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"या डिव्हाइसमध्ये फिंगरप्रिंट सेन्सर नाही."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"सेन्सर तात्पुरता बंद केला आहे."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"फिंगरप्रिंट सेन्सर वापरू शकत नाही. दुरुस्तीच्या सेवा पुरवठादाराला भेट द्या"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"कोणत्याही फिंगरप्रिंटची नोंदणी करण्यात आली नाही"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"या डिव्हाइसवर फिंगरप्रिंट सेन्सर नाही"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"सेन्सर तात्पुरते बंद करण्यात आले आहे"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"फिंगरप्रिंट सेन्सर वापरू शकत नाही. दुरुस्तीच्या सेवा पुरवठादाराला भेट द्या."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"पॉवर बटण दाबले"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g> बोट"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"फिंगरप्रिंट वापरा"</string>
@@ -796,7 +796,7 @@
<string name="permlab_bindDreamService" msgid="4776175992848982706">"स्‍वप्न सेवेवर प्रतिबद्ध करा"</string>
<string name="permdesc_bindDreamService" msgid="9129615743300572973">"होल्‍डरला स्‍वप्नसेवेच्या शीर्ष-स्‍तराच्या इंटरफेसशी प्रतिबद्ध करण्‍यास अनुमती देते. सामान्‍य अ‍ॅप्‍सकरिता कधीही आवश्‍यक नसते."</string>
<string name="permlab_invokeCarrierSetup" msgid="5098810760209818140">"वाहकाद्वारे-प्रदान केलेल्‍या कॉन्‍फिगरेशन अ‍ॅपची विनंती करा"</string>
- <string name="permdesc_invokeCarrierSetup" msgid="4790845896063237887">"होल्‍डरला वाहकद्वारे-प्रदान केलेल्या कॉंफिगरेशन अ‍ॅपची विनंती करण्‍याची अनुमती देते. सामान्‍य अ‍ॅप्‍ससाठी कधीही आवश्‍यक नसावे."</string>
+ <string name="permdesc_invokeCarrierSetup" msgid="4790845896063237887">"होल्‍डरला वाहकाद्वारे दिलेल्या कॉन्फिगरेशन अ‍ॅपची विनंती करण्‍याची अनुमती देते. सामान्‍य अ‍ॅप्‍ससाठी कधीही आवश्‍यक नसावे."</string>
<string name="permlab_accessNetworkConditions" msgid="1270732533356286514">"नेटवर्क स्‍थितींवरील निरीक्षणांसाठी ऐका"</string>
<string name="permdesc_accessNetworkConditions" msgid="2959269186741956109">"अनु्प्रयोगाला नेटवर्क स्‍थितींवरील निरीक्षणे ऐकण्‍यासाठी अनुमती देते. सामान्‍य अ‍ॅप्‍ससाठी कधीही आवश्‍यक नसावे."</string>
<string name="permlab_setInputCalibration" msgid="932069700285223434">"इनपुट डिव्हाइस कॅलिब्रेशन बदला"</string>
@@ -812,7 +812,7 @@
<string name="permlab_bindCarrierServices" msgid="2395596978626237474">"वाहक सेवांवर प्रतिबद्ध करा"</string>
<string name="permdesc_bindCarrierServices" msgid="9185614481967262900">"वाहक सेवांवर प्रतिबद्ध करण्यासाठी होल्डरला अनुमती देते. सामान्य ॲप्ससाठी कधीही आवश्यकता नसावी."</string>
<string name="permlab_access_notification_policy" msgid="5524112842876975537">"व्यत्यय आणू नका अ‍ॅक्सेस करा"</string>
- <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"व्यत्यय आणू नका कॉंफिगरेशन वाचण्यासाठी आणि लिहिण्यासाठी ॲपला अनुमती देते."</string>
+ <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"व्यत्यय आणू नका हे कॉन्फिगरेशन वाचण्यासाठी आणि लिहिण्यासाठी अ‍ॅपला अनुमती देते."</string>
<string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"व्ह्यू परवानगी वापर सुरू करा"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"धारकास अ‍ॅपसाठी परवानगी वापरणे सुरू करण्याची अनुमती देते. सामान्य अ‍ॅप्ससाठी कधीही आवश्यकता नसते."</string>
<string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"परवानगीशी संबंधित निर्णय पाहणे सुरू करा"</string>
@@ -1419,8 +1419,8 @@
<string name="select_input_method" msgid="3971267998568587025">"इनपुट पद्धत निवडा"</string>
<string name="show_ime" msgid="6406112007347443383">"भौतिक कीबोर्ड सक्रिय असताना त्यास स्क्रीनवर ठेवा"</string>
<string name="hardware" msgid="3611039921284836033">"ऑन-स्क्रीन कीबोर्ड वापरा"</string>
- <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"<xliff:g id="DEVICE_NAME">%s</xliff:g> कॉंफिगर करा"</string>
- <string name="select_multiple_keyboards_layout_notification_title" msgid="6999491025126641938">"वास्तविक कीबोर्ड कॉंफिगर करा"</string>
+ <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"<xliff:g id="DEVICE_NAME">%s</xliff:g> कॉन्फिगर करा"</string>
+ <string name="select_multiple_keyboards_layout_notification_title" msgid="6999491025126641938">"वास्तविक कीबोर्ड कॉन्फिगर करा"</string>
<string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"भाषा आणि लेआउट निवडण्यासाठी टॅप करा"</string>
<string name="fast_scroll_alphabet" msgid="8854435958703888376">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="2529539945421557329">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
@@ -2371,12 +2371,12 @@
<string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"बॅटरी सेव्हर सुरू असल्यामुळे Dual Screen उपलब्ध नाही. तुम्ही हे सेटिंग्ज मध्ये बंद करू शकता."</string>
<string name="device_state_notification_settings_button" msgid="691937505741872749">"सेटिंग्ज वर जा"</string>
<string name="device_state_notification_turn_off_button" msgid="6327161707661689232">"बंद करा"</string>
- <string name="keyboard_layout_notification_selected_title" msgid="1202560174252421219">"<xliff:g id="DEVICE_NAME">%s</xliff:g> कॉंफिगर केले आहे"</string>
+ <string name="keyboard_layout_notification_selected_title" msgid="1202560174252421219">"<xliff:g id="DEVICE_NAME">%s</xliff:g> कॉन्फिगर केले आहे"</string>
<string name="keyboard_layout_notification_one_selected_message" msgid="4314216053129257197">"कीबोर्ड लेआउट <xliff:g id="LAYOUT_1">%s</xliff:g> वर सेट केला. बदलण्यासाठी टॅप करा."</string>
<string name="keyboard_layout_notification_two_selected_message" msgid="1876349944065922950">"कीबोर्ड लेआउट <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g> वर सेट केला. बदलण्यासाठी टॅप करा."</string>
<string name="keyboard_layout_notification_three_selected_message" msgid="280734264593115419">"कीबोर्ड लेआउट <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g> वर सेट केला. बदलण्यासाठी टॅप करा."</string>
<string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"कीबोर्ड लेआउट <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g> वर सेट करा… बदलण्यासाठी टॅप करा."</string>
- <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"वास्तविक कीबोर्ड कॉंफिगर केला"</string>
+ <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"वास्तविक कीबोर्ड कॉन्फिगर केला"</string>
<string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"कीबोर्ड पाहण्यासाठी टॅप करा"</string>
<string name="profile_label_private" msgid="6463418670715290696">"खाजगी"</string>
<string name="profile_label_clone" msgid="769106052210954285">"क्लोन"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 5865a7493b47..7112ed389f99 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Cap jari disahkan"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Wajah disahkan"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Wajah disahkan, sila tekan sahkan"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Perkakasan cap jari tidak tersedia."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Perkakasan cap jari tidak tersedia"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Tidak dapat menyediakan cap jari"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Persediaan cap jari telah tamat masa. Cuba lagi."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Pengendalian cap jari dibatalkan."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Pengendalian cap jari dibatalkan oleh pengguna."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Pengendalian cap jari dibatalkan"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Pengendalian cap jari dibatalkan oleh pengguna"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Terlalu banyak percubaan. Gunakan kunci skrin."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Terlalu banyak percubaan. Gunakan kunci skrin."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Tidak dapat memproses cap jari. Cuba lagi."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Tiada cap jari didaftarkan."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Peranti ini tiada penderia cap jari."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Penderia dilumpuhkan sementara."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Tidak boleh menggunakan penderia cap jari. Lawati penyedia pembaikan"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Tiada cap jari didaftarkan"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Peranti ini tiada penderia cap jari"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Penderia dilumpuhkan untuk sementara"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Tidak dapat menggunakan penderia cap jari. Lawati penyedia pembaikan."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Butang kuasa ditekan"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Jari <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Gunakan cap jari"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index c77c8aac3b12..2c3e7b743afe 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"လက်ဗွေကို အထောက်အထား စိစစ်ပြီးပါပြီ"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"မျက်နှာ အထောက်အထားစိစစ်ပြီးပြီ"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"မျက်နှာ အထောက်အထားစိစစ်ပြီးပြီ၊ အတည်ပြုရန်ကို နှိပ်ပါ"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"လက်ဗွေ စက်ပစ္စည်းမရနိုင်ပါ။"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"လက်ဗွေစက် မရနိုင်ပါ"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"လက်ဗွေကို စနစ်ထည့်သွင်း၍ မရပါ"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"လက်ဗွေစနစ်ထည့်သွင်းချိန် ကုန်သွားပါပြီ။ ထပ်စမ်းကြည့်ပါ။"</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"လက်ဗွေယူခြင်း ပယ်ဖျက်လိုက်သည်။"</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"လက်ဗွေဖြင့် အထောက်အထားစိစစ်ခြင်းကို အသုံးပြုသူက ပယ်ဖျက်ထားသည်။"</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"လက်ဗွေဖြင့် လုပ်ဆောင်ခြင်းကို ပယ်ဖျက်လိုက်သည်"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"လက်ဗွေဖြင့် လုပ်ဆောင်ခြင်းကို အသုံးပြုသူက ပယ်ဖျက်လိုက်သည်"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"ကြိုးပမ်းမှုအကြိမ်ရေ များလွန်းသည်။ ဖန်သားပြင်လော့ခ်ချခြင်းကို အစားထိုးသုံးပါ။"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ကြိုးပမ်းမှုအကြိမ်ရေ များလွန်းသည်။ ဖန်သားပြင်လော့ခ်ချခြင်းကို အစားထိုးသုံးပါ။"</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"လက်ဗွေကို လုပ်ဆောင်နိုင်ခြင်းမရှိပါ။ ထပ်စမ်းကြည့်ပါ။"</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"မည်သည့် လက်ဗွေကိုမျှ ထည့်သွင်းမထားပါ။"</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ဤစက်တွင် လက်ဗွေအာရုံခံကိရိယာ မရှိပါ။"</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"အာရုံခံကိရိယာကို ယာယီပိတ်ထားသည်။"</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"လက်ဗွေ အာရုံခံကိရိယာကို အသုံးပြု၍ မရပါ။ ပြုပြင်ရေး ဝန်ဆောင်မှုပေးသူထံသို့ သွားပါ"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"မည်သည့်လက်ဗွေကိုမျှ ထည့်သွင်းမထားပါ"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"ဤစက်ပစ္စည်းတွင် လက်ဗွေအာရုံခံကိရိယာ မရှိပါ"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"အာရုံခံကိရိယာကို ယာယီပိတ်ထားသည်"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"လက်ဗွေ အာရုံခံကိရိယာကို သုံး၍မရပါ။ ပြုပြင်ရေး ဝန်ဆောင်မှုပေးသူထံသို့ သွားပါ။"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"ဖွင့်ပိတ်ခလုတ် နှိပ်ထားသည်"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"လက်ချောင်း <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"လက်ဗွေ သုံးခြင်း"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 2bed82e71808..541e1b540570 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingeravtrykket er godkjent"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Ansiktet er autentisert"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Ansiktet er autentisert. Trykk på Bekreft"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Maskinvare for fingeravtrykk er ikke tilgjengelig."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Maskinvare for fingeravtrykk er ikke tilgjengelig"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Kan ikke konfigurere fingeravtrykk"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Konfigureringen av fingeravtrykk er tidsavbrutt. Prøv på nytt."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingeravtrykk-operasjonen ble avbrutt."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Fingeravtrykk-operasjonen ble avbrutt av brukeren."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Fingeravtrykk-operasjonen ble avbrutt"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Fingeravtrykk-operasjonen ble avbrutt av brukeren"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"For mange forsøk. Bruk skjermlås i stedet."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"For mange forsøk. Bruk skjermlås i stedet."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Kan ikke behandle fingeravtrykket. Prøv på nytt."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Ingen fingeravtrykk er registrert."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Denne enheten har ikke fingeravtrykkssensor."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensoren er midlertidig slått av."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Kan ikke bruke fingeravtrykkssensoren. Gå til en reparasjonsleverandør"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Ingen fingeravtrykk er registrert"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Denne enheten har ikke fingeravtrykkssensor"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensoren er midlertidig slått av"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Kan ikke bruke fingeravtrykkssensoren. Gå til en reparasjonsleverandør."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Av/på-knappen ble trykket"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Bruk fingeravtrykk"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 6e4c0d200821..8c951e0a9dab 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"फिंगरप्रिन्ट प्रमाणीकरण गरियो"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"अनुहार प्रमाणीकरण गरियो"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"अनुहार प्रमाणीकरण गरियो, कृपया पुष्टि गर्नुहोस् थिच्नुहोस्"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"औँठाछाप हार्डवेयर उपलब्ध छैन।"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"फिंगरप्रिन्ट हार्डवेयर उपलब्ध छैन"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"फिंगरप्रिन्ट सेटअप गर्न सकिएन"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"फिंगरप्रिन्ट सेट अप गर्ने समय सकियो। फेरि प्रयास गर्नुहोस्।"</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"फिंगरप्रिन्ट सञ्चालन रद्द गरियो।"</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"प्रयोगकर्ताले फिंगरप्रिन्टसम्बन्धी कारबाही रद्द गर्नुभयो।"</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"फिंगरप्रिन्टसम्बन्धी कारबाही रद्द गरियो"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"प्रयोगकर्ताले फिंगरप्रिन्टसम्बन्धी कारबाही रद्द गर्नुभयो"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"निकै धेरै पटक प्रयास गरिसकिएको छ। बरु स्क्रिन लक प्रयोग गर्नुहोस्।"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"निकै धेरै पटक प्रयास गरिसकिएको छ। बरु स्क्रिन लक प्रयोग गर्नुहोस्।"</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"फिंगरप्रिन्ट पहिचान गर्ने प्रक्रिया अघि बढाउन सकिएन। फेरि प्रयास गर्नुहोस्।"</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"कुनै पनि फिंगरप्रिन्ट दर्ता गरिएको छैन।"</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"यो डिभाइसमा कुनै पनि फिंगरप्रिन्ट सेन्सर छैन।"</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"केही समयका लागि सेन्सर असक्षम पारियो।"</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"फिंगरप्रिन्ट सेन्सर प्रयोग गर्न मिल्दैन। फिंगरप्रिन्ट सेन्सर मर्मत गर्ने सेवा प्रदायक कम्पनीमा सम्पर्क गर्नुहोस्"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"कुनै पनि फिंगरप्रिन्ट दर्ता गरिएको छैन"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"यो डिभाइसमा कुनै फिंगरप्रिन्ट सेन्सर छैन"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"केही समयका लागि सेन्सर अफ गरियो"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"फिंगरप्रिन्ट सेन्सर प्रयोग गर्न सकिएन। फिंगरप्रिन्ट सेन्सर मर्मत गर्ने सेवा प्रदायक कम्पनीमा सम्पर्क गर्नुहोस्।"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"पावर बटन थिचियो"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"औंला <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"फिंगरप्रिन्ट प्रयोग गर्नुहोस्"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 2de4cb9acf39..bbcc2aa88440 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Vingerafdruk geverifieerd"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Gezicht geverifieerd"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Gezicht geverifieerd. Druk op Bevestigen."</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardware voor vingerafdruk niet beschikbaar."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Hardware voor vingerafdruk niet beschikbaar"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Kan vingerafdruk niet instellen"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Time-out bij instellen van vingerafdruk. Probeer het opnieuw."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Vingerafdrukbewerking geannuleerd."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Vingerafdrukverificatie geannuleerd door gebruiker."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Vingerafdrukbewerking geannuleerd"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Vingerafdrukverificatie geannuleerd door gebruiker"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Te veel pogingen. Gebruik in plaats daarvan de schermvergrendeling."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Te veel pogingen. Gebruik in plaats daarvan de schermvergrendeling."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Kan vingerafdruk niet verwerken. Probeer het opnieuw."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Geen vingerafdrukken geregistreerd."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Dit apparaat heeft geen vingerafdruksensor."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor staat tijdelijk uit."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Kan vingerafdruksensor niet gebruiken. Ga naar een reparateur."</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Geen vingerafdrukken geregistreerd"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Dit apparaat heeft geen vingerafdruksensor"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensor tijdelijk uitgezet"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Kan vingerafdruksensor niet gebruiken. Ga naar een reparateur."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Aan/uit-knop ingedrukt"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Vinger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Vingerafdruk gebruiken"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 2a4a07ef9746..d8bcbd10f5f5 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"ଟିପଚିହ୍ନ ପ୍ରମାଣିତ ହେଲା"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ମୁହଁ ଚିହ୍ନଟ ହୋଇଛି"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ମୁହଁ ଚିହ୍ନଟ ହୋଇଛି, ଦୟାକରି ସୁନିଶ୍ଚିତ ଦବାନ୍ତୁ"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"ଟିପଚିହ୍ନ ହାର୍ଡୱେର୍‍ ଉପଲବ୍ଧ ନାହିଁ।"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"ଟିପଚିହ୍ନ ହାର୍ଡୱେର ଉପଲବ୍ଧ ନାହିଁ"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"ଟିପଚିହ୍ନକୁ ସେଟ୍ ଅପ୍ କରାଯାଇପାରିବ ନାହିଁ"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"ଫିଙ୍ଗରପ୍ରିଣ୍ଟ ସେଟଅପର ସମୟସୀମା ସମାପ୍ତ ହୋଇଯାଇଛି। ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"ଟିପଚିହ୍ନ କାର୍ଯ୍ୟ ବାତିଲ୍ କରାଗଲା।"</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ଉପଯୋଗକର୍ତ୍ତା ଟିପଚିହ୍ନ କାର୍ଯ୍ୟ ବାତିଲ୍ କରିଛନ୍ତି।"</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"ଟିପଚିହ୍ନ ଅପରେସନକୁ ବାତିଲ କରାଯାଇଛି"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"ୟୁଜରଙ୍କ ଦ୍ୱାରା ଟିପଚିହ୍ନ ଅପରେସନକୁ ବାତିଲ କରାଯାଇଛି"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"ଅନେକଗୁଡ଼ିଏ ପ୍ରଚେଷ୍ଟା। ଏହା ପରିବର୍ତ୍ତେ ସ୍କ୍ରିନ ଲକ ବ୍ୟବହାର କରନ୍ତୁ।"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ଅନେକଗୁଡ଼ିଏ ପ୍ରଚେଷ୍ଟା। ଏହା ପରିବର୍ତ୍ତେ ସ୍କ୍ରିନ ଲକ ବ୍ୟବହାର କରନ୍ତୁ।"</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ଟିପଚିହ୍ନକୁ ପ୍ରକ୍ରିୟାନ୍ୱିତ କରାଯାଇପାରିବ ନାହିଁ। ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"କୌଣସି ଆଙ୍ଗୁଠି ଚିହ୍ନ ପଞ୍ଜୀକୃତ ହୋଇନାହିଁ।"</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ଏହି ଡିଭାଇସ୍‌ରେ ଟିପଚିହ୍ନ ସେନ୍‍ସର୍ ନାହିଁ।"</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ସେନ୍ସରକୁ ଅସ୍ଥାୟୀ ଭାବେ ଅକ୍ଷମ କରାଯାଇଛି।"</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"ଟିପଚିହ୍ନ ସେନ୍ସରକୁ ବ୍ୟବହାର କରାଯାଇପାରିବ ନାହିଁ। ଏକ ମରାମତି କେନ୍ଦ୍ରକୁ ଭିଜିଟ୍ କରନ୍ତୁ"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"କୌଣସି ଟିପଚିହ୍ନକୁ ପଞ୍ଜିକରଣ କରାଯାଇନାହିଁ"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"ଏହି ଡିଭାଇସ୍‌ରେ ଟିପଚିହ୍ନ ସେନସର୍‌ ନାହିଁ"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"ସେନ୍ସରକୁ ଅସ୍ଥାୟୀ ଭାବେ ଅକ୍ଷମ କରାଯାଇଛି"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"ଟିପଚିହ୍ନ ସେନ୍ସରକୁ ବ୍ୟବହାର କରାଯାଇପାରିବ ନାହିଁ। ଏକ ମରାମତି କେନ୍ଦ୍ରକୁ ଭିଜିଟ୍ କରନ୍ତୁ।"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"ପାୱାର ବଟନ ଦବାଯାଇଛି"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"ଆଙ୍ଗୁଠି <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ଟିପଚିହ୍ନ ବ୍ୟବହାର କରନ୍ତୁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index db4c38e7349c..b762d6621d92 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਪ੍ਰਮਾਣਿਤ ਹੋਇਆ"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ਚਿਹਰਾ ਪੁਸ਼ਟੀਕਰਨ"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ਚਿਹਰਾ ਪੁਸ਼ਟੀਕਰਨ, ਕਿਰਪਾ ਕਰਕੇ \'ਪੁਸ਼ਟੀ ਕਰੋ\' ਦਬਾਓ"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਹਾਰਡਵੇਅਰ ਉਪਲਬਧ ਨਹੀਂ ਹੈ।"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਹਾਰਡਵੇਅਰ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਾ ਸੈੱਟਅੱਪ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਾ ਸੈੱਟਅੱਪ ਕਰਨ ਲਈ ਸਮਾਂ ਸਮਾਪਤ ਹੋਇਆ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਓਪਰੇਸ਼ਨ ਰੱਦ ਕੀਤਾ ਗਿਆ।"</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਓਪਰੇਸ਼ਨ ਵਰਤੋਂਕਾਰ ਵੱਲੋਂ ਰੱਦ ਕੀਤਾ ਗਿਆ।"</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੰਬੰਧੀ ਕਾਰਵਾਈ ਰੱਦ ਕੀਤੀ ਗਈ"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੰਬੰਧੀ ਕਾਰਵਾਈ ਵਰਤੋਂਕਾਰ ਵੱਲੋਂ ਰੱਦ ਕੀਤੀ ਗਈ"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"ਬਹੁਤ ਸਾਰੀਆਂ ਕੋਸ਼ਿਸ਼ਾਂ। ਇਸਦੀ ਬਜਾਏ ਸਕ੍ਰੀਨ ਲਾਕ ਵਰਤੋ।"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ਬਹੁਤ ਸਾਰੀਆਂ ਕੋਸ਼ਿਸ਼ਾਂ। ਇਸਦੀ ਬਜਾਏ ਸਕ੍ਰੀਨ ਲਾਕ ਵਰਤੋ।"</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ਫਿੰਗਰਪ੍ਰਿੰਟ \'ਤੇ ਪ੍ਰਕਿਰਿਆ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ਕੋਈ ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਰਜ ਨਹੀਂ ਕੀਤੇ ਗਏ।"</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ਇਸ ਡੀਵਾਈਸ ਵਿੱਚ ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ ਨਹੀਂ ਹੈ।"</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ਸੈਂਸਰ ਅਸਥਾਈ ਤੌਰ \'ਤੇ ਬੰਦ ਕੀਤਾ ਗਿਆ।"</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ ਦੀ ਵਰਤੋਂ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ। ਮੁਰੰਮਤ ਪ੍ਰਦਾਨਕ \'ਤੇ ਜਾਓ"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"ਕੋਈ ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਰਜ ਨਹੀਂ ਕੀਤਾ ਗਿਆ"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"ਇਸ ਡੀਵਾਈਸ ਵਿੱਚ ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ ਨਹੀਂ ਹੈ"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"ਸੈਂਸਰ ਕੁਝ ਸਮੇਂ ਲਈ ਬੰਦ ਕੀਤਾ ਗਿਆ"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ ਦੀ ਵਰਤੋਂ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ। ਮੁਰੰਮਤ ਕਰਨ ਵਾਲੇ ਪ੍ਰਦਾਨਕ ਕੋਲ ਜਾਓ।"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"\'ਪਾਵਰ\' ਬਟਨ ਦਬਾਇਆ ਗਿਆ"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"ਉਂਗਲ <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 1dcc7393f0a9..5f3e683d8053 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -668,18 +668,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Uwierzytelniono odciskiem palca"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Twarz rozpoznana"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Twarz rozpoznana, kliknij Potwierdź"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Czytnik linii papilarnych nie jest dostępny."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Czytnik linii papilarnych nie jest dostępny"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Nie można skonfigurować odcisku palca"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Upłynął limit czasu konfiguracji odcisku palca. Spróbuj ponownie."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Odczyt odcisku palca został anulowany."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Odczyt odcisku palca został anulowany przez użytkownika."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Odczyt odcisku palca został anulowany"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Odczyt odcisku palca został anulowany przez użytkownika"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Zbyt wiele prób. Użyj blokady ekranu."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Zbyt wiele prób. Użyj blokady ekranu."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Nie udało się przetworzyć odcisku palca. Spróbuj ponownie."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nie zarejestrowano odcisków palców."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"To urządzenie nie jest wyposażone w czytnik linii papilarnych."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Czujnik jest tymczasowo wyłączony."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Nie można użyć czytnika linii papilarnych. Odwiedź serwis."</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Nie zarejestrowano odcisków palców"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"To urządzenie nie jest wyposażone w czytnik linii papilarnych"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Czujnik tymczasowo wyłączony"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Nie można użyć czytnika linii papilarnych. Odwiedź serwis."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Naciśnięto przycisk zasilania"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Odcisk palca <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Używaj odcisku palca"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index b0a3adbed054..9887bf6d5db6 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -667,18 +667,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Impressão digital autenticada"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Rosto autenticado"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Rosto autenticado, pressione \"Confirmar\""</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardware de impressão digital não disponível."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Hardware de impressão digital indisponível"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Não foi possível configurar a impressão digital"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Tempo de configuração esgotado. Tente de novo."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Operação de impressão digital cancelada."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Operação de impressão digital cancelada pelo usuário."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Operação de impressão digital cancelada"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Operação de impressão digital cancelada pelo usuário"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Excesso de tentativas. Use o bloqueio de tela."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Excesso de tentativas. Use o bloqueio de tela."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Não foi possível processar a impressão digital. Tente de novo."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nenhuma impressão digital registrada."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo não tem um sensor de impressão digital."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor desativado temporariamente."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Não foi possível usar o sensor de impressão digital. Entre em contato com uma assistência técnica"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Nenhuma impressão digital registrada"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Este dispositivo não tem um sensor de impressão digital"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensor desativado temporariamente"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Não foi possível usar o sensor de impressão digital. Entre em contato com uma assistência técnica."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Botão liga/desliga pressionado"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usar impressão digital"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 735aa7c8f508..9903acb3a4ab 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -667,18 +667,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"A impressão digital foi autenticada."</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Rosto autenticado."</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Rosto autenticado. Prima Confirmar."</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardware de impressão digital não disponível."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Hardware de impressão digital não disponível"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Não é possível configurar a impressão digital"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"A configuração da impressão digital expirou. Tente novamente."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Operação de impressão digital cancelada."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Operação de impressão digital cancelada pelo utilizador."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Operação de impressão digital cancelada"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Operação de impressão digital cancelada pelo utilizador"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Demasiadas tentativas. Em alternativa, use o bloqueio de ecrã."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Demasiadas tentativas. Em alternativa, use o bloqueio de ecrã."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Não é possível processar a impressão digital. Tente novamente."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nenhuma impressão digital registada."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo não tem sensor de impressões digitais."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporariamente desativado."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Não é possível usar o sensor de impressões digitais. Visite um fornecedor de serviços de reparação"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Nenhuma impressão digital configurada"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Este dispositivo não tem sensor de impressões digitais."</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensor temporariamente desativado"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Não é possível usar o sensor de impressões digitais. Visite um fornecedor de serviços de reparação."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Botão ligar/desligar premido"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usar a impressão digital"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index b0a3adbed054..9887bf6d5db6 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -667,18 +667,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Impressão digital autenticada"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Rosto autenticado"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Rosto autenticado, pressione \"Confirmar\""</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardware de impressão digital não disponível."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Hardware de impressão digital indisponível"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Não foi possível configurar a impressão digital"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Tempo de configuração esgotado. Tente de novo."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Operação de impressão digital cancelada."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Operação de impressão digital cancelada pelo usuário."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Operação de impressão digital cancelada"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Operação de impressão digital cancelada pelo usuário"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Excesso de tentativas. Use o bloqueio de tela."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Excesso de tentativas. Use o bloqueio de tela."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Não foi possível processar a impressão digital. Tente de novo."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nenhuma impressão digital registrada."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo não tem um sensor de impressão digital."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor desativado temporariamente."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Não foi possível usar o sensor de impressão digital. Entre em contato com uma assistência técnica"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Nenhuma impressão digital registrada"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Este dispositivo não tem um sensor de impressão digital"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensor desativado temporariamente"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Não foi possível usar o sensor de impressão digital. Entre em contato com uma assistência técnica."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Botão liga/desliga pressionado"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usar impressão digital"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 820ca895258a..73d92fbe6fd6 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -667,18 +667,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Amprentă autentificată"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Chip autentificat"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Chip autentificat, apasă pe Confirmă"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardware-ul pentru amprentă nu este disponibil."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Hardware-ul pentru amprentă nu este disponibil"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Nu se poate configura amprenta"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Configurarea amprentei a expirat. Încearcă din nou."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Operațiunea privind amprenta a fost anulată."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Operațiunea privind amprenta a fost anulată de utilizator."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Operațiunea privind amprenta a fost anulată"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Operațiunea privind amprenta a fost anulată de utilizator"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Prea multe încercări. Folosește blocarea ecranului."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Prea multe încercări. Folosește blocarea ecranului."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Nu putem procesa amprenta. Încearcă din nou."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nu au fost înregistrate amprente."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Dispozitivul nu are senzor de amprentă."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzorul este dezactivat temporar."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Nu se poate folosi senzorul de amprentă. Vizitează un furnizor de servicii de reparații."</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Nu au fost înregistrate amprente"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Acest dispozitiv nu are senzor de amprentă"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Senzor dezactivat temporar"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Nu se poate folosi senzorul de amprentă. Vizitează un furnizor de servicii de reparații."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"A fost apăsat butonul de pornire"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Degetul <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Folosește amprenta"</string>
@@ -1166,7 +1166,7 @@
<string name="Midnight" msgid="8176019203622191377">"Miezul nopții"</string>
<string name="elapsed_time_short_format_mm_ss" msgid="8689459651807876423">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
<string name="elapsed_time_short_format_h_mm_ss" msgid="2302144714803345056">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
- <string name="selectAll" msgid="1532369154488982046">"Selectează-le pe toate"</string>
+ <string name="selectAll" msgid="1532369154488982046">"Selectează tot"</string>
<string name="cut" msgid="2561199725874745819">"Decupează"</string>
<string name="copy" msgid="5472512047143665218">"Copiază"</string>
<string name="failed_to_copy_to_clipboard" msgid="725919885138539875">"Eroare la copierea în clipboard"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 0c80e0ec709e..5b434a56f8ec 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -668,18 +668,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Отпечаток пальца проверен"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Лицо распознано"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Лицо распознано, нажмите кнопку \"Подтвердить\""</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Сканер недоступен"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Сканер отпечатков пальцев недоступен."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Не удалось сохранить отпечаток."</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Время настройки отпечатка пальца истекло. Повторите попытку."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Операция с отпечатком отменена."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Операция с отпечатком пальца отменена пользователем."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Операция с отпечатком пальца отменена."</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Пользователь отменил операцию с отпечатком пальца."</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Слишком много попыток. Используйте другой способ разблокировки экрана."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Слишком много попыток. Используйте другой способ разблокировки экрана."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Не удалось распознать отпечаток пальца. Повторите попытку."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Нет отсканированных отпечатков пальцев"</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"На этом устройстве нет сканера отпечатков пальцев."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сканер отпечатков пальцев временно отключен."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Невозможно использовать сканер отпечатков пальцев. Обратитесь в сервисный центр."</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Отпечатки пальцев не добавлены."</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"На этом устройстве нет сканера отпечатков пальцев"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Сканер временно отключен."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Невозможно использовать сканер отпечатков пальцев. Обратитесь в сервисный центр."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Нажата кнопка питания."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Отпечаток <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Использовать отпечаток пальца"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 0aa3200545cb..5a271180499f 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"ඇඟිලි සලකුණ සත්‍යාපනය කරන ලදී"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"මුහුණ සත්‍යාපනය කරන ලදී"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"මුහුණ සත්‍යාපනය කරන ලදී, කරුණාකර තහවුරු කරන්න ඔබන්න"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"ඇඟිලි සලකුණු දෘඪාංගය ලද නොහැකිය."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"ඇඟිලි සලකුණු දෘඪාංගය ලද නොහැක"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"ඇඟිලි සලකුණ පිහිටුවිය නොහැකිය"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"ඇඟිලි සලකුණු පිහිටුවීම කාලය නිමා විය. නැවත උත්සාහ කරන්න."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"ඇඟිලි සලකුණු මෙහෙයුම අවලංගු කරන ලදී."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"පරිශීලක විසින් ඇඟිලි සලකුණු මෙහෙයුම අවසන් කරන ලදී."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"ඇඟිලි සලකුණු මෙහෙයුම අවලංගු කරන ලදි"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"පරිශීලක විසින් ඇඟිලි සලකුණු මෙහෙයුම අවසන් කරන ලදි"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"උත්සාහ ගණන ඉතා වැඩියි. ඒ වෙනුවට තිර අගුල භාවිත කරන්න."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"උත්සාහ ගණන ඉතා වැඩියි. ඒ වෙනුවට තිර අගුල භාවිත කරන්න."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ඇඟිලි සලකුණ සැකසීමට නොහැක. නැවත උත්සාහ කරන්න."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ඇඟිලි සලකුණු ඇතුළත් කර නොමැත."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"මෙම උපාංගයේ ඇඟිලි සලකුණු සංවේදකයක් නොමැත."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"සංවේදකය තාවකාලිකව අබල කර ඇත."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"ඇඟිලි සලකුණු සංවේදකය භාවිත කළ නොහැකිය. අළුත්වැඩියා සැපයුම්කරුවෙකු බලන්න"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"ඇඟිලි සලකුණු ඇතුළත් කර නොමැත"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"මෙම උපාංගයේ ඇඟිලි සලකුණු සංවේදකයක් නොමැත"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"සංවේදකය තාවකාලිකව අබල කර ඇත"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"ඇඟිලි සලකුණු සංවේදකය භාවිත කළ නොහැක. අලුත්වැඩියා සැපයුම්කරුවෙකු වෙත පැමිණෙන්න."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"බල බොත්තම ඔබා ඇත"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"ඇඟිලි <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ඇඟිලි සලකුණ භාවිත කරන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 2ddfbffe4b7c..16a56d3156f1 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -668,18 +668,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Odtlačok prsta bol overený"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Tvár bola overená"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Tvár bola overená, stlačte tlačidlo potvrdenia"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardvér na snímanie odtlačku prsta nie je k dispozícii"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Hardvér na odtlačky prstov nie je k dispozícii"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Odtlačok prsta sa nedá nastaviť"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Nastavenie odtlačku prsta vypršalo. Skúste to znova."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Operácia týkajúca sa odtlačku prsta bola zrušená"</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Overenie odtlačku prsta zrušil používateľ."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Operácia týkajúca sa odtlačku prsta bola zrušená"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Overenie odtlačku prsta zrušil používateľ"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Príliš veľa pokusov. Použite radšej zámku obrazovky."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Príliš veľa pokusov. Použite radšej zámku obrazovky."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Odtlačok prsta sa nedá spracovať. Skúste to znova."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Neregistrovali ste žiadne odtlačky prstov."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Toto zariadenie nemá senzor odtlačkov prstov."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je dočasne vypnutý."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Senzor odtlačkov prstov nie je možné používať. Navštívte poskytovateľa opráv."</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Nie sú registrované žiadne odtlačky prstov"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Toto zariadenie nemá senzor odtlačkov prstov"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Senzor je dočasne vypnutý"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Senzor odtlačkov prstov nie je možné používať. Navštívte opravára."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Bol stlačený vypínač"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Prst: <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Použiť odtlačok prsta"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 191570374251..79a275ca7089 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -668,18 +668,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Pristnost prstnega odtisa je preverjena"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Pristnost obraza je potrjena"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Pristnost obraza je preverjena. Pritisnite gumb »Potrdi«."</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Strojna oprema za prstne odtise ni na voljo."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Strojna oprema za prstne odtise ni na voljo"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Prstnega odtisa ni mogoče nastaviti."</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Čas za nastavitev prstnega odtisa je potekel. Poskusite znova."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Dejanje s prstnim odtisom je bilo preklicano."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Dejanje s prstnim odtisom je preklical uporabnik."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Dejanje s prstnim odtisom je bilo preklicano"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Dejanje s prstnim odtisom je preklical uporabnik"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Preveč poskusov. Odklenite z načinom za zaklepanje zaslona."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Preveč poskusov. Odklenite z zaklepanjem zaslona."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Prstnega odtisa ni mogoče obdelati. Poskusite znova."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Ni registriranih prstnih odtisov."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ta naprava nima tipala prstnih odtisov."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Tipalo je začasno onemogočeno."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Tipala prstnih odtisov ni mogoče uporabiti. Obiščite ponudnika popravil."</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Ni prijavljenih prstnih odtisov"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Ta naprava nima tipala prstnih odtisov"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Tipalo je začasno onemogočeno"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Tipala prstnih odtisov ni mogoče uporabiti. Obiščite ponudnika popravil."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Gumb za vklop je pritisnjen."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Uporaba prstnega odtisa"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 9b3935173de1..fc2e71590b9a 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Gjurma e gishtit u vërtetua"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Fytyra u vërtetua"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Fytyra u vërtetua, shtyp \"Konfirmo\""</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardueri i gjurmës së gishtit nuk mundësohet."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Hardueri i gjurmës së gishtit nuk ofrohet"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Nuk mund të konfigurohet gjurma e gishtit"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Konfigurimi i gjurmës së gishtit skadoi. Provo përsëri."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Operacioni i gjurmës së gishtit u anulua."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Veprimi i gjurmës së gishtit u anulua nga përdoruesi."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Veprimi i gjurmës së gishtit u anulua"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Veprimi i gjurmës së gishtit u anulua nga përdoruesi"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Shumë përpjekje. Përdor më mirë kyçjen e ekranit."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Shumë përpjekje. Përdor më mirë kyçjen e ekranit."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Gjurma e gishtit nuk mund të përpunohet. Provo përsëri."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nuk ka asnjë gjurmë gishti të regjistruar."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Kjo pajisje nuk ka sensor të gjurmës së gishtit."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensori është çaktivizuar përkohësisht."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Sensori i gjurmës së gishtit nuk mund të përdoret. Vizito një ofrues të shërbimit të riparimit"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Nuk ka asnjë gjurmë gishti të regjistruar"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Kjo pajisje nuk ka sensor të gjurmës së gishtit"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensori është çaktivizuar përkohësisht"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Sensori i gjurmës së gishtit nuk mund të përdoret. Vizito një ofrues të shërbimit të riparimit."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Butoni i energjisë u shtyp"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Gishti <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Përdor gjurmën e gishtit"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 8440b939466e..4f9ce47d4a90 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -667,18 +667,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Отисак прста је потврђен"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Лице је потврђено"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Лице је потврђено. Притисните Потврди"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Хардвер за отиске прстију није доступан."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Хардвер за отисак прста није доступан"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Подешавање отиска прста није успело"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Време за подешавање отиска прста је истекло. Пробајте поново."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Радња са отиском прста је отказана."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Корисник је отказао радњу са отиском прста."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Радња са отиском прста је отказана"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Корисник је отказао радњу са отиском прста"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Превише покушаја. Користите закључавање екрана уместо тога."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Превише покушаја. Користите закључавање екрана уместо тога."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Обрађивање отиска прста није успело. Пробајте поново."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Није регистрован ниједан отисак прста."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Овај уређај нема сензор за отисак прста."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сензор је привремено онемогућен."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Не можете да користите сензор за отисак прста. Посетите добављача за поправке"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Није регистрован ниједан отисак прста"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Овај уређај нема сензор за отисак прста"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Сензор је привремено онемогућен"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Не можете да користите сензор за отисак прста. Посетите сервис."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Притиснуто је дугме за укључивање"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Прст <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Користите отисак прста"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index cfd3aefbd932..7f0a2014b5f0 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingeravtrycket har autentiserats"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Ansiktet har autentiserats"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Ansiktet har autentiserats. Tryck på Bekräfta"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Det finns ingen maskinvara för fingeravtryck."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Det finns ingen maskinvara för fingeravtryck"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Det gick inte att konfigurera fingeravtryck"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Tiden för fingeravtrycksinställning gick ut. Försök igen."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingeravtrycksåtgärden avbröts."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Fingeravtrycksåtgärden avbröts av användaren."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Fingeravtrycksåtgärden avbröts"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Fingeravtrycksåtgärden avbröts av användaren"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"För många försök. Använd låsskärmen i stället."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"För många försök. Använd låsskärmen i stället."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Det gick inte att bearbeta fingeravtrycket. Försök igen."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Inga fingeravtryck har registrerats."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Enheten har ingen fingeravtryckssensor."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensorn har tillfälligt inaktiverats."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Det går inte att använda fingeravtryckssensorn. Besök ett reparationsställe"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Det finns inga registrerade fingeravtryck"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Enheten har ingen fingeravtryckssensor"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensorn har tillfälligt inaktiverats"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Det går inte att använda fingeravtryckssensorn. Besök ett reparationsställe."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Av/på-knappen nedtryckt"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Använd ditt fingeravtryck"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index ff1a603c1d89..4d004402a6ba 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Imethibitisha alama ya kidole"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Uso umethibitishwa"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Uso umethibitishwa, tafadhali bonyeza thibitisha"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Maunzi ya alama ya kidole hayapatikani."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Maunzi ya kutambua alama ya kidole hayapatikani"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Imeshindwa kuweka mipangilio ya alama ya kidole"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Muda wa kuweka alama ya kidole umeisha. Jaribu tena."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Mchakato wa alama ya kidole umeghairiwa."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Mtumiaji ameghairi uthibitishaji wa alama ya kidole."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Mchakato wa alama ya kidole umeachwa"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Mtumiaji ameacha mchakato wa uthibitishaji wa alama ya kidole"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Umejaribu mara nyingi mno. Badala yake, tumia mbinu ya kufunga skrini."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Umejaribu mara nyingi mno. Badala yake, tumia mbinu ya kufunga skrini."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Imeshindwa kutambua alama ya kidole. Jaribu tena."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Hakuna alama za vidole zilizojumuishwa."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Kifaa hiki hakina kitambua alama ya kidole."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Kitambuzi kimezimwa kwa muda."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Imeshindwa kutumia kitambua alama ya kidole. Tembelea mtoa huduma za urekebishaji"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Hakuna alama za vidole zilizojumuishwa"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Kifaa hiki hakina kitambua alama ya kidole"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Kitambuzi kimezimwa kwa muda"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Imeshindwa kutumia kitambuzi cha alama ya kidole. Tembelea mtoa huduma za urekebishaji."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Kitufe cha kuwasha au kuzima kimebonyezwa"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Kidole cha <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Tumia alama ya kidole"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 6074fdf606e1..f962728c8b30 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"கைரேகை அங்கீகரிக்கப்பட்டது"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"முகம் அங்கீகரிக்கப்பட்டது"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"முகம் அங்கீகரிக்கப்பட்டது. ’உறுதிப்படுத்துக’ என்பதை அழுத்துக"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"கைரேகை வன்பொருள் இல்லை."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"கைரேகை பாகம் இல்லை"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"கைரேகையை அமைக்க முடியவில்லை"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"கைரேகை அமைவுக்கான நேரம் முடிந்துவிட்டது. மீண்டும் முயலவும்."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"கைரேகை செயல்பாடு ரத்துசெய்யப்பட்டது."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"பயனர், கைரேகை உறுதிப்படுத்துதலை ரத்துசெய்தார்."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"கைரேகைச் செயல்பாடு ரத்துசெய்யப்பட்டது"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"கைரேகைச் செயல்பாடு பயனரால் ரத்துசெய்யப்பட்டது"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"பலமுறை முயன்றுவிட்டீர்கள். இதற்குப் பதிலாகத் திரைப்பூட்டைப் பயன்படுத்தவும்."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"பலமுறை முயன்றுவிட்டீர்கள். இதற்குப் பதிலாகத் திரைப்பூட்டைப் பயன்படுத்தவும்."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"கைரேகையைச் செயலாக்க முடியவில்லை. மீண்டும் முயலவும்."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"கைரேகைப் பதிவுகள் எதுவும் இல்லை."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"இந்தச் சாதனத்தில் கைரேகை சென்சார் இல்லை."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"சென்சார் தற்காலிகமாக முடக்கப்பட்டுள்ளது."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"கைரேகை சென்சாரைப் பயன்படுத்த முடியவில்லை. பழுதுபார்ப்புச் சேவை வழங்குநரைத் தொடர்புகொள்ளவும்"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"கைரேகைகள் எதுவும் பதிவுசெய்யப்படவில்லை"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"இந்தச் சாதனத்தில் கைரேகை சென்சார் இல்லை"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"சென்சார் தற்காலிகமாக முடக்கப்பட்டுள்ளது"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"கைரேகை சென்சாரைப் பயன்படுத்த முடியவில்லை. பழுதுபார்ப்புச் சேவை வழங்குநரைத் தொடர்புகொள்ளவும்."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"பவர் பட்டன் அழுத்தப்பட்டுள்ளது"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"கைரேகை <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"கைரேகையைப் பயன்படுத்து"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 3f729667ad04..30c28129e8c5 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"వేలిముద్ర ప్రమాణీకరించబడింది"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ముఖం ప్రమాణీకరించబడింది"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ముఖం ప్రమాణీకరించబడింది, దయచేసి ధృవీకరించును నొక్కండి"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"వేలిముద్ర హార్డ్‌వేర్ అందుబాటులో లేదు."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"వేలిముద్ర హార్డ్‌వేర్ అందుబాటులో లేదు"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"వేలిముద్రను సెటప్ చేయడం సాధ్యం కాదు"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"వేలిముద్ర సెటప్ సమయం ముగిసింది. మళ్లీ ట్రై చేయండి."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"వేలిముద్ర యాక్టివిటీ రద్దయింది."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"వేలిముద్ర చర్యని వినియోగదారు రద్దు చేశారు."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"వేలిముద్ర ఆపరేషన్ రద్దయింది"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"వేలిముద్ర ఆపరేషన్‌ను యూజర్ రద్దు చేశారు"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"చాలా ఎక్కువ సార్లు ప్రయత్నించారు. బదులుగా స్క్రీన్ లాక్‌ను ఉపయోగించండి."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"చాలా ఎక్కువ సార్లు ప్రయత్నించారు. బదులుగా స్క్రీన్ లాక్‌ను ఉపయోగించండి."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"వేలిముద్రను ప్రాసెస్ చేయడం సాధ్యపడదు. మళ్లీ ట్రై చేయండి."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"వేలిముద్రలు నమోదు చేయబడలేదు."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ఈ పరికరంలో వేలిముద్ర సెన్సార్ ఎంపిక లేదు."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"సెన్సార్ తాత్కాలికంగా డిజేబుల్ చేయబడింది."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"వేలిముద్ర సెన్సార్‌ను ఉపయోగించడం సాధ్యం కాదు. రిపెయిర్ ప్రొవైడర్‌ను సందర్శించండి"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"వేలిముద్రలు ఏవీ ఎన్‌రోల్ అవలేదు"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"ఈ పరికరంలో వేలిముద్ర సెన్సార్ లేదు"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"సెన్సార్ తాత్కాలికంగా డిజేబుల్ అయ్యింది"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"వేలిముద్ర సెన్సార్‌ను ఉపయోగించడం సాధ్యం కాదు. రిపెయిర్ ప్రొవైడర్‌ను చూడండి."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Power button pressed"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>వ వేలు"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"వేలిముద్రను ఉపయోగించండి"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 2f8486ce2763..c92d6139147b 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -514,8 +514,8 @@
<string name="permdesc_backgroundCamera" msgid="1615291686191138250">"แอปนี้ถ่ายภาพและวิดีโอด้วยกล้องได้ทุกเมื่อ"</string>
<string name="permlab_systemCamera" msgid="3642917457796210580">"อนุญาตให้แอปพลิเคชันหรือบริการเข้าถึงกล้องของระบบเพื่อถ่ายภาพและวิดีโอ"</string>
<string name="permdesc_systemCamera" msgid="5938360914419175986">"แอปของระบบหรือที่ได้รับสิทธิ์นี้จะถ่ายภาพและบันทึกวิดีโอโดยใช้กล้องของระบบได้ทุกเมื่อ แอปต้องมีสิทธิ์ android.permission.CAMERA ด้วย"</string>
- <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"อนุญาตให้แอปพลิเคชันหรือบริการได้รับโค้ดเรียกกลับเมื่อมีการเปิดหรือปิดอุปกรณ์กล้อง"</string>
- <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"แอปนี้จะได้รับโค้ดเรียกกลับเมื่อมีการปิดหรือเปิดอุปกรณ์กล้อง (โดยแอปพลิเคชันที่เปิด)"</string>
+ <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"อนุญาตให้แอปพลิเคชันหรือบริการได้รับ Callback เมื่อมีการเปิดหรือปิดอุปกรณ์กล้อง"</string>
+ <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"แอปนี้จะได้รับ Callback เมื่อมีการปิดหรือเปิดอุปกรณ์กล้อง (โดยแอปพลิเคชันที่เปิด)"</string>
<string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"อนุญาตให้แอปพลิเคชันหรือบริการเข้าถึงกล้องในฐานะผู้ใช้ระบบแบบไม่มีส่วนหัว"</string>
<string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"แอปนี้เข้าถึงกล้องในฐานะผู้ใช้ระบบแบบไม่มีส่วนหัว"</string>
<string name="permlab_vibrate" msgid="8596800035791962017">"ควบคุมการสั่นเตือน"</string>
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"ตรวจสอบสิทธิ์ลายนิ้วมือแล้ว"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ตรวจสอบสิทธิ์ใบหน้าแล้ว"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ตรวจสอบสิทธิ์ใบหน้าแล้ว โปรดกดยืนยัน"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"ฮาร์ดแวร์ลายนิ้วมือไม่พร้อมใช้งาน"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"ฮาร์ดแวร์อ่านลายนิ้วมือไม่พร้อมใช้งาน"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"ตั้งค่าลายนิ้วมือไม่ได้"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"การตั้งค่าลายนิ้วมือหมดเวลา โปรดลองอีกครั้ง"</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"ยกเลิกการทำงานของลายนิ้วมือ"</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ผู้ใช้ยกเลิกการทำงานของลายนิ้วมือ"</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"การทำงานของลายนิ้วมือถูกยกเลิก"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"ผู้ใช้ยกเลิกการทำงานของลายนิ้วมือ"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"ลองหลายครั้งเกินไป ใช้การล็อกหน้าจอแทน"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ลองหลายครั้งเกินไป ใช้การล็อกหน้าจอแทน"</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ประมวลผลลายนิ้วมือไม่ได้ โปรดลองอีกครั้ง"</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ไม่มีลายนิ้วมือที่ลงทะเบียน"</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"อุปกรณ์นี้ไม่มีเซ็นเซอร์ลายนิ้วมือ"</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ปิดใช้เซ็นเซอร์ชั่วคราวแล้ว"</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"ใช้เซ็นเซอร์ลายนิ้วมือไม่ได้ โปรดติดต่อผู้ให้บริการซ่อม"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"ไม่มีลายนิ้วมือที่ลงทะเบียน"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"อุปกรณ์นี้ไม่มีเซ็นเซอร์ลายนิ้วมือ"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"เซ็นเซอร์ถูกปิดใช้ชั่วคราว"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"ใช้เซ็นเซอร์ลายนิ้วมือไม่ได้ โปรดติดต่อผู้ให้บริการซ่อม"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"กดปุ่มเปิด/ปิดแล้ว"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"นิ้วมือ <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ใช้ลายนิ้วมือ"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 3cb5babd5fcb..d9443349d691 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Na-authenticate ang fingerprint"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Na-authenticate ang mukha"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Na-authenticate ang mukha, pakipindot ang kumpirmahin"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hindi available ang hardware na ginagamitan ng fingerprint."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Hindi available ang hardware na ginagamitan ng fingerprint"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Hindi ma-set up ang fingerprint"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Nag-time out ang pag-set up ng fingerprint. Subukan ulit."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Nakansela ang operasyong ginagamitan ng fingerprint."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Kinansela ng user ang operasyon sa fingerprint."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Nakansela ang operasyon sa fingerprint"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Kinansela ng user ang operasyon sa fingerprint"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Masyadong maraming pagsubok. Gamitin na lang ang lock ng screen."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Masyadong maraming pagsubok. Gamitin na lang ang lock ng screen."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Hindi maproseso ang fingerprint. Subukan ulit."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Walang naka-enroll na fingerprint."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Walang sensor ng fingerprint ang device na ito."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Pansamantalang na-disable ang sensor."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Hindi magamit ang sensor para sa fingerprint. Bumisita sa provider ng pag-aayos"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Walang naka-enroll na fingerprint"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Walang sensor para sa fingerprint ang device na ito"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Pansamantalang na-disable ang sensor"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Hindi magamit ang sensor para sa fingerprint. Bumisita sa provider ng pag-aayos."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Napindot ang power button"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Daliri <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Gumamit ng fingerprint"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 48beb9a0a8d7..dfae3016beb3 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Parmak izi kimlik doğrulaması yapıldı"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Yüz kimliği doğrulandı"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Yüz kimliği doğrulandı, lütfen onayla\'ya basın"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Parmak izi donanımı kullanılamıyor."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Parmak izi donanımı kullanılamıyor"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Parmak izi ayarlanamıyor"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Parmak izi kurulumu zaman aşımına uğradı. Tekrar deneyin."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Parmak izi işlemi iptal edildi."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Parmak izi işlemi kullanıcı tarafından iptal edildi."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Parmak izi işlemi iptal edildi"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Parmak izi işlemi kullanıcı tarafından iptal edildi"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Çok fazla deneme yapıldı. Bunun yerine ekran kilidini kullanın."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Çok fazla deneme yapıldı. Bunun yerine ekran kilidini kullanın."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Parmak izi işlenemiyor. Tekrar deneyin."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Parmak izi kaydedilmedi."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Bu cihazda parmak izi sensörü yok."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensör geçici olarak devre dışı bırakıldı."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Parmak izi sensörü kullanılamıyor. Bir onarım hizmeti sağlayıcıyı ziyaret edin"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Parmak izi kaydedilmedi"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Bu cihazda parmak izi sensörü yok"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Sensör geçici olarak devre dışı bırakıldı"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Parmak izi sensörü kullanılamıyor. Bir onarım hizmeti sağlayıcıyı ziyaret edin."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Güç düğmesine basıldı"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>. parmak"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Parmak izi kullan"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 08051a76a1a6..c8fa2108e6d3 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -668,18 +668,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Відбиток пальця автентифіковано"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Обличчя автентифіковано"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Обличчя автентифіковано. Натисніть \"Підтвердити\""</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Сканер відбитків пальців недоступний."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Сканер відбитків пальців недоступний"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Не вдалося створити відбиток пальця"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Час очікування для налаштування відбитка пальця минув. Повторіть спробу."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Дію з відбитком пальця скасовано."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Користувач скасував дію з відбитком пальця."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Дію з відбитком пальця скасовано"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Користувач скасував дію з відбитком пальця"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Забагато спроб. Використайте натомість розблокування екрана."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Забагато спроб. Використайте натомість розблокування екрана."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Не вдалось обробити відбиток пальця. Повторіть спробу."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Відбитки пальців не зареєстровано."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"На цьому пристрої немає сканера відбитків пальців."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Датчик тимчасово вимкнено."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Не вдається скористатися сканером відбитків пальців. Зверніться до постачальника послуг із ремонту."</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Відбитки пальців не зареєстровано"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"На цьому пристрої немає сканера відбитків пальців"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Сканер тимчасово вимкнено"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Не вдається скористатися сканером відбитків пальців. Зверніться до постачальника послуг із ремонту."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Натиснуто кнопку живлення"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Відбиток пальця <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Доступ за відбитком пальця"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index ba8dfe376caf..5ad1f1832077 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"فنگر پرنٹ کی تصدیق ہو گئی"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"چہرے کی تصدیق ہو گئی"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"چہرے کی تصدیق ہو گئی، براہ کرم \'تصدیق کریں\' کو دبائيں"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"فنگر پرنٹ ہارڈ ویئر دستیاب نہیں ہے۔"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"فنگر پرنٹ ہارڈ ویئر دستیاب نہیں ہے"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"فنگر پرنٹ کو سیٹ اپ نہیں کیا جا سکا"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"فنگر پرنٹ کے سیٹ اپ کا وقت ختم ہو گیا۔ دوبارہ کوشش کریں۔"</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"فنگر پرنٹ کی کارروائی منسوخ ہوگئی۔"</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"صارف نے فنگر پرنٹ کی کارروائی منسوخ کر دی۔"</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"فنگر پرنٹ کی کارروائی منسوخ ہوگئی"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"صارف نے فنگر پرنٹ کی کارروائی منسوخ کر دی"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"کافی زیادہ کوششیں۔ اس کے بجائے اسکرین لاک کا استعمال کریں۔"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"کافی زیادہ کوششیں۔ اس کے بجائے اسکرین لاک کا استعمال کریں۔"</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"فنگر پرنٹ پروسیس نہیں ہو سکتا۔ دوبارہ کوشش کریں۔"</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"کوئی فنگر پرنٹ مندرج شدہ نہیں ہے۔"</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"اس آلہ میں فنگر پرنٹ سینسر نہیں ہے۔"</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"سینسر عارضی طور غیر فعال ہے۔"</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"فنگر پرنٹ سینسر کا استعمال نہیں کر سکتے۔ ایک مرمت فراہم کنندہ کو ملاحظہ کریں"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"کوئی فنگر پرنٹ مندرج نہیں ہے"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"اس آلہ میں فنگر پرنٹ سینسر نہیں ہے"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"سینسر عارضی طور غیر فعال ہے"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"فنگر پرنٹ سینسر کو استعمال نہیں کیا جا سکتا۔ مرمت فراہم کنندہ کو ملاحظہ کریں۔"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"پاور بٹن دبایا گیا"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"انگلی <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"فنگر پرنٹ استعمال کریں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index b4c6a8d87735..a3ffb9c62ad5 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Barmoq izi tekshirildi"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Yuzingiz aniqlandi"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Yuzingiz aniqlandi, tasdiqlash uchun bosing"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Barmoq izi skaneri ish holatida emas."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Barmoq izi skaneri ish holatida emas"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Barmoq izi sozlanmadi"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Barmoq izini sozlash vaqti tugadi. Qayta urining."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Barmoq izi tekshiruvi bekor qilindi."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Barmoq izi amali foydalanuvchi tomonidan bekor qilindi"</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Barmoq izi amali bekor qilindi"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Barmoq izi amali foydalanuvchi tomonidan bekor qilindi"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Juda koʻp urinildi. Ekran qulfi orqali urining."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Juda koʻp urinildi. Ekran qulfi orqali urining."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Barmoq izi tekshirilmadi. Qayta urining."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Hech qanday barmoq izi qayd qilinmagan."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Bu qurilmada barmoq izi skaneri mavjud emas."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor vaqtincha faol emas."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Barmoq izi skaneridan foydalanish imkonsiz. Xizmat koʻrsatish markaziga murojaat qiling"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Hech qanday barmoq izi qayd qilinmagan"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Bu qurilmada barmoq izi skaneri yo‘q"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Skaner vaqtincha faol emas"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Barmoq izi skaneridan foydalanish imkonsiz. Xizmat koʻrsatish markaziga murojaat qiling."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Quvvat tugmasi bosildi"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Barmoq izi <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Barmoq izi ishlatish"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 61f134800c41..8f52be0cb10d 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Đã xác thực vân tay"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Đã xác thực khuôn mặt"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Đã xác thực khuôn mặt, vui lòng nhấn để xác nhận"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Phần cứng vân tay không khả dụng."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Không có phần cứng xử lý vân tay"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Không thể thiết lập vân tay"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Hết thời gian chờ thiết lập vân tay. Hãy thử lại."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Thao tác dùng dấu vân tay bị hủy."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Người dùng đã hủy thao tác dùng dấu vân tay."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Đã huỷ thao tác dùng vân tay"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Người dùng đã huỷ thao tác sử dụng vân tay"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Bạn đã thử quá nhiều lần. Hãy dùng phương thức khoá màn hình."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Bạn đã thử quá nhiều lần. Hãy dùng phương thức khoá màn hình."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Không xử lý được vân tay. Hãy thử lại."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Chưa đăng ký vân tay."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Thiết bị này không có cảm biến vân tay."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Đã tạm thời tắt cảm biến."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Không thể dùng cảm biến vân tay. Hãy liên hệ với một nhà cung cấp dịch vụ sửa chữa"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Chưa đăng ký vân tay"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Thiết bị này không có cảm biến vân tay"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Đã tắt tạm thời cảm biến"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Không dùng được cảm biến vân tay. Hãy liên hệ với một nhà cung cấp dịch vụ sửa chữa."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Đã nhấn nút nguồn"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Ngón tay <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Dùng vân tay"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 09e80eb8f7c0..28f563d2f170 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"已验证指纹"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"面孔已验证"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"面孔已验证,请按确认按钮"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"指纹硬件无法使用。"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"无法使用指纹硬件"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"无法设置指纹"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"指纹设置已超时,请重试。"</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"指纹操作已取消。"</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"用户取消了指纹操作。"</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"已取消指纹操作"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"用户取消了指纹操作"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"尝试次数过多,请通过屏幕锁定功能解锁。"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"尝试次数过多,请通过屏幕锁定功能解锁。"</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"无法处理指纹,请重试。"</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"未注册任何指纹。"</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"此设备没有指纹传感器。"</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"传感器已暂时停用。"</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"无法使用指纹传感器。请联系维修服务提供商"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"未注册任何指纹"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"此设备没有指纹传感器"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"已暂时停用传感器"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"无法使用指纹传感器。请联系维修服务提供商。"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"已按下电源按钮"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"手指 <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"使用指纹"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 8743f8548a71..59a2abd442eb 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -153,15 +153,15 @@
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> 於 <xliff:g id="TIME_DELAY">{2}</xliff:g> 秒後轉接"</string>
<string name="cfTemplateRegistered" msgid="5619930473441550596">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:尚未轉接"</string>
<string name="cfTemplateRegisteredTime" msgid="5222794399642525045">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:尚未轉接"</string>
- <string name="scCellularNetworkSecurityTitle" msgid="90330018476923559">"行動網路安全性"</string>
- <string name="scCellularNetworkSecuritySummary" msgid="8659128412709908263">"查看設定"</string>
- <string name="scIdentifierDisclosureIssueTitle" msgid="3737384845335568193">"已存取裝置 ID"</string>
- <string name="scIdentifierDisclosureIssueSummary" msgid="3870743771498510600">"<xliff:g id="DISCLOSURE_NETWORK">%4$s</xliff:g> 連線上的網路已記錄裝置的專屬 ID (IMSI),次數為 <xliff:g id="DISCLOSURE_COUNT">%1$d</xliff:g> 次;記錄區間為 <xliff:g id="DISCLOSURE_WINDOW_START_TIME">%2$tr</xliff:g>至 <xliff:g id="DISCLOSURE_WINDOW_END_TIME">%3$tr</xliff:g>。"</string>
- <string name="scNullCipherIssueEncryptedTitle" msgid="8426373579673205292">"已連上加密的 <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
- <string name="scNullCipherIssueEncryptedSummary" msgid="6437468449554283998">"你現在已連上較安全的行動網路。"</string>
- <string name="scNullCipherIssueNonEncryptedTitle" msgid="2069674849204163569">"已連上未加密的 <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
- <string name="scNullCipherIssueNonEncryptedSummary" msgid="3577092996366374833">"你已連上未加密的行動網路。通話、訊息和資料會容易遭到攔截。"</string>
- <string name="scNullCipherIssueActionSettings" msgid="8378372959891478470">"行動網路安全性設定"</string>
+ <string name="scCellularNetworkSecurityTitle" msgid="90330018476923559">"流動網絡安全性"</string>
+ <string name="scCellularNetworkSecuritySummary" msgid="8659128412709908263">"檢查設定"</string>
+ <string name="scIdentifierDisclosureIssueTitle" msgid="3737384845335568193">"已存取裝置識別碼"</string>
+ <string name="scIdentifierDisclosureIssueSummary" msgid="3870743771498510600">"<xliff:g id="DISCLOSURE_NETWORK">%4$s</xliff:g> 連線上的網絡已記錄裝置的專屬識別碼 (IMSI),次數為 <xliff:g id="DISCLOSURE_COUNT">%1$d</xliff:g> 次;記錄區間為 <xliff:g id="DISCLOSURE_WINDOW_START_TIME">%2$tr</xliff:g>至<xliff:g id="DISCLOSURE_WINDOW_END_TIME">%3$tr</xliff:g>。"</string>
+ <string name="scNullCipherIssueEncryptedTitle" msgid="8426373579673205292">"已連線至加密的 <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
+ <string name="scNullCipherIssueEncryptedSummary" msgid="6437468449554283998">"你現已連線至較安全的流動網絡。"</string>
+ <string name="scNullCipherIssueNonEncryptedTitle" msgid="2069674849204163569">"已連線至未加密的 <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
+ <string name="scNullCipherIssueNonEncryptedSummary" msgid="3577092996366374833">"你已連線至未加密的流動網絡。通話、訊息和資料會容易被攔截。"</string>
+ <string name="scNullCipherIssueActionSettings" msgid="8378372959891478470">"流動網絡安全性設定"</string>
<string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"瞭解詳情"</string>
<string name="fcComplete" msgid="1080909484660507044">"功能碼輸入完成。"</string>
<string name="fcError" msgid="5325116502080221346">"連線問題或功能碼無效。"</string>
@@ -553,8 +553,8 @@
<string name="permdesc_transmitIr" product="default" msgid="8484193849295581808">"允許應用程式使用手機的紅外線傳送器。"</string>
<string name="permlab_setWallpaper" msgid="6959514622698794511">"設定桌布"</string>
<string name="permdesc_setWallpaper" msgid="2973996714129021397">"允許應用程式設定系統桌布。"</string>
- <string name="permlab_accessHiddenProfile" msgid="8607094418491556823">"存取隱藏的設定檔"</string>
- <string name="permdesc_accessHiddenProfile" msgid="1543153202481009676">"允許應用程式存取隱藏的設定檔。"</string>
+ <string name="permlab_accessHiddenProfile" msgid="8607094418491556823">"存取已隱藏的設定檔"</string>
+ <string name="permdesc_accessHiddenProfile" msgid="1543153202481009676">"允許應用程式存取已隱藏的設定檔。"</string>
<string name="permlab_setWallpaperHints" msgid="1153485176642032714">"調整桌布大小"</string>
<string name="permdesc_setWallpaperHints" msgid="6257053376990044668">"允許應用程式設定有關系統桌布大小的提示。"</string>
<string name="permlab_setTimeZone" msgid="7922618798611542432">"設定時區"</string>
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"驗證咗指紋"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"面孔已經驗證"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"面孔已經驗證,請㩒一下 [確認]"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"無法使用指紋軟件。"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"無法使用指紋硬件"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"無法設定指紋"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"指紋設定逾時,請再試一次。"</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"指紋操作已取消。"</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"使用者已取消指紋操作。"</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"指紋操作已取消"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"使用者已取消指紋操作"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"嘗試次數過多,請改用螢幕鎖定功能。"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"嘗試次數過多,請改用螢幕鎖定功能。"</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"無法處理指紋,請再試一次。"</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"未註冊任何指紋"</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"此裝置沒有指紋感應器。"</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"感應器已暫時停用。"</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"無法使用指紋感應器。請諮詢維修服務供應商"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"尚未註冊任何指紋"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"此裝置沒有指紋感應器"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"感應器已暫時停用"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"指紋感應器無法使用,請諮詢維修服務供應商。"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"已按下開關按鈕"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"手指 <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"使用指紋鎖定"</string>
@@ -823,8 +823,8 @@
<string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"允許應用程式以大於 200 Hz 的頻率對感應器資料進行取樣"</string>
<string name="permlab_updatePackagesWithoutUserAction" msgid="3363272609642618551">"更新應用程式,無需使用者操作"</string>
<string name="permdesc_updatePackagesWithoutUserAction" msgid="4567739631260526366">"允許擁有者更新先前安裝的應用程式,無需使用者操作"</string>
- <string name="permlab_writeVerificationStateE2eeContactKeys" msgid="3990742344778360457">"更新 E2EE 聯絡人金鑰的驗證狀態,這些金鑰為其他應用程式所有"</string>
- <string name="permdesc_writeVerificationStateE2eeContactKeys" msgid="8453156829747427041">"允許應用程式更新 E2EE 聯絡人金鑰的驗證狀態,這些金鑰為其他應用程式所有"</string>
+ <string name="permlab_writeVerificationStateE2eeContactKeys" msgid="3990742344778360457">"更新其他應用程式擁有的點對點加密聯絡人密鑰的驗證狀態"</string>
+ <string name="permdesc_writeVerificationStateE2eeContactKeys" msgid="8453156829747427041">"允許應用程式更新其他應用程式擁有的點對點加密聯絡人密鑰的驗證狀態"</string>
<string name="policylab_limitPassword" msgid="4851829918814422199">"設定密碼規則"</string>
<string name="policydesc_limitPassword" msgid="4105491021115793793">"控制螢幕鎖定密碼和 PIN 所允許的長度和字元。"</string>
<string name="policylab_watchLogin" msgid="7599669460083719504">"監控螢幕解鎖嘗試次數"</string>
@@ -2386,8 +2386,8 @@
<string name="profile_label_test" msgid="9168641926186071947">"測試"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"共用"</string>
<string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
- <string name="satellite_notification_title" msgid="4026338973463121526">"已自動連上衛星"</string>
- <string name="satellite_notification_summary" msgid="5207364139430767162">"你可以收發訊息,沒有行動/Wi-Fi 網路也無妨"</string>
- <string name="satellite_notification_open_message" msgid="4149234979688273729">"開啟「訊息」應用程式"</string>
+ <string name="satellite_notification_title" msgid="4026338973463121526">"已自動連線至衛星"</string>
+ <string name="satellite_notification_summary" msgid="5207364139430767162">"你可在沒有流動/Wi-Fi 網絡的情況下收發訊息"</string>
+ <string name="satellite_notification_open_message" msgid="4149234979688273729">"開啟「訊息」"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"運作方式"</string>
</resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index ba257da04279..2233668ba404 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"指紋驗證成功"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"臉孔驗證成功"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"臉孔驗證成功,請按下 [確認] 按鈕"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"指紋硬體無法使用。"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"指紋辨識硬體無法使用"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"無法設定指紋"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"指紋設定逾時,請再試一次。"</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"指紋作業已取消。"</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"使用者已取消指紋驗證作業。"</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"指紋作業已取消"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"使用者已取消指紋驗證作業"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"嘗試次數過多,請改用螢幕鎖定功能。"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"嘗試次數過多,請改用螢幕鎖定功能。"</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"無法處理指紋,請再試一次。"</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"未登錄任何指紋。"</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"這個裝置沒有指紋感應器。"</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"感應器已暫時停用。"</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"指紋感應器無法使用,請洽詢維修供應商"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"未登錄任何指紋"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"這個裝置沒有指紋感應器"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"感應器已暫時停用"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"指紋感應器無法使用,請洽詢維修供應商。"</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"已按下電源鍵"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"手指 <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"使用指紋"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 04c60cb5c1b8..176c3743825d 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -666,18 +666,18 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Izigxivizo zeminwe zigunyaziwe"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Ubuso bufakazelwe ubuqiniso"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Ukuqinisekiswa kobuso, sicela ucindezele okuthi qinisekisa"</string>
- <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Izingxenyekazi zekhompuyutha zezigxivizo zeminwe azitholakali."</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Izingxenyekazi zekhompuyutha zezigxivizo zeminwe azitholakali"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Ayikwazi ukusetha izigxivizo zeminwe"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Ukusethwa kwesigxivizo somunwe kuphelelwe yisikhathi Zama futhi."</string>
- <string name="fingerprint_error_canceled" msgid="540026881380070750">"Ukusebenza kwezigxivizo zeminwe kukhanseliwe."</string>
- <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Umsebenzi wezigxivizo zomunwe ukhanselwe umsebenzisi."</string>
+ <string name="fingerprint_error_canceled" msgid="5541771463159727513">"Ukusebenza kwezigxivizo zeminwe kukhanseliwe"</string>
+ <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"Umsebenzi wezigxivizo zeminwe ukhanselwe umsebenzisi"</string>
<string name="fingerprint_error_lockout" msgid="6626753679019351368">"Imizamo eminingi kakhulu. Sebenzisa ukukhiya isikrini kunalokho."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Imizamo eminingi kakhulu. Sebenzisa ukukhiya isikrini kunalokho."</string>
<string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Ayikwazi ukucubungula isigxivizo somunwe. Zama futhi."</string>
- <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Azikho izigxivizo zeminwe ezibhalisiwe."</string>
- <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Le divayisi ayinayo inzwa yezigxivizo zeminwe."</string>
- <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Inzwa ikhutshazwe okwesikhashana."</string>
- <string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Ayikwazi ukusebenzisa inzwa yesigxivizo somunwe. Vakashela umhlinzeki wokulungisa"</string>
+ <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"Azikho izigxivizo zeminwe ezibhalisiwe"</string>
+ <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"Le divayisi ayinayo inzwa yezigxivizo zeminwe"</string>
+ <string name="fingerprint_error_security_update_required" msgid="8440349108169661934">"Inzwa ikhutshazwe okwesikhashana"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="6770614925736183528">"Ayikwazi ukusebenzisa inzwa yesigxivizo somunwe. Vakashela umhlinzeki wokulungisa."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"Inkinobho yamandla icindezelwe"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Umunwe ongu-<xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Sebenzisa izigxivizo zeminwe"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 4ee03deab6c2..c1fd61948e68 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2514,6 +2514,13 @@
<!-- The icon is shown unless the launching app specified SPLASH_SCREEN_STYLE_EMPTY -->
<enum name="icon_preferred" value="1" />
</attr>
+ <!-- Offer Window the ability to opt out the UI Toolkit discrete variable refresh rate.
+ This feature allows device to adjust refresh rate as needed and
+ can be useful for power saving.
+ Set to false to reduce the frame rate optimizations on devices with
+ variable refresh rate screens.
+ The default is true. -->
+ <attr name="windowIsFrameRatePowerSavingsBalanced" format="boolean"/>
<!-- Flag indicating whether this window would opt-out the edge-to-edge enforcement.
@@ -5891,6 +5898,17 @@
use glyph bound's as a source of text width. -->
<!-- @FlaggedApi("com.android.text.flags.use_bounds_for_width") -->
<attr name="useBoundsForWidth" format="boolean" />
+
+
+ <!-- Whether to shift the drawing offset for prevent clipping start drawing offset.
+ This value is ignored when the useBoundsForWidth attribute is false.
+
+ If this value is false, the TextView draws text from the zero X coordinate. This is
+ useful for aligning multiple TextViews vertically.
+ If this value is true, the TextView shift the drawing offset not to clip the
+ stroke in the region where the X coordinate is negative. -->
+ <!-- @FlaggedApi("com.android.text.flags.use_bounds_for_width") -->
+ <attr name="shiftDrawingOffsetForStartOverhang" format="boolean" />
<!-- Whether to use the locale preferred line height for the minimum line height.
This flag is useful for preventing jitter of entering letters into empty EditText.
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 647bccfb0c89..b2e0be7c2201 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1919,6 +1919,12 @@
try to load its code when launching components. The default is true
for normal behavior. -->
<attr name="hasCode" format="boolean" />
+ <!-- Specifies if activities can be launched on top of this application by activities from
+ other applications in the same task. If set to false, activity launches which would
+ replace this application with another when in the user's view will be blocked.
+ The default is true. -->
+ <!-- @FlaggedApi("android.security.asm_restrictions_enabled") -->
+ <attr name="allowCrossUidActivitySwitchFromBelow" format="boolean" />
<attr name="persistent" />
<attr name="persistentWhenFeatureAvailable" />
<attr name="requiredForAllUsers" />
@@ -3289,7 +3295,8 @@
{@link java.lang.SecurityException}.
<p> Note that the enforcement works for content URIs inside
- {@link android.content.Intent#getData} and {@link android.content.Intent#getClipData}.
+ {@link android.content.Intent#getData}, {@link android.content.Intent#EXTRA_STREAM},
+ and {@link android.content.Intent#getClipData}.
@FlaggedApi("android.security.content_uri_permission_apis") -->
<attr name="requireContentUriPermissionFromCaller" format="string">
<!-- Default, no specific permissions are required. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index c6bc589cffcb..1f06b0b7c62b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4670,6 +4670,13 @@
-->
<string name="config_defaultWearableSensingService" translatable="false"></string>
+
+ <!-- The component name for the default system on-device intelligence service, -->
+ <string name="config_defaultOnDeviceIntelligenceService" translatable="false"></string>
+
+ <!-- The component name for the default system on-device trusted inference service. -->
+ <string name="config_defaultOnDeviceTrustedInferenceService" translatable="false"></string>
+
<!-- Component name that accepts ACTION_SEND intents for requesting ambient context consent for
wearable sensing. -->
<string translatable="false" name="config_defaultWearableSensingConsentComponent"></string>
diff --git a/core/res/res/values/config_battery_saver.xml b/core/res/res/values/config_battery_saver.xml
index e1b0ef4bd0a7..551cd0a87867 100644
--- a/core/res/res/values/config_battery_saver.xml
+++ b/core/res/res/values/config_battery_saver.xml
@@ -33,6 +33,9 @@
<!-- Whether or not battery saver should be "sticky" when manually enabled. -->
<bool name="config_batterySaverStickyBehaviourDisabled">false</bool>
+ <!-- Whether to enable "Battery Saver turned off" notification. -->
+ <bool name="config_batterySaverTurnedOffNotificationEnabled">true</bool>
+
<!-- Config flag to track default disable threshold for Dynamic power savings enabled battery saver. -->
<integer name="config_dynamicPowerSavingsDefaultDisableThreshold">80</integer>
diff --git a/core/res/res/values/config_display.xml b/core/res/res/values/config_display.xml
new file mode 100644
index 000000000000..2e66060926fd
--- /dev/null
+++ b/core/res/res/values/config_display.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright (C) 2024 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 used in DisplayManager.
+
+ 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:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Whether even dimmer feature is enabled. -->
+ <bool name="config_evenDimmerEnabled">false</bool>
+
+</resources>
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
index fa15c3fa4cff..972fe7ed91de 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -204,11 +204,4 @@
<dimen name="progress_bar_size_small">16dip</dimen>
<dimen name="progress_bar_size_medium">48dp</dimen>
<dimen name="progress_bar_size_large">76dp</dimen>
-
- <!-- System corner radius baseline sizes. Used by Material styling of rounded corner shapes-->
- <dimen name="system_corner_radius_xsmall">4dp</dimen>
- <dimen name="system_corner_radius_small">8dp</dimen>
- <dimen name="system_corner_radius_medium">16dp</dimen>
- <dimen name="system_corner_radius_large">26dp</dimen>
- <dimen name="system_corner_radius_xlarge">36dp</dimen>
</resources>
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index dcb6bb0cd743..5987f6ea05d4 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -159,6 +159,14 @@
<public name="contentSensitivity" />
<!-- @FlaggedApi("android.view.inputmethod.connectionless_handwriting") -->
<public name="supportsConnectionlessStylusHandwriting" />
+ <!-- @FlaggedApi("android.nfc.Flags.FLAG_OBSERVE_MODE") -->
+ <public name="defaultToObserveMode"/>
+ <!-- @FlaggedApi("android.security.asm_restrictions_enabled") -->
+ <public name="allowCrossUidActivitySwitchFromBelow"/>
+ <!-- @FlaggedApi("com.android.text.flags.use_bounds_for_width") -->
+ <public name="shiftDrawingOffsetForStartOverhang" />
+ <!-- @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") -->
+ <public name="windowIsFrameRatePowerSavingsBalanced"/>
</staging-public-group>
<staging-public-group type="id" first-id="0x01bc0000">
@@ -176,11 +184,11 @@
<staging-public-group type="dimen" first-id="0x01b90000">
<!-- System corner radius baseline sizes. Used by Material styling of rounded corner shapes-->
- <public name="system_corner_radius_xsmall" />
- <public name="system_corner_radius_small" />
- <public name="system_corner_radius_medium" />
- <public name="system_corner_radius_large" />
- <public name="system_corner_radius_xlarge" />
+ <public name="removed_system_corner_radius_xsmall" />
+ <public name="removed_system_corner_radius_small" />
+ <public name="removed_system_corner_radius_medium" />
+ <public name="removed_system_corner_radius_large" />
+ <public name="removed_system_corner_radius_xlarge" />
</staging-public-group>
<staging-public-group type="color" first-id="0x01b80000">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e9996955a9c0..59066eb83f1c 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5448,6 +5448,13 @@
<!-- Title for button to launch the personal safety app to make an emergency call -->
<string name="work_mode_emergency_call_button">Emergency</string>
+ <!-- Title of the alert dialog prompting the user to set up a screen lock [CHAR LIMIT=30] -->
+ <string name="set_up_screen_lock_title">Set a screen lock</string>
+ <!-- Action label for the dialog prompting the user to set up a screen lock [CHAR LIMIT=30] -->
+ <string name="set_up_screen_lock_action_label">Set screen lock</string>
+ <!-- Message shown in the dialog prompting the user to set up a screen lock to access private space [CHAR LIMIT=30] -->
+ <string name="private_space_set_up_screen_lock_message">To use your private space, set a screen lock on this device</string>
+
<!-- Title of the dialog that is shown when the user tries to launch a blocked application [CHAR LIMIT=50] -->
<string name="app_blocked_title">App is not available</string>
<!-- Default message shown in the dialog that is shown when the user tries to launch a blocked application [CHAR LIMIT=NONE] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7c290b1ba3e1..b36b1d63cbf2 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3247,6 +3247,12 @@
<java-symbol type="string" name="work_mode_off_title" />
<java-symbol type="string" name="work_mode_turn_on" />
+ <!-- Alert dialog prompting the user to set up a screen lock -->
+ <java-symbol type="string" name="set_up_screen_lock_title" />
+ <java-symbol type="string" name="set_up_screen_lock_action_label" />
+ <!-- Message for the alert dialog prompting the user to set up a screen lock to access private space -->
+ <java-symbol type="string" name="private_space_set_up_screen_lock_message" />
+
<java-symbol type="string" name="deprecated_target_sdk_message" />
<java-symbol type="string" name="deprecated_target_sdk_app_store" />
@@ -3909,6 +3915,8 @@
<java-symbol type="string" name="config_ambientContextPackageNameExtraKey" />
<java-symbol type="string" name="config_ambientContextEventArrayExtraKey" />
<java-symbol type="string" name="config_defaultWearableSensingService" />
+ <java-symbol type="string" name="config_defaultOnDeviceIntelligenceService" />
+ <java-symbol type="string" name="config_defaultOnDeviceTrustedInferenceService" />
<java-symbol type="string" name="config_retailDemoPackage" />
<java-symbol type="string" name="config_retailDemoPackageSignature" />
@@ -4115,6 +4123,7 @@
<java-symbol type="bool" name="config_batterySaverSupported" />
<java-symbol type="string" name="config_batterySaverDeviceSpecificConfig" />
<java-symbol type="bool" name="config_batterySaverStickyBehaviourDisabled" />
+ <java-symbol type="bool" name="config_batterySaverTurnedOffNotificationEnabled" />
<java-symbol type="integer" name="config_dynamicPowerSavingsDefaultDisableThreshold" />
<java-symbol type="string" name="config_batterySaverScheduleProvider" />
<java-symbol type="string" name="config_powerSaveModeChangedListenerPackage" />
@@ -5355,12 +5364,8 @@
<java-symbol type="string" name="satellite_notification_how_it_works" />
<java-symbol type="drawable" name="ic_satellite_alt_24px" />
- <java-symbol type="bool" name="config_watchlistUseFileHashesCache" />
+ <!-- DisplayManager configs. -->
+ <java-symbol type="bool" name="config_evenDimmerEnabled" />
- <!-- System corner radius baseline sizes. Used by Material styling of rounded corner shapes-->
- <java-symbol type="dimen" name="system_corner_radius_xsmall" />
- <java-symbol type="dimen" name="system_corner_radius_small" />
- <java-symbol type="dimen" name="system_corner_radius_medium" />
- <java-symbol type="dimen" name="system_corner_radius_large" />
- <java-symbol type="dimen" name="system_corner_radius_xlarge" />
+ <java-symbol type="bool" name="config_watchlistUseFileHashesCache" />
</resources>
diff --git a/core/tests/BroadcastRadioTests/Android.bp b/core/tests/BroadcastRadioTests/Android.bp
index 6be553b99c3c..beffb9aac12b 100644
--- a/core/tests/BroadcastRadioTests/Android.bp
+++ b/core/tests/BroadcastRadioTests/Android.bp
@@ -18,6 +18,7 @@ package {
// all of the 'license_kinds' from "frameworks_base_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
+ default_team: "trendy_team_aaos_framework",
default_applicable_licenses: ["frameworks_base_license"],
}
diff --git a/core/tests/BroadcastRadioTests/TEST_MAPPING b/core/tests/BroadcastRadioTests/TEST_MAPPING
index b085a27b25b8..563706374bf2 100644
--- a/core/tests/BroadcastRadioTests/TEST_MAPPING
+++ b/core/tests/BroadcastRadioTests/TEST_MAPPING
@@ -1,5 +1,5 @@
{
- "postsubmit": [
+ "presubmit": [
{
"name": "BroadcastRadioTests"
}
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 861f71992f54..37e6780a8109 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -211,6 +211,7 @@ android_ravenwood_test {
],
srcs: [
"src/android/app/ActivityManagerTest.java",
+ "src/android/content/ContextTest.java",
"src/android/content/pm/PackageManagerTest.java",
"src/android/content/pm/UserInfoTest.java",
"src/android/database/CursorWindowTest.java",
diff --git a/core/tests/coretests/res/values/styles.xml b/core/tests/coretests/res/values/styles.xml
index 32eebb35e0c3..78cd1e1e47e8 100644
--- a/core/tests/coretests/res/values/styles.xml
+++ b/core/tests/coretests/res/values/styles.xml
@@ -55,4 +55,10 @@
<style name="ViewDefaultBackground">
<item name="android:background">#00000000</item>
</style>
+ <style name="IsFrameRatePowerSavingsBalancedDisabled">
+ <item name="android:windowIsFrameRatePowerSavingsBalanced">false</item>
+ </style>
+ <style name="IsFrameRatePowerSavingsBalancedEnabled">
+ <item name="android:windowIsFrameRatePowerSavingsBalanced">true</item>
+ </style>
</resources>
diff --git a/core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java b/core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java
new file mode 100644
index 000000000000..aaa199d4c814
--- /dev/null
+++ b/core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2024 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.accessibilityservice;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.hardware.usb.UsbDevice;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityInteractionClient;
+import android.view.accessibility.Flags;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+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;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Tests for internal details of BrailleDisplayControllerImpl.
+ *
+ * <p>Prefer adding new tests in CTS where possible.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@RequiresFlagsEnabled(Flags.FLAG_BRAILLE_DISPLAY_HID)
+public class BrailleDisplayControllerImplTest {
+ private static final int TEST_SERVICE_CONNECTION_ID = 123;
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ private BrailleDisplayController mBrailleDisplayController;
+
+ @Mock
+ private IAccessibilityServiceConnection mAccessibilityServiceConnection;
+ @Mock
+ private BrailleDisplayController.BrailleDisplayCallback mBrailleDisplayCallback;
+
+ public static class TestAccessibilityService extends AccessibilityService {
+ public void onAccessibilityEvent(AccessibilityEvent event) {
+ }
+
+ public void onInterrupt() {
+ }
+ }
+
+ @Before
+ public void test() {
+ MockitoAnnotations.initMocks(this);
+ AccessibilityService accessibilityService = spy(new TestAccessibilityService());
+ doReturn((Executor) Runnable::run).when(accessibilityService).getMainExecutor();
+ doReturn(TEST_SERVICE_CONNECTION_ID).when(accessibilityService).getConnectionId();
+ AccessibilityInteractionClient.addConnection(TEST_SERVICE_CONNECTION_ID,
+ mAccessibilityServiceConnection, /*initializeCache=*/false);
+ mBrailleDisplayController = accessibilityService.getBrailleDisplayController();
+ }
+
+ // Automated CTS tests only use the BluetoothDevice version of BrailleDisplayController#connect
+ // because fake UsbDevice objects cannot be created in CTS. This internal test can mock the
+ // UsbDevice object and at least validate that the correct system_server AIDL call is made.
+ @Test
+ public void connect_withUsbDevice_callsConnectUsbBrailleDisplay() throws Exception {
+ UsbDevice usbDevice = Mockito.mock(UsbDevice.class);
+
+ mBrailleDisplayController.connect(usbDevice, mBrailleDisplayCallback);
+
+ verify(mAccessibilityServiceConnection).connectUsbBrailleDisplay(eq(usbDevice), any());
+ }
+
+ @Test
+ public void connect_serviceNotConnected_throwsIllegalStateException() {
+ AccessibilityInteractionClient.removeConnection(TEST_SERVICE_CONNECTION_ID);
+ UsbDevice usbDevice = Mockito.mock(UsbDevice.class);
+
+ assertThrows(IllegalStateException.class,
+ () -> mBrailleDisplayController.connect(usbDevice, mBrailleDisplayCallback));
+ }
+}
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 2327b20bada6..a7d083cbc399 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -40,9 +40,9 @@ import android.app.ActivityThread.ActivityClientRecord;
import android.app.Application;
import android.app.IApplicationThread;
import android.app.PictureInPictureParams;
+import android.app.PictureInPictureUiState;
import android.app.ResourcesManager;
import android.app.servertransaction.ActivityConfigurationChangeItem;
-import android.app.servertransaction.ActivityLifecycleItem;
import android.app.servertransaction.ActivityRelaunchItem;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.ClientTransactionItem;
@@ -74,7 +74,6 @@ import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.content.ReferrerIntent;
-import com.android.window.flags.Flags;
import org.junit.After;
import org.junit.Before;
@@ -229,7 +228,7 @@ public class ActivityThreadTest {
try {
// Send process level config change.
ClientTransaction transaction = newTransaction(activityThread);
- addClientTransactionItem(transaction, ConfigurationChangeItem.obtain(
+ transaction.addTransactionItem(ConfigurationChangeItem.obtain(
newConfig, DEVICE_ID_INVALID));
appThread.scheduleTransaction(transaction);
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -246,7 +245,7 @@ public class ActivityThreadTest {
newConfig.seq++;
newConfig.smallestScreenWidthDp++;
transaction = newTransaction(activityThread);
- addClientTransactionItem(transaction, ActivityConfigurationChangeItem.obtain(
+ transaction.addTransactionItem(ActivityConfigurationChangeItem.obtain(
activity.getActivityToken(), newConfig));
appThread.scheduleTransaction(transaction);
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -447,16 +446,16 @@ public class ActivityThreadTest {
activity.mTestLatch = new CountDownLatch(1);
ClientTransaction transaction = newTransaction(activityThread);
- addClientTransactionItem(transaction, ConfigurationChangeItem.obtain(
+ transaction.addTransactionItem(ConfigurationChangeItem.obtain(
processConfigLandscape, DEVICE_ID_INVALID));
appThread.scheduleTransaction(transaction);
transaction = newTransaction(activityThread);
- addClientTransactionItem(transaction, ActivityConfigurationChangeItem.obtain(
+ transaction.addTransactionItem(ActivityConfigurationChangeItem.obtain(
activity.getActivityToken(), activityConfigLandscape));
- addClientTransactionItem(transaction, ConfigurationChangeItem.obtain(
+ transaction.addTransactionItem(ConfigurationChangeItem.obtain(
processConfigPortrait, DEVICE_ID_INVALID));
- addClientTransactionItem(transaction, ActivityConfigurationChangeItem.obtain(
+ transaction.addTransactionItem(ActivityConfigurationChangeItem.obtain(
activity.getActivityToken(), activityConfigPortrait));
appThread.scheduleTransaction(transaction);
@@ -706,6 +705,9 @@ public class ActivityThreadTest {
final TestActivity activity = mActivityTestRule.launchActivity(startIntent);
final ActivityThread activityThread = activity.getActivityThread();
final ActivityClientRecord r = getActivityClientRecord(activity);
+ if (android.app.Flags.enablePipUiStateCallbackOnEntering()) {
+ activity.mPipUiStateLatch = new CountDownLatch(1);
+ }
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
activityThread.handlePictureInPictureRequested(r);
@@ -843,8 +845,8 @@ public class ActivityThreadTest {
false /* shouldSendCompatFakeFocus*/);
final ClientTransaction transaction = newTransaction(activity);
- addClientTransactionItem(transaction, callbackItem);
- addClientTransactionItem(transaction, resumeStateRequest);
+ transaction.addTransactionItem(callbackItem);
+ transaction.addTransactionItem(resumeStateRequest);
return transaction;
}
@@ -856,7 +858,7 @@ public class ActivityThreadTest {
false /* shouldSendCompatFakeFocus */);
final ClientTransaction transaction = newTransaction(activity);
- addClientTransactionItem(transaction, resumeStateRequest);
+ transaction.addTransactionItem(resumeStateRequest);
return transaction;
}
@@ -867,7 +869,7 @@ public class ActivityThreadTest {
activity.getActivityToken(), 0 /* configChanges */);
final ClientTransaction transaction = newTransaction(activity);
- addClientTransactionItem(transaction, stopStateRequest);
+ transaction.addTransactionItem(stopStateRequest);
return transaction;
}
@@ -879,7 +881,7 @@ public class ActivityThreadTest {
activity.getActivityToken(), config);
final ClientTransaction transaction = newTransaction(activity);
- addClientTransactionItem(transaction, item);
+ transaction.addTransactionItem(item);
return transaction;
}
@@ -891,7 +893,7 @@ public class ActivityThreadTest {
resume);
final ClientTransaction transaction = newTransaction(activity);
- addClientTransactionItem(transaction, item);
+ transaction.addTransactionItem(item);
return transaction;
}
@@ -906,17 +908,6 @@ public class ActivityThreadTest {
return ClientTransaction.obtain(activityThread.getApplicationThread());
}
- private static void addClientTransactionItem(@NonNull ClientTransaction transaction,
- @NonNull ClientTransactionItem item) {
- if (Flags.bundleClientTransactionFlag()) {
- transaction.addTransactionItem(item);
- } else if (item.isActivityLifecycleItem()) {
- transaction.setLifecycleStateRequest((ActivityLifecycleItem) item);
- } else {
- transaction.addCallback(item);
- }
- }
-
// Test activity
public static class TestActivity extends Activity {
static final String PIP_REQUESTED_OVERRIDE_ENTER = "pip_requested_override_enter";
@@ -940,6 +931,11 @@ public class ActivityThreadTest {
* latch reaches 0.
*/
volatile CountDownLatch mConfigLatch;
+ /**
+ * A latch used to notify tests that we're about to wait for the
+ * onPictureInPictureUiStateChanged callback.
+ */
+ volatile CountDownLatch mPipUiStateLatch;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -974,6 +970,14 @@ public class ActivityThreadTest {
if (getIntent().getBooleanExtra(PIP_REQUESTED_OVERRIDE_ENTER, false)) {
enterPictureInPictureMode(new PictureInPictureParams.Builder().build());
mPipEntered = true;
+ // Await for onPictureInPictureUiStateChanged callback if applicable
+ if (mPipUiStateLatch != null) {
+ try {
+ mPipUiStateLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new IllegalStateException(e);
+ }
+ }
return true;
} else if (getIntent().getBooleanExtra(PIP_REQUESTED_OVERRIDE_SKIP, false)) {
mPipEnterSkipped = true;
@@ -982,6 +986,13 @@ public class ActivityThreadTest {
return super.onPictureInPictureRequested();
}
+ @Override
+ public void onPictureInPictureUiStateChanged(PictureInPictureUiState pipState) {
+ if (mPipUiStateLatch != null && pipState.isTransitioningToPip()) {
+ mPipUiStateLatch.countDown();
+ }
+ }
+
boolean pipRequested() {
return mPipRequested;
}
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
index 95d50499b92f..213fd7bd494d 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
@@ -16,10 +16,13 @@
package android.app.servertransaction;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG;
+
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.hardware.display.DisplayManager;
@@ -28,12 +31,14 @@ import android.hardware.display.IDisplayManager;
import android.os.Handler;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.view.DisplayInfo;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -49,6 +54,10 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@Presubmit
public class ClientTransactionListenerControllerTest {
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+
@Mock
private IDisplayManager mIDisplayManager;
@Mock
@@ -60,12 +69,12 @@ public class ClientTransactionListenerControllerTest {
@Before
public void setup() {
+ mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
MockitoAnnotations.initMocks(this);
mDisplayManager = new DisplayManagerGlobal(mIDisplayManager);
mHandler = getInstrumentation().getContext().getMainThreadHandler();
- mController = spy(ClientTransactionListenerController.createInstanceForTesting(
- mDisplayManager));
- doReturn(true).when(mController).isBundleClientTransactionFlagEnabled();
+ mController = ClientTransactionListenerController.createInstanceForTesting(mDisplayManager);
}
@Test
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
index d10cf1691408..527241613a7a 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
@@ -16,16 +16,22 @@
package android.app.servertransaction;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+
+import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG;
+
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.app.ClientTransactionHandler;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -43,31 +49,28 @@ import org.junit.runner.RunWith;
@Presubmit
public class ClientTransactionTests {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+
@Test
public void testPreExecute() {
- final ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
- final ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
- final ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class);
- final ClientTransactionHandler clientTransactionHandler =
- mock(ClientTransactionHandler.class);
+ mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
- final ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
- transaction.addCallback(callback1);
- transaction.addCallback(callback2);
- transaction.setLifecycleStateRequest(stateRequest);
+ testPreExecuteInner();
+ }
- transaction.preExecute(clientTransactionHandler);
+ @Test
+ public void testPreExecute_bundleClientTransaction() {
+ mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
- verify(callback1, times(1)).preExecute(clientTransactionHandler);
- verify(callback2, times(1)).preExecute(clientTransactionHandler);
- verify(stateRequest, times(1)).preExecute(clientTransactionHandler);
+ testPreExecuteInner();
}
- @Test
- public void testPreExecuteTransactionItems() {
+ private void testPreExecuteInner() {
final ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
final ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
final ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class);
+ doReturn(true).when(stateRequest).isActivityLifecycleItem();
final ClientTransactionHandler clientTransactionHandler =
mock(ClientTransactionHandler.class);
@@ -78,8 +81,8 @@ public class ClientTransactionTests {
transaction.preExecute(clientTransactionHandler);
- verify(callback1, times(1)).preExecute(clientTransactionHandler);
- verify(callback2, times(1)).preExecute(clientTransactionHandler);
- verify(stateRequest, times(1)).preExecute(clientTransactionHandler);
+ verify(callback1).preExecute(clientTransactionHandler);
+ verify(callback2).preExecute(clientTransactionHandler);
+ verify(stateRequest).preExecute(clientTransactionHandler);
}
}
diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
index c6447be71855..207fe730aac2 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
@@ -39,6 +39,7 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.PersistableBundle;
import android.platform.test.annotations.Presubmit;
+import android.window.ActivityWindowInfo;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -124,6 +125,7 @@ public class ObjectPoolTests {
final int deviceId = 3;
final IBinder taskFragmentToken = new Binder();
final IBinder initialCallerInfoAccessToken = new Binder();
+ final ActivityWindowInfo activityWindowInfo = new ActivityWindowInfo();
testRecycle(() -> new LaunchActivityItemBuilder(
activityToken, intent, activityInfo)
@@ -142,6 +144,7 @@ public class ObjectPoolTests {
.setTaskFragmentToken(taskFragmentToken)
.setDeviceId(deviceId)
.setInitialCallerInfoAccessToken(initialCallerInfoAccessToken)
+ .setActivityWindowInfo(activityWindowInfo)
.build());
}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
index d641659e6a5f..c1b9efda4652 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
@@ -32,6 +32,7 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.PersistableBundle;
import android.util.MergedConfiguration;
+import android.window.ActivityWindowInfo;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.content.ReferrerIntent;
@@ -134,6 +135,8 @@ class TestUtils {
private IBinder mTaskFragmentToken;
@Nullable
private IBinder mInitialCallerInfoAccessToken;
+ @NonNull
+ private ActivityWindowInfo mActivityWindowInfo = new ActivityWindowInfo();
LaunchActivityItemBuilder(@NonNull IBinder activityToken, @NonNull Intent intent,
@NonNull ActivityInfo info) {
@@ -260,6 +263,13 @@ class TestUtils {
}
@NonNull
+ LaunchActivityItemBuilder setActivityWindowInfo(
+ @NonNull ActivityWindowInfo activityWindowInfo) {
+ mActivityWindowInfo.set(activityWindowInfo);
+ return this;
+ }
+
+ @NonNull
LaunchActivityItem build() {
return LaunchActivityItem.obtain(mActivityToken, mIntent, mIdent, mInfo,
mCurConfig, mOverrideConfig, mDeviceId, mReferrer, mVoiceInteractor,
@@ -267,7 +277,7 @@ class TestUtils {
mActivityOptions != null ? mActivityOptions.getSceneTransitionInfo() : null,
mIsForward, mProfilerInfo, mAssistToken, null /* activityClientController */,
mShareableActivityToken, mLaunchedFromBubble, mTaskFragmentToken,
- mInitialCallerInfoAccessToken);
+ mInitialCallerInfoAccessToken, mActivityWindowInfo);
}
}
}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
index 2315a58eb487..adb6f2a23847 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
@@ -25,6 +25,9 @@ import static android.app.servertransaction.ActivityLifecycleItem.ON_START;
import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+
+import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -51,12 +54,14 @@ import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.util.ArrayMap;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
@@ -83,6 +88,9 @@ import java.util.stream.Collectors;
@Presubmit
public class TransactionExecutorTests {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+
@Mock
private ClientTransactionHandler mTransactionHandler;
@Mock
@@ -240,29 +248,19 @@ public class TransactionExecutorTests {
@Test
public void testTransactionResolution() {
- ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
- when(callback1.getPostExecutionState()).thenReturn(UNDEFINED);
- ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
- when(callback2.getPostExecutionState()).thenReturn(UNDEFINED);
+ mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
- ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
- transaction.addCallback(callback1);
- transaction.addCallback(callback2);
- transaction.setLifecycleStateRequest(mActivityLifecycleItem);
+ testTransactionResolutionInner();
+ }
- transaction.preExecute(mTransactionHandler);
- mExecutor.execute(transaction);
+ @Test
+ public void testTransactionResolution_bundleClientTransaction() {
+ mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
- InOrder inOrder = inOrder(mTransactionHandler, callback1, callback2,
- mActivityLifecycleItem);
- inOrder.verify(callback1).execute(eq(mTransactionHandler), any());
- inOrder.verify(callback2).execute(eq(mTransactionHandler), any());
- inOrder.verify(mActivityLifecycleItem).execute(eq(mTransactionHandler), eq(mClientRecord),
- any());
+ testTransactionResolutionInner();
}
- @Test
- public void testExecuteTransactionItems_transactionResolution() {
+ private void testTransactionResolutionInner() {
ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
when(callback1.getPostExecutionState()).thenReturn(UNDEFINED);
ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
@@ -286,38 +284,19 @@ public class TransactionExecutorTests {
@Test
public void testDoNotLaunchDestroyedActivity() {
- final Map<IBinder, DestroyActivityItem> activitiesToBeDestroyed = new ArrayMap<>();
- when(mTransactionHandler.getActivitiesToBeDestroyed()).thenReturn(activitiesToBeDestroyed);
- // Assume launch transaction is still in queue, so there is no client record.
- when(mTransactionHandler.getActivityClient(any())).thenReturn(null);
-
- // An incoming destroy transaction enters binder thread (preExecute).
- final IBinder token = mock(IBinder.class);
- final ClientTransaction destroyTransaction = ClientTransaction.obtain(null /* client */);
- destroyTransaction.setLifecycleStateRequest(
- DestroyActivityItem.obtain(token, false /* finished */, 0 /* configChanges */));
- destroyTransaction.preExecute(mTransactionHandler);
- // The activity should be added to to-be-destroyed container.
- assertEquals(1, activitiesToBeDestroyed.size());
+ mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
- // A previous queued launch transaction runs on main thread (execute).
- final ClientTransaction launchTransaction = ClientTransaction.obtain(null /* client */);
- final LaunchActivityItem launchItem =
- spy(new LaunchActivityItemBuilder(token, new Intent(), new ActivityInfo()).build());
- launchTransaction.addCallback(launchItem);
- mExecutor.execute(launchTransaction);
+ testDoNotLaunchDestroyedActivityInner();
+ }
- // The launch transaction should not be executed because its token is in the
- // to-be-destroyed container.
- verify(launchItem, never()).execute(any(), any());
+ @Test
+ public void testDoNotLaunchDestroyedActivity_bundleClientTransaction() {
+ mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
- // After the destroy transaction has been executed, the token should be removed.
- mExecutor.execute(destroyTransaction);
- assertTrue(activitiesToBeDestroyed.isEmpty());
+ testDoNotLaunchDestroyedActivityInner();
}
- @Test
- public void testExecuteTransactionItems_doNotLaunchDestroyedActivity() {
+ private void testDoNotLaunchDestroyedActivityInner() {
final Map<IBinder, DestroyActivityItem> activitiesToBeDestroyed = new ArrayMap<>();
when(mTransactionHandler.getActivitiesToBeDestroyed()).thenReturn(activitiesToBeDestroyed);
// Assume launch transaction is still in queue, so there is no client record.
@@ -350,26 +329,19 @@ public class TransactionExecutorTests {
@Test
public void testActivityResultRequiredStateResolution() {
- when(mTransactionHandler.getActivity(any())).thenReturn(mock(Activity.class));
-
- PostExecItem postExecItem = new PostExecItem(ON_RESUME);
+ mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
- ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
- transaction.addCallback(postExecItem);
+ testActivityResultRequiredStateResolutionInner();
+ }
- // Verify resolution that should get to onPause
- mClientRecord.setState(ON_RESUME);
- mExecutor.executeCallbacks(transaction);
- verify(mExecutor).cycleToPath(eq(mClientRecord), eq(ON_PAUSE), eq(transaction));
+ @Test
+ public void testActivityResultRequiredStateResolution_bundleClientTransaction() {
+ mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
- // Verify resolution that should get to onStart
- mClientRecord.setState(ON_STOP);
- mExecutor.executeCallbacks(transaction);
- verify(mExecutor).cycleToPath(eq(mClientRecord), eq(ON_START), eq(transaction));
+ testActivityResultRequiredStateResolutionInner();
}
- @Test
- public void testExecuteTransactionItems_activityResultRequiredStateResolution() {
+ private void testActivityResultRequiredStateResolutionInner() {
when(mTransactionHandler.getActivity(any())).thenReturn(mock(Activity.class));
PostExecItem postExecItem = new PostExecItem(ON_RESUME);
@@ -379,12 +351,12 @@ public class TransactionExecutorTests {
// Verify resolution that should get to onPause
mClientRecord.setState(ON_RESUME);
- mExecutor.executeTransactionItems(transaction);
+ mExecutor.execute(transaction);
verify(mExecutor).cycleToPath(eq(mClientRecord), eq(ON_PAUSE), eq(transaction));
// Verify resolution that should get to onStart
mClientRecord.setState(ON_STOP);
- mExecutor.executeTransactionItems(transaction);
+ mExecutor.execute(transaction);
verify(mExecutor).cycleToPath(eq(mClientRecord), eq(ON_START), eq(transaction));
}
@@ -523,18 +495,19 @@ public class TransactionExecutorTests {
@Test(expected = IllegalArgumentException.class)
public void testActivityItemNullRecordThrowsException() {
- final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class);
- when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED);
- final IBinder token = mock(IBinder.class);
- final ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
- transaction.addCallback(activityItem);
- when(mTransactionHandler.getActivityClient(token)).thenReturn(null);
+ mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
- mExecutor.executeCallbacks(transaction);
+ testActivityItemNullRecordThrowsExceptionInner();
}
@Test(expected = IllegalArgumentException.class)
- public void testExecuteTransactionItems_activityItemNullRecordThrowsException() {
+ public void testActivityItemNullRecordThrowsException_bundleClientTransaction() {
+ mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
+ testActivityItemNullRecordThrowsExceptionInner();
+ }
+
+ private void testActivityItemNullRecordThrowsExceptionInner() {
final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class);
when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED);
final IBinder token = mock(IBinder.class);
@@ -542,28 +515,24 @@ public class TransactionExecutorTests {
transaction.addTransactionItem(activityItem);
when(mTransactionHandler.getActivityClient(token)).thenReturn(null);
- mExecutor.executeTransactionItems(transaction);
+ mExecutor.execute(transaction);
}
@Test
public void testActivityItemExecute() {
- final ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
- final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class);
- when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED);
- when(activityItem.getActivityToken()).thenReturn(mActivityToken);
- transaction.addCallback(activityItem);
- transaction.setLifecycleStateRequest(mActivityLifecycleItem);
-
- mExecutor.execute(transaction);
+ mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
- final InOrder inOrder = inOrder(activityItem, mActivityLifecycleItem);
- inOrder.verify(activityItem).execute(eq(mTransactionHandler), eq(mClientRecord), any());
- inOrder.verify(mActivityLifecycleItem).execute(eq(mTransactionHandler), eq(mClientRecord),
- any());
+ testActivityItemExecuteInner();
}
@Test
- public void testExecuteTransactionItems_activityItemExecute() {
+ public void testActivityItemExecute_bundleClientTransaction() {
+ mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
+ testActivityItemExecuteInner();
+ }
+
+ private void testActivityItemExecuteInner() {
final ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class);
when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED);
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index c30d216208f9..03b85dc8b4e2 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -20,6 +20,9 @@ import static android.app.servertransaction.TestUtils.config;
import static android.app.servertransaction.TestUtils.mergedConfig;
import static android.app.servertransaction.TestUtils.referrerIntentList;
import static android.app.servertransaction.TestUtils.resultInfoList;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+
+import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG;
import static org.junit.Assert.assertEquals;
@@ -29,6 +32,7 @@ import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
+import android.graphics.Rect;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
@@ -36,11 +40,14 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.window.ActivityWindowInfo;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -60,6 +67,9 @@ import java.util.ArrayList;
@Presubmit
public class TransactionParcelTests {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+
private Parcel mParcel;
private IBinder mActivityToken;
@@ -172,6 +182,9 @@ public class TransactionParcelTests {
bundle.putParcelable("data", new ParcelableData(1));
final PersistableBundle persistableBundle = new PersistableBundle();
persistableBundle.putInt("k", 4);
+ final ActivityWindowInfo activityWindowInfo = new ActivityWindowInfo();
+ activityWindowInfo.set(true /* isEmbedded */, new Rect(0, 0, 500, 1000),
+ new Rect(0, 0, 500, 500));
final LaunchActivityItem item = new LaunchActivityItemBuilder(
activityToken, intent, activityInfo)
@@ -190,6 +203,7 @@ public class TransactionParcelTests {
.setShareableActivityToken(new Binder())
.setTaskFragmentToken(new Binder())
.setInitialCallerInfoAccessToken(new Binder())
+ .setActivityWindowInfo(activityWindowInfo)
.build();
writeAndPrepareForReading(item);
@@ -275,6 +289,8 @@ public class TransactionParcelTests {
@Test
public void testClientTransaction() {
+ mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
// Write to parcel
NewIntentItem callback1 = NewIntentItem.obtain(mActivityToken, new ArrayList<>(), true);
ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain(
@@ -300,14 +316,16 @@ public class TransactionParcelTests {
@Test
public void testClientTransactionCallbacksOnly() {
+ mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
// Write to parcel
NewIntentItem callback1 = NewIntentItem.obtain(mActivityToken, new ArrayList<>(), true);
ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain(
mActivityToken, config());
ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
- transaction.addCallback(callback1);
- transaction.addCallback(callback2);
+ transaction.addTransactionItem(callback1);
+ transaction.addTransactionItem(callback2);
writeAndPrepareForReading(transaction);
@@ -321,12 +339,14 @@ public class TransactionParcelTests {
@Test
public void testClientTransactionLifecycleOnly() {
+ mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
// Write to parcel
StopActivityItem lifecycleRequest = StopActivityItem.obtain(mActivityToken,
78 /* configChanges */);
ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
- transaction.setLifecycleStateRequest(lifecycleRequest);
+ transaction.addTransactionItem(lifecycleRequest);
writeAndPrepareForReading(transaction);
diff --git a/core/tests/coretests/src/android/content/ContextTest.java b/core/tests/coretests/src/android/content/ContextTest.java
index d4784374745d..a02af788d496 100644
--- a/core/tests/coretests/src/android/content/ContextTest.java
+++ b/core/tests/coretests/src/android/content/ContextTest.java
@@ -26,6 +26,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import android.app.ActivityThread;
@@ -35,7 +36,9 @@ import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.media.ImageReader;
import android.os.UserHandle;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.ravenwood.RavenwoodRule;
import android.view.Display;
import androidx.test.core.app.ApplicationProvider;
@@ -43,6 +46,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -54,7 +58,23 @@ import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class ContextTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder().build();
+
+ @Test
+ public void testInstrumentationContext() {
+ // Confirm that we have a valid Context
+ assertNotNull(InstrumentationRegistry.getInstrumentation().getContext());
+ }
+
+ @Test
+ public void testInstrumentationTargetContext() {
+ // Confirm that we have a valid Context
+ assertNotNull(InstrumentationRegistry.getInstrumentation().getTargetContext());
+ }
+
@Test
+ @DisabledOnRavenwood(blockedBy = Context.class)
public void testDisplayIdForSystemContext() {
final Context systemContext =
ActivityThread.currentActivityThread().getSystemContext();
@@ -63,6 +83,7 @@ public class ContextTest {
}
@Test
+ @DisabledOnRavenwood(blockedBy = Context.class)
public void testDisplayIdForSystemUiContext() {
final Context systemUiContext =
ActivityThread.currentActivityThread().getSystemUiContext();
@@ -71,6 +92,7 @@ public class ContextTest {
}
@Test
+ @DisabledOnRavenwood(blockedBy = Context.class)
public void testDisplayIdForTestContext() {
final Context testContext =
InstrumentationRegistry.getInstrumentation().getTargetContext();
@@ -79,6 +101,7 @@ public class ContextTest {
}
@Test
+ @DisabledOnRavenwood(blockedBy = Context.class)
public void testDisplayIdForDefaultDisplayContext() {
final Context testContext =
InstrumentationRegistry.getInstrumentation().getTargetContext();
@@ -91,6 +114,7 @@ public class ContextTest {
}
@Test(expected = NullPointerException.class)
+ @DisabledOnRavenwood(blockedBy = Context.class)
public void testStartActivityAsUserNullIntentNullUser() {
final Context testContext =
InstrumentationRegistry.getInstrumentation().getTargetContext();
@@ -98,6 +122,7 @@ public class ContextTest {
}
@Test(expected = NullPointerException.class)
+ @DisabledOnRavenwood(blockedBy = Context.class)
public void testStartActivityAsUserNullIntentNonNullUser() {
final Context testContext =
InstrumentationRegistry.getInstrumentation().getTargetContext();
@@ -105,6 +130,7 @@ public class ContextTest {
}
@Test(expected = NullPointerException.class)
+ @DisabledOnRavenwood(blockedBy = Context.class)
public void testStartActivityAsUserNonNullIntentNullUser() {
final Context testContext =
InstrumentationRegistry.getInstrumentation().getTargetContext();
@@ -112,6 +138,7 @@ public class ContextTest {
}
@Test(expected = RuntimeException.class)
+ @DisabledOnRavenwood(blockedBy = Context.class)
public void testStartActivityAsUserNonNullIntentNonNullUser() {
final Context testContext =
InstrumentationRegistry.getInstrumentation().getTargetContext();
@@ -119,6 +146,7 @@ public class ContextTest {
}
@Test
+ @DisabledOnRavenwood(blockedBy = Context.class)
public void testIsUiContext_appContext_returnsFalse() {
final Context appContext = ApplicationProvider.getApplicationContext();
@@ -126,6 +154,7 @@ public class ContextTest {
}
@Test
+ @DisabledOnRavenwood(blockedBy = Context.class)
public void testIsUiContext_systemContext_returnsTrue() {
final Context systemContext =
ActivityThread.currentActivityThread().getSystemContext();
@@ -134,6 +163,7 @@ public class ContextTest {
}
@Test
+ @DisabledOnRavenwood(blockedBy = Context.class)
public void testIsUiContext_systemUiContext_returnsTrue() {
final Context systemUiContext =
ActivityThread.currentActivityThread().getSystemUiContext();
@@ -142,11 +172,13 @@ public class ContextTest {
}
@Test
+ @DisabledOnRavenwood(blockedBy = Context.class)
public void testGetDisplayFromDisplayContextDerivedContextOnPrimaryDisplay() {
verifyGetDisplayFromDisplayContextDerivedContext(false /* onSecondaryDisplay */);
}
@Test
+ @DisabledOnRavenwood(blockedBy = Context.class)
public void testGetDisplayFromDisplayContextDerivedContextOnSecondaryDisplay() {
verifyGetDisplayFromDisplayContextDerivedContext(true /* onSecondaryDisplay */);
}
@@ -179,6 +211,7 @@ public class ContextTest {
}
@Test
+ @DisabledOnRavenwood(blockedBy = Context.class)
public void testIsUiContext_ContextWrapper() {
ContextWrapper wrapper = new ContextWrapper(null /* base */);
@@ -190,6 +223,7 @@ public class ContextTest {
}
@Test
+ @DisabledOnRavenwood(blockedBy = Context.class)
public void testIsUiContext_UiContextDerivedContext() {
final Context uiContext = createUiContext();
Context context = uiContext.createAttributionContext(null /* attributionTag */);
@@ -202,6 +236,7 @@ public class ContextTest {
}
@Test
+ @DisabledOnRavenwood(blockedBy = Context.class)
public void testIsUiContext_UiContextDerivedDisplayContext() {
final Context uiContext = createUiContext();
final Display secondaryDisplay =
@@ -212,6 +247,7 @@ public class ContextTest {
}
@Test
+ @DisabledOnRavenwood(blockedBy = Context.class)
public void testDeviceIdForSystemContext() {
final Context systemContext =
ActivityThread.currentActivityThread().getSystemContext();
@@ -220,6 +256,7 @@ public class ContextTest {
}
@Test
+ @DisabledOnRavenwood(blockedBy = Context.class)
public void testDeviceIdForSystemUiContext() {
final Context systemUiContext =
ActivityThread.currentActivityThread().getSystemUiContext();
@@ -228,6 +265,7 @@ public class ContextTest {
}
@Test
+ @DisabledOnRavenwood(blockedBy = Context.class)
public void testDeviceIdForTestContext() {
final Context testContext =
InstrumentationRegistry.getInstrumentation().getTargetContext();
diff --git a/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java b/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java
index 661b210f3e18..402d08eb4a25 100644
--- a/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java
+++ b/core/tests/coretests/src/android/content/pm/CrossProfileAppsTest.java
@@ -27,6 +27,8 @@ import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyResourcesManager;
@@ -118,12 +120,55 @@ public class CrossProfileAppsTest {
public void initUsers() throws Exception {
when(mUserManager.isManagedProfile(PERSONAL_PROFILE.getIdentifier())).thenReturn(false);
when(mUserManager.isManagedProfile(MANAGED_PROFILE.getIdentifier())).thenReturn(true);
+ when(mUserManager.isProfile(PERSONAL_PROFILE.getIdentifier())).thenReturn(false);
+ when(mUserManager.isProfile(MANAGED_PROFILE.getIdentifier())).thenReturn(true);
mTargetProfiles = new ArrayList<>();
when(mService.getTargetUserProfiles(MY_PACKAGE)).thenReturn(mTargetProfiles);
}
@Test
+ public void isProfile_managedProfile_returnsTrue() {
+ setValidTargetProfile(MANAGED_PROFILE);
+
+ boolean result = mCrossProfileApps.isProfile(MANAGED_PROFILE);
+
+ assertTrue(result);
+ }
+
+ @Test
+ public void isProfile_personalProfile_returnsFalse() {
+ setValidTargetProfile(PERSONAL_PROFILE);
+
+ boolean result = mCrossProfileApps.isProfile(PERSONAL_PROFILE);
+
+ assertFalse(result);
+ }
+
+ @Test
+ public void isManagedProfile_managedProfile_returnsTrue() {
+ setValidTargetProfile(MANAGED_PROFILE);
+
+ boolean result = mCrossProfileApps.isManagedProfile(MANAGED_PROFILE);
+
+ assertTrue(result);
+ }
+
+ @Test
+ public void isManagedProfile_personalProfile_returnsFalse() {
+ setValidTargetProfile(PERSONAL_PROFILE);
+
+ boolean result = mCrossProfileApps.isManagedProfile(PERSONAL_PROFILE);
+
+ assertFalse(result);
+ }
+
+ @Test(expected = SecurityException.class)
+ public void isManagedProfile_notValidTarget_throwsSecurityException() {
+ mCrossProfileApps.isManagedProfile(PERSONAL_PROFILE);
+ }
+
+ @Test
public void getProfileSwitchingLabel_managedProfile() {
setValidTargetProfile(MANAGED_PROFILE);
when(mApplicationInfo.loadSafeLabel(any(), anyFloat(), anyInt())).thenReturn("app");
diff --git a/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java b/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java
index d816d0853ab6..34f5841aef72 100644
--- a/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java
+++ b/core/tests/coretests/src/android/hardware/face/FaceManagerTest.java
@@ -194,13 +194,13 @@ public class FaceManagerTest {
new CancellationSignal(), mEnrollmentCallback, null /* disabledFeatures */);
verify(mService).enroll(eq(USER_ID), any(), any(), any(), anyString(), any(), any(),
- anyBoolean());
+ anyBoolean(), any());
mFaceManager.enroll(USER_ID, new byte[]{},
new CancellationSignal(), mEnrollmentCallback, null /* disabledFeatures */);
verify(mService, atMost(1 /* maxNumberOfInvocations */)).enroll(eq(USER_ID), any(), any(),
- any(), anyString(), any(), any(), anyBoolean());
+ any(), anyString(), any(), any(), anyBoolean(), any());
verify(mEnrollmentCallback).onEnrollmentError(eq(FACE_ERROR_HW_UNAVAILABLE), anyString());
}
@@ -213,7 +213,7 @@ public class FaceManagerTest {
verify(mEnrollmentCallback).onEnrollmentError(eq(FACE_ERROR_UNABLE_TO_PROCESS),
anyString());
verify(mService, never()).enroll(eq(USER_ID), any(), any(),
- any(), anyString(), any(), any(), anyBoolean());
+ any(), anyString(), any(), any(), anyBoolean(), any());
}
@Test
diff --git a/core/tests/coretests/src/android/hardware/fingerprint/FingerprintManagerTest.java b/core/tests/coretests/src/android/hardware/fingerprint/FingerprintManagerTest.java
index 70313b8c9ea7..ce7d6a95c2f4 100644
--- a/core/tests/coretests/src/android/hardware/fingerprint/FingerprintManagerTest.java
+++ b/core/tests/coretests/src/android/hardware/fingerprint/FingerprintManagerTest.java
@@ -162,11 +162,13 @@ public class FingerprintManagerTest {
mCaptor.getValue().onAllAuthenticatorsRegistered(mProps);
mFingerprintManager.enroll(null, new CancellationSignal(), USER_ID,
- mEnrollCallback, FingerprintManager.ENROLL_ENROLL);
+ mEnrollCallback, FingerprintManager.ENROLL_ENROLL,
+ (new FingerprintEnrollOptions.Builder()).build());
verify(mEnrollCallback).onEnrollmentError(eq(FINGERPRINT_ERROR_UNABLE_TO_PROCESS),
anyString());
- verify(mService, never()).enroll(any(), any(), anyInt(), any(), anyString(), anyInt());
+ verify(mService, never()).enroll(any(), any(), anyInt(), any(), anyString(), anyInt(),
+ any());
}
@Test
diff --git a/core/tests/coretests/src/android/os/RemoteCallbackListTest.java b/core/tests/coretests/src/android/os/RemoteCallbackListTest.java
index aa89e04b4b7e..cc342cf0fc80 100644
--- a/core/tests/coretests/src/android/os/RemoteCallbackListTest.java
+++ b/core/tests/coretests/src/android/os/RemoteCallbackListTest.java
@@ -17,6 +17,7 @@
package android.os;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import androidx.test.runner.AndroidJUnit4;
@@ -24,6 +25,8 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
@@ -59,10 +62,17 @@ public class RemoteCallbackListTest {
mList.register(mRed.mInterface);
mList.register(mGreen.mInterface, mCookie);
assertEquals(2, mList.getRegisteredCallbackCount());
- assertEquals(mRed.mInterface, mList.getRegisteredCallbackItem(0));
- assertEquals(null, mList.getRegisteredCallbackCookie(0));
- assertEquals(mGreen.mInterface, mList.getRegisteredCallbackItem(1));
- assertEquals(mCookie, mList.getRegisteredCallbackCookie(1));
+
+ final List<IRemoteCallback> list = new ArrayList<>();
+ for (int i = 0; i < mList.getRegisteredCallbackCount(); i++) {
+ list.add(mList.getRegisteredCallbackItem(i));
+ }
+ final int redIndex = list.indexOf(mRed.mInterface);
+ final int greenIndex = list.indexOf(mGreen.mInterface);
+ assertTrue(redIndex >= 0);
+ assertTrue(greenIndex >= 0);
+ assertEquals(null, mList.getRegisteredCallbackCookie(redIndex));
+ assertEquals(mCookie, mList.getRegisteredCallbackCookie(greenIndex));
mList.unregister(mRed.mInterface);
assertEquals(1, mList.getRegisteredCallbackCount());
diff --git a/core/tests/coretests/src/android/os/WakeLockStatsTest.java b/core/tests/coretests/src/android/os/WakeLockStatsTest.java
index 2675ba07d2f7..f3b18c8bd9bb 100644
--- a/core/tests/coretests/src/android/os/WakeLockStatsTest.java
+++ b/core/tests/coretests/src/android/os/WakeLockStatsTest.java
@@ -29,10 +29,114 @@ import java.util.List;
public class WakeLockStatsTest {
@Test
+ public void isDataValidOfWakeLockData_invalid_returnFalse() {
+ WakeLockStats.WakeLockData wakeLockData =
+ new WakeLockStats.WakeLockData(
+ /* timesAcquired= */ 0, /* totalTimeHeldMs= */ 10, /* timeHeldMs= */ 0);
+ assertThat(wakeLockData.isDataValid()).isFalse();
+
+ wakeLockData =
+ new WakeLockStats.WakeLockData(
+ /* timesAcquired= */ 1, /* totalTimeHeldMs= */ 0, /* timeHeldMs= */ 0);
+ assertThat(wakeLockData.isDataValid()).isFalse();
+
+ wakeLockData =
+ new WakeLockStats.WakeLockData(
+ /* timesAcquired= */ 1, /* totalTimeHeldMs= */ 10, /* timeHeldMs= */ -10);
+ assertThat(wakeLockData.isDataValid()).isFalse();
+
+ wakeLockData =
+ new WakeLockStats.WakeLockData(
+ /* timesAcquired= */ 1, /* totalTimeHeldMs= */ 10, /* timeHeldMs= */ 20);
+ assertThat(wakeLockData.isDataValid()).isFalse();
+ }
+
+ @Test
+ public void isDataValidOfWakeLockData_empty_returnTrue() {
+ final WakeLockStats.WakeLockData wakeLockData = WakeLockStats.WakeLockData.EMPTY;
+ assertThat(wakeLockData.isDataValid()).isTrue();
+ }
+
+ @Test
+ public void isDataValidOfWakeLockData_valid_returnTrue() {
+ WakeLockStats.WakeLockData wakeLockData =
+ new WakeLockStats.WakeLockData(
+ /* timesAcquired= */ 1, /* totalTimeHeldMs= */ 10, /* timeHeldMs= */ 5);
+ assertThat(wakeLockData.isDataValid()).isTrue();
+ }
+
+ @Test
+ public void isDataValidOfWakeLock_zeroTotalHeldMs_returnFalse() {
+ final WakeLockStats.WakeLockData wakeLockData =
+ new WakeLockStats.WakeLockData(
+ /* timesAcquired= */ 0, /* totalTimeHeldMs= */ 0, /* timeHeldMs= */ 0);
+
+ assertThat(WakeLockStats.WakeLock.isDataValid(wakeLockData, wakeLockData)).isFalse();
+ }
+
+ @Test
+ public void isDataValidOfWakeLock_invalidData_returnFalse() {
+ final WakeLockStats.WakeLockData totalWakeLockData =
+ new WakeLockStats.WakeLockData(
+ /* timesAcquired= */ 6, /* totalTimeHeldMs= */ 60, /* timeHeldMs= */ 20);
+ final WakeLockStats.WakeLockData backgroundWakeLockData =
+ new WakeLockStats.WakeLockData(
+ /* timesAcquired= */ 0, /* totalTimeHeldMs= */ 10, /* timeHeldMs= */ 0);
+
+ assertThat(WakeLockStats.WakeLock.isDataValid(totalWakeLockData, backgroundWakeLockData))
+ .isFalse();
+ }
+
+ @Test
+ public void isDataValidOfWakeLock_totalSmallerThanBackground_returnFalse() {
+ final WakeLockStats.WakeLockData totalWakeLockData =
+ new WakeLockStats.WakeLockData(
+ /* timesAcquired= */ 10, /* totalTimeHeldMs= */ 60, /* timeHeldMs= */ 50);
+ final WakeLockStats.WakeLockData backgroundWakeLockData =
+ new WakeLockStats.WakeLockData(
+ /* timesAcquired= */ 6, /* totalTimeHeldMs= */ 100, /* timeHeldMs= */ 30);
+
+ assertThat(WakeLockStats.WakeLock.isDataValid(totalWakeLockData, backgroundWakeLockData))
+ .isFalse();
+ }
+
+ @Test
+ public void isDataValidOfWakeLock_returnTrue() {
+ final WakeLockStats.WakeLockData totalWakeLockData =
+ new WakeLockStats.WakeLockData(
+ /* timesAcquired= */ 10, /* totalTimeHeldMs= */ 100, /* timeHeldMs= */ 50);
+ final WakeLockStats.WakeLockData backgroundWakeLockData =
+ new WakeLockStats.WakeLockData(
+ /* timesAcquired= */ 6, /* totalTimeHeldMs= */ 60, /* timeHeldMs= */ 20);
+
+ assertThat(WakeLockStats.WakeLock.isDataValid(totalWakeLockData, backgroundWakeLockData))
+ .isTrue();
+ }
+
+ @Test
public void parcelablity() {
+ final WakeLockStats.WakeLockData totalWakeLockData1 =
+ new WakeLockStats.WakeLockData(
+ /* timesAcquired= */ 10, /* totalTimeHeldMs= */ 60, /* timeHeldMs= */ 50);
+ final WakeLockStats.WakeLockData backgroundWakeLockData1 =
+ new WakeLockStats.WakeLockData(
+ /* timesAcquired= */ 6, /* totalTimeHeldMs= */ 100, /* timeHeldMs= */ 30);
+ final WakeLockStats.WakeLock wakeLock1 =
+ new WakeLockStats.WakeLock(
+ 1, "foo", /* isAggregated= */ false, totalWakeLockData1,
+ backgroundWakeLockData1);
+ final WakeLockStats.WakeLockData totalWakeLockData2 =
+ new WakeLockStats.WakeLockData(
+ /* timesAcquired= */ 20, /* totalTimeHeldMs= */ 80, /* timeHeldMs= */ 30);
+ final WakeLockStats.WakeLockData backgroundWakeLockData2 =
+ new WakeLockStats.WakeLockData(
+ /* timesAcquired= */ 1, /* totalTimeHeldMs= */ 100, /* timeHeldMs= */ 30);
+ final WakeLockStats.WakeLock wakeLock2 =
+ new WakeLockStats.WakeLock(
+ 2, "bar", /* isAggregated= */ true, totalWakeLockData2,
+ backgroundWakeLockData2);
WakeLockStats wakeLockStats = new WakeLockStats(
- List.of(new WakeLockStats.WakeLock(1, "foo", 200, 3000, 40000),
- new WakeLockStats.WakeLock(2, "bar", 500, 6000, 70000)));
+ List.of(wakeLock1), List.of(wakeLock2));
Parcel parcel = Parcel.obtain();
wakeLockStats.writeToParcel(parcel, 0);
@@ -44,15 +148,28 @@ public class WakeLockStatsTest {
parcel.setDataPosition(0);
WakeLockStats actual = WakeLockStats.CREATOR.createFromParcel(parcel);
- assertThat(actual.getWakeLocks()).hasSize(2);
- WakeLockStats.WakeLock wl1 = actual.getWakeLocks().get(0);
- assertThat(wl1.uid).isEqualTo(1);
- assertThat(wl1.name).isEqualTo("foo");
- assertThat(wl1.timesAcquired).isEqualTo(200);
- assertThat(wl1.totalTimeHeldMs).isEqualTo(3000);
- assertThat(wl1.timeHeldMs).isEqualTo(40000);
-
- WakeLockStats.WakeLock wl2 = actual.getWakeLocks().get(1);
- assertThat(wl2.uid).isEqualTo(2);
- }
-}
+ assertThat(actual.getWakeLocks()).hasSize(1);
+ WakeLockStats.WakeLock actualWakelock = actual.getWakeLocks().get(0);
+ assertThat(actualWakelock.uid).isEqualTo(1);
+ assertThat(actualWakelock.name).isEqualTo("foo");
+ assertThat(actualWakelock.isAggregated).isFalse();
+ assertThat(actualWakelock.totalWakeLockData.timesAcquired).isEqualTo(10);
+ assertThat(actualWakelock.totalWakeLockData.totalTimeHeldMs).isEqualTo(60);
+ assertThat(actualWakelock.totalWakeLockData.timeHeldMs).isEqualTo(50);
+ assertThat(actualWakelock.backgroundWakeLockData.timesAcquired).isEqualTo(6);
+ assertThat(actualWakelock.backgroundWakeLockData.totalTimeHeldMs).isEqualTo(100);
+ assertThat(actualWakelock.backgroundWakeLockData.timeHeldMs).isEqualTo(30);
+
+ assertThat(actual.getAggregatedWakeLocks()).hasSize(1);
+ WakeLockStats.WakeLock actualAggregatedWakelock = actual.getAggregatedWakeLocks().get(0);
+ assertThat(actualAggregatedWakelock.uid).isEqualTo(2);
+ assertThat(actualAggregatedWakelock.name).isEqualTo("bar");
+ assertThat(actualAggregatedWakelock.isAggregated).isTrue();
+ assertThat(actualAggregatedWakelock.totalWakeLockData.timesAcquired).isEqualTo(20);
+ assertThat(actualAggregatedWakelock.totalWakeLockData.totalTimeHeldMs).isEqualTo(80);
+ assertThat(actualAggregatedWakelock.totalWakeLockData.timeHeldMs).isEqualTo(30);
+ assertThat(actualAggregatedWakelock.backgroundWakeLockData.timesAcquired).isEqualTo(1);
+ assertThat(actualAggregatedWakelock.backgroundWakeLockData.totalTimeHeldMs).isEqualTo(100);
+ assertThat(actualAggregatedWakelock.backgroundWakeLockData.timeHeldMs).isEqualTo(30);
+ }
+} \ No newline at end of file
diff --git a/core/tests/coretests/src/android/text/LayoutTest.java b/core/tests/coretests/src/android/text/LayoutTest.java
index 5649e7118fd5..f60eff69690a 100644
--- a/core/tests/coretests/src/android/text/LayoutTest.java
+++ b/core/tests/coretests/src/android/text/LayoutTest.java
@@ -16,6 +16,11 @@
package android.text;
+import static com.android.graphics.hwui.flags.Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -26,11 +31,16 @@ import static org.junit.Assert.fail;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.text.Layout.Alignment;
import android.text.style.StrikethroughSpan;
@@ -38,6 +48,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -49,6 +60,9 @@ import java.util.Locale;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class LayoutTest {
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
private static final int LINE_COUNT = 5;
private static final int LINE_HEIGHT = 12;
private static final int LINE_DESCENT = 4;
@@ -638,22 +652,268 @@ public class LayoutTest {
}
}
- private final class MockCanvas extends Canvas {
+ @Test
+ @RequiresFlagsEnabled(FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+ public void highContrastTextEnabled_testDrawSelectionAndHighlight_drawsHighContrastSelectionAndHighlight() {
+ Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
+ mAlign, mSpacingMult, mSpacingAdd);
+
+ List<Path> highlightPaths = new ArrayList<>();
+ List<Paint> highlightPaints = new ArrayList<>();
+
+ Path selectionPath = new Path();
+ RectF selectionRect = new RectF(0f, 0f, mWidth / 2f, LINE_HEIGHT);
+ selectionPath.addRect(selectionRect, Path.Direction.CW);
+ highlightPaths.add(selectionPath);
+
+ Paint selectionPaint = new Paint();
+ selectionPaint.setColor(Color.CYAN);
+ highlightPaints.add(selectionPaint);
+
+ final int width = 256;
+ final int height = 256;
+ MockCanvas c = new MockCanvas(width, height);
+ c.setHighContrastTextEnabled(true);
+ layout.draw(c, highlightPaths, highlightPaints, selectionPath, selectionPaint,
+ /* cursorOffsetVertical= */ 0);
+ List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands();
+ var textsDrawn = LINE_COUNT;
+ var highlightsDrawn = 2;
+ assertThat(drawCommands.size()).isEqualTo(textsDrawn + highlightsDrawn);
+
+ var highlightsFound = 0;
+ var curLineIndex = 0;
+ for (int i = 0; i < drawCommands.size(); i++) {
+ MockCanvas.DrawCommand drawCommand = drawCommands.get(i);
+
+ if (drawCommand.path != null) {
+ assertThat(drawCommand.path).isEqualTo(selectionPath);
+ assertThat(drawCommand.paint.getColor()).isEqualTo(Color.YELLOW);
+ assertThat(drawCommand.paint.getBlendMode()).isNotNull();
+ highlightsFound++;
+ } else if (drawCommand.text != null) {
+ int start = layout.getLineStart(curLineIndex);
+ int end = layout.getLineEnd(curLineIndex);
+ assertEquals(LAYOUT_TEXT.toString().substring(start, end), drawCommand.text);
+ curLineIndex++;
+
+ assertWithMessage("highlight is drawn on top of text")
+ .that(highlightsFound).isEqualTo(0);
+ }
+ }
+
+ assertThat(highlightsFound).isEqualTo(2);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+ public void highContrastTextEnabled_testDrawHighlight_drawsHighContrastHighlight() {
+ Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
+ mAlign, mSpacingMult, mSpacingAdd);
+
+ List<Path> highlightPaths = new ArrayList<>();
+ List<Paint> highlightPaints = new ArrayList<>();
+
+ Path selectionPath = new Path();
+ RectF selectionRect = new RectF(0f, 0f, mWidth / 2f, LINE_HEIGHT);
+ selectionPath.addRect(selectionRect, Path.Direction.CW);
+ highlightPaths.add(selectionPath);
+
+ Paint selectionPaint = new Paint();
+ selectionPaint.setColor(Color.CYAN);
+ highlightPaints.add(selectionPaint);
+
+ final int width = 256;
+ final int height = 256;
+ MockCanvas c = new MockCanvas(width, height);
+ c.setHighContrastTextEnabled(true);
+ layout.draw(c, highlightPaths, highlightPaints, /* selectionPath= */ null,
+ /* selectionPaint= */ null, /* cursorOffsetVertical= */ 0);
+ List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands();
+ var textsDrawn = LINE_COUNT;
+ var highlightsDrawn = 1;
+ assertThat(drawCommands.size()).isEqualTo(textsDrawn + highlightsDrawn);
+
+ var highlightsFound = 0;
+ var curLineIndex = 0;
+ for (int i = 0; i < drawCommands.size(); i++) {
+ MockCanvas.DrawCommand drawCommand = drawCommands.get(i);
+
+ if (drawCommand.path != null) {
+ assertThat(drawCommand.path).isEqualTo(selectionPath);
+ assertThat(drawCommand.paint.getColor()).isEqualTo(Color.YELLOW);
+ assertThat(drawCommand.paint.getBlendMode()).isNotNull();
+ highlightsFound++;
+ } else if (drawCommand.text != null) {
+ int start = layout.getLineStart(curLineIndex);
+ int end = layout.getLineEnd(curLineIndex);
+ assertEquals(LAYOUT_TEXT.toString().substring(start, end), drawCommand.text);
+ curLineIndex++;
+
+ assertWithMessage("highlight is drawn on top of text")
+ .that(highlightsFound).isEqualTo(0);
+ }
+ }
+
+ assertThat(highlightsFound).isEqualTo(1);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+ public void highContrastTextDisabledByDefault_testDrawHighlight_drawsNormalHighlightBehind() {
+ Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
+ mAlign, mSpacingMult, mSpacingAdd);
+
+ List<Path> highlightPaths = new ArrayList<>();
+ List<Paint> highlightPaints = new ArrayList<>();
+
+ Path selectionPath = new Path();
+ RectF selectionRect = new RectF(0f, 0f, mWidth / 2f, LINE_HEIGHT);
+ selectionPath.addRect(selectionRect, Path.Direction.CW);
+ highlightPaths.add(selectionPath);
+
+ Paint selectionPaint = new Paint();
+ selectionPaint.setColor(Color.CYAN);
+ highlightPaints.add(selectionPaint);
+
+ final int width = 256;
+ final int height = 256;
+ MockCanvas c = new MockCanvas(width, height);
+ layout.draw(c, highlightPaths, highlightPaints, /* selectionPath= */ null,
+ /* selectionPaint= */ null, /* cursorOffsetVertical= */ 0);
+ List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands();
+ var textsDrawn = LINE_COUNT;
+ var highlightsDrawn = 1;
+ assertThat(drawCommands.size()).isEqualTo(textsDrawn + highlightsDrawn);
+
+ var highlightsFound = 0;
+ var curLineIndex = 0;
+ for (int i = 0; i < drawCommands.size(); i++) {
+ MockCanvas.DrawCommand drawCommand = drawCommands.get(i);
+
+ if (drawCommand.path != null) {
+ assertThat(drawCommand.path).isEqualTo(selectionPath);
+ assertThat(drawCommand.paint.getColor()).isEqualTo(Color.CYAN);
+ assertThat(drawCommand.paint.getBlendMode()).isNull();
+ highlightsFound++;
+ } else if (drawCommand.text != null) {
+ int start = layout.getLineStart(curLineIndex);
+ int end = layout.getLineEnd(curLineIndex);
+ assertEquals(LAYOUT_TEXT.toString().substring(start, end), drawCommand.text);
+ curLineIndex++;
+
+ assertWithMessage("highlight is drawn behind text")
+ .that(highlightsFound).isGreaterThan(0);
+ }
+ }
+
+ assertThat(highlightsFound).isEqualTo(1);
+ }
+
+ @Test
+ @RequiresFlagsDisabled(FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+ public void highContrastTextEnabledButFlagOff_testDrawHighlight_drawsNormalHighlightBehind() {
+ Layout layout = new MockLayout(LAYOUT_TEXT, mTextPaint, mWidth,
+ mAlign, mSpacingMult, mSpacingAdd);
+
+ List<Path> highlightPaths = new ArrayList<>();
+ List<Paint> highlightPaints = new ArrayList<>();
+
+ Path selectionPath = new Path();
+ RectF selectionRect = new RectF(0f, 0f, mWidth / 2f, LINE_HEIGHT);
+ selectionPath.addRect(selectionRect, Path.Direction.CW);
+ highlightPaths.add(selectionPath);
+
+ Paint selectionPaint = new Paint();
+ selectionPaint.setColor(Color.CYAN);
+ highlightPaints.add(selectionPaint);
+
+ final int width = 256;
+ final int height = 256;
+ MockCanvas c = new MockCanvas(width, height);
+ c.setHighContrastTextEnabled(true);
+ layout.draw(c, highlightPaths, highlightPaints, /* selectionPath= */ null,
+ /* selectionPaint= */ null, /* cursorOffsetVertical= */ 0);
+ List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands();
+ var textsDrawn = LINE_COUNT;
+ var highlightsDrawn = 1;
+ assertThat(drawCommands.size()).isEqualTo(textsDrawn + highlightsDrawn);
- class DrawCommand {
+ var highlightsFound = 0;
+ var curLineIndex = 0;
+ for (int i = 0; i < drawCommands.size(); i++) {
+ MockCanvas.DrawCommand drawCommand = drawCommands.get(i);
+
+ if (drawCommand.path != null) {
+ assertThat(drawCommand.path).isEqualTo(selectionPath);
+ assertThat(drawCommand.paint.getColor()).isEqualTo(Color.CYAN);
+ assertThat(drawCommand.paint.getBlendMode()).isNull();
+ highlightsFound++;
+ } else if (drawCommand.text != null) {
+ int start = layout.getLineStart(curLineIndex);
+ int end = layout.getLineEnd(curLineIndex);
+ assertEquals(LAYOUT_TEXT.toString().substring(start, end), drawCommand.text);
+ curLineIndex++;
+
+ assertWithMessage("highlight is drawn behind text")
+ .that(highlightsFound).isGreaterThan(0);
+ }
+ }
+
+ assertThat(highlightsFound).isEqualTo(1);
+ }
+
+ @Test
+ public void mockCanvasHighContrastOverridesCorrectly() {
+ var canvas = new MockCanvas(100, 100);
+
+ assertThat(canvas.isHighContrastTextEnabled()).isFalse();
+ canvas.setHighContrastTextEnabled(true);
+ assertThat(canvas.isHighContrastTextEnabled()).isTrue();
+ canvas.setHighContrastTextEnabled(false);
+ assertThat(canvas.isHighContrastTextEnabled()).isFalse();
+ }
+
+ private static final class MockCanvas extends Canvas {
+
+ static class DrawCommand {
public final String text;
public final float x;
public final float y;
+ public final Path path;
+ public final Paint paint;
- DrawCommand(String text, float x, float y) {
+ DrawCommand(String text, float x, float y, Paint paint) {
this.text = text;
this.x = x;
this.y = y;
+ this.paint = paint;
+ path = null;
+ }
+
+ DrawCommand(Path path, Paint paint) {
+ this.path = path;
+ this.paint = paint;
+ y = 0;
+ x = 0;
+ text = null;
}
}
List<DrawCommand> mDrawCommands;
+ private Boolean mIsHighContrastTextOverride = null;
+
+ public void setHighContrastTextEnabled(boolean enabled) {
+ mIsHighContrastTextOverride = enabled;
+ }
+
+ @Override
+ public boolean isHighContrastTextEnabled() {
+ return mIsHighContrastTextOverride == null ? super.isHighContrastTextEnabled()
+ : mIsHighContrastTextOverride;
+ }
+
MockCanvas(int width, int height) {
super();
mDrawCommands = new ArrayList<>();
@@ -666,7 +926,7 @@ public class LayoutTest {
@Override
public void drawText(String text, int start, int end, float x, float y, Paint p) {
- mDrawCommands.add(new DrawCommand(text.substring(start, end), x, y));
+ mDrawCommands.add(new DrawCommand(text.substring(start, end), x, y, p));
}
@Override
@@ -676,7 +936,7 @@ public class LayoutTest {
@Override
public void drawText(char[] text, int index, int count, float x, float y, Paint p) {
- mDrawCommands.add(new DrawCommand(new String(text, index, count), x, y));
+ mDrawCommands.add(new DrawCommand(new String(text, index, count), x, y, p));
}
@Override
@@ -691,6 +951,11 @@ public class LayoutTest {
drawText(text, index, count, x, y, paint);
}
+ @Override
+ public void drawPath(Path path, Paint p) {
+ mDrawCommands.add(new DrawCommand(path, p));
+ }
+
List<DrawCommand> getDrawCommands() {
return mDrawCommands;
}
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 0657e4b9c619..64cbe7f1b079 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -24,6 +24,8 @@ import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH_HINT;
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.Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
+import static android.view.Surface.FRAME_RATE_COMPATIBILITY_GTE;
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;
@@ -660,10 +662,22 @@ public class ViewRootImplTest {
public void votePreferredFrameRate_voteFrameRateCategory_aggregate() {
View view = new View(sContext);
attachViewToWindow(view);
+ ViewRootImpl viewRootImpl = view.getViewRootImpl();
sInstrumentation.runOnMainSync(() -> {
- ViewRootImpl viewRootImpl = view.getViewRootImpl();
assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ });
+
+ // reset the frame rate category counts
+ for (int i = 0; i < 5; i++) {
+ sInstrumentation.runOnMainSync(() -> {
+ view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ view.invalidate();
+ });
+ sInstrumentation.waitForIdleSync();
+ }
+
+ sInstrumentation.runOnMainSync(() -> {
viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_LOW);
assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_LOW);
viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_NORMAL);
@@ -693,15 +707,51 @@ public class ViewRootImplTest {
public void votePreferredFrameRate_voteFrameRate_aggregate() {
View view = new View(sContext);
attachViewToWindow(view);
+ ViewRootImpl viewRootImpl = view.getViewRootImpl();
sInstrumentation.runOnMainSync(() -> {
- ViewRootImpl viewRootImpl = view.getViewRootImpl();
assertEquals(viewRootImpl.getPreferredFrameRate(), 0, 0.1);
- viewRootImpl.votePreferredFrameRate(24);
+ assertEquals(viewRootImpl.getFrameRateCompatibility(),
+ FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
+ assertEquals(viewRootImpl.isFrameRateConflicted(), false);
+ viewRootImpl.votePreferredFrameRate(24, FRAME_RATE_COMPATIBILITY_GTE);
assertEquals(viewRootImpl.getPreferredFrameRate(), 24, 0.1);
- viewRootImpl.votePreferredFrameRate(30);
+ assertEquals(viewRootImpl.getFrameRateCompatibility(),
+ FRAME_RATE_COMPATIBILITY_GTE);
+ assertEquals(viewRootImpl.isFrameRateConflicted(), false);
+ viewRootImpl.votePreferredFrameRate(30, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
+ assertEquals(viewRootImpl.getPreferredFrameRate(), 30, 0.1);
+ // If there is a conflict, then set compatibility to
+ // FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
+ assertEquals(viewRootImpl.getFrameRateCompatibility(),
+ FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
+ // Should be true since there is a conflict between 24 and 30.
+ assertEquals(viewRootImpl.isFrameRateConflicted(), true);
+ view.invalidate();
+ });
+ sInstrumentation.waitForIdleSync();
+
+ sInstrumentation.runOnMainSync(() -> {
+ assertEquals(viewRootImpl.isFrameRateConflicted(), false);
+ viewRootImpl.votePreferredFrameRate(60, FRAME_RATE_COMPATIBILITY_GTE);
assertEquals(viewRootImpl.getPreferredFrameRate(), 60, 0.1);
- viewRootImpl.votePreferredFrameRate(120);
+ assertEquals(viewRootImpl.getFrameRateCompatibility(),
+ FRAME_RATE_COMPATIBILITY_GTE);
+ assertEquals(viewRootImpl.isFrameRateConflicted(), false);
+ viewRootImpl.votePreferredFrameRate(120, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
assertEquals(viewRootImpl.getPreferredFrameRate(), 120, 0.1);
+ assertEquals(viewRootImpl.getFrameRateCompatibility(),
+ FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
+ // Should be false since 60 is a divisor of 120.
+ assertEquals(viewRootImpl.isFrameRateConflicted(), false);
+ viewRootImpl.votePreferredFrameRate(60, FRAME_RATE_COMPATIBILITY_GTE);
+ assertEquals(viewRootImpl.getPreferredFrameRate(), 120, 0.1);
+ // compatibility should be remained the same (FRAME_RATE_COMPATIBILITY_FIXED_SOURCE)
+ // since the frame rate 60 is smaller than 120.
+ assertEquals(viewRootImpl.getFrameRateCompatibility(),
+ FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
+ // Should be false since 60 is a divisor of 120.
+ assertEquals(viewRootImpl.isFrameRateConflicted(), false);
+
});
}
@@ -721,6 +771,18 @@ public class ViewRootImplTest {
sInstrumentation.runOnMainSync(() -> {
assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ });
+
+ // reset the frame rate category counts
+ for (int i = 0; i < 5; i++) {
+ sInstrumentation.runOnMainSync(() -> {
+ view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ view.invalidate();
+ });
+ sInstrumentation.waitForIdleSync();
+ }
+
+ sInstrumentation.runOnMainSync(() -> {
view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_LOW);
view.invalidate();
assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_LOW);
@@ -816,14 +878,26 @@ public class ViewRootImplTest {
sInstrumentation.runOnMainSync(() -> {
assertEquals(viewRootImpl.getPreferredFrameRate(), 0, 0.1);
- viewRootImpl.votePreferredFrameRate(24);
+ assertEquals(viewRootImpl.getFrameRateCompatibility(),
+ FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
+ assertEquals(viewRootImpl.isFrameRateConflicted(), false);
+ viewRootImpl.votePreferredFrameRate(24, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
assertEquals(viewRootImpl.getPreferredFrameRate(), 24, 0.1);
+ assertEquals(viewRootImpl.getFrameRateCompatibility(),
+ FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
+ assertEquals(viewRootImpl.isFrameRateConflicted(), false);
view.invalidate();
assertEquals(viewRootImpl.getPreferredFrameRate(), 24, 0.1);
+ assertEquals(viewRootImpl.getFrameRateCompatibility(),
+ FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
+ assertEquals(viewRootImpl.isFrameRateConflicted(), false);
});
Thread.sleep(delay);
assertEquals(viewRootImpl.getPreferredFrameRate(), 0, 0.1);
+ assertEquals(viewRootImpl.getFrameRateCompatibility(),
+ FRAME_RATE_COMPATIBILITY_FIXED_SOURCE);
+ assertEquals(viewRootImpl.isFrameRateConflicted(), false);
}
/**
@@ -847,7 +921,18 @@ public class ViewRootImplTest {
assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
FRAME_RATE_CATEGORY_NO_PREFERENCE);
assertEquals(viewRootImpl.getPreferredFrameRate(), frameRate, 0.1);
+ });
+ // reset the frame rate category counts
+ for (int i = 0; i < 5; i++) {
+ sInstrumentation.runOnMainSync(() -> {
+ view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ view.invalidate();
+ });
+ sInstrumentation.waitForIdleSync();
+ }
+
+ sInstrumentation.runOnMainSync(() -> {
view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_LOW);
view.invalidate();
assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_LOW);
@@ -882,33 +967,56 @@ public class ViewRootImplTest {
ViewRootImpl viewRootImpl = view.getViewRootImpl();
- // Frequent update
+ // In transistion from frequent update to infrequent update
+ Thread.sleep(delay);
sInstrumentation.runOnMainSync(() -> {
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
- FRAME_RATE_CATEGORY_NO_PREFERENCE);
- view.invalidate();
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
- view.invalidate();
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
view.invalidate();
assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
});
+ // reset the frame rate category counts
+ for (int i = 0; i < 5; i++) {
+ sInstrumentation.runOnMainSync(() -> {
+ view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ view.invalidate();
+ });
+ sInstrumentation.waitForIdleSync();
+ }
+
// In transistion from frequent update to infrequent update
Thread.sleep(delay);
sInstrumentation.runOnMainSync(() -> {
+ view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
view.invalidate();
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
+ FRAME_RATE_CATEGORY_NO_PREFERENCE);
});
// Infrequent update
Thread.sleep(delay);
sInstrumentation.runOnMainSync(() -> {
+ view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_DEFAULT);
view.invalidate();
assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NORMAL);
});
}
+ /**
+ * Test the IsFrameRatePowerSavingsBalanced values are properly set
+ */
+ @UiThreadTest
+ @Test
+ @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+ public void votePreferredFrameRate_isFrameRatePowerSavingsBalanced() {
+ ViewRootImpl viewRootImpl = new ViewRootImpl(sContext,
+ sContext.getDisplayNoVerify());
+ assertEquals(viewRootImpl.isFrameRatePowerSavingsBalanced(), true);
+ viewRootImpl.setFrameRatePowerSavingsBalanced(false);
+ assertEquals(viewRootImpl.isFrameRatePowerSavingsBalanced(), false);
+ viewRootImpl.setFrameRatePowerSavingsBalanced(true);
+ assertEquals(viewRootImpl.isFrameRatePowerSavingsBalanced(), true);
+ }
+
@Test
public void forceInvertOffDarkThemeOff_forceDarkModeDisabled() {
mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
index a567b4bd5a48..20a876809ba0 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationConstantsTest.java
@@ -28,6 +28,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.function.Consumer;
+import java.util.function.Predicate;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -74,6 +75,13 @@ public class TextClassificationConstantsTest {
.isEqualTo(1));
}
+ @Test
+ public void runtimeMutableSettings() {
+ assertOverride(
+ TextClassificationConstants.SYSTEM_TEXT_CLASSIFIER_ENABLED,
+ settings -> settings.isSystemTextClassifierEnabled());
+ }
+
private static void assertSettings(
String key, String value, Consumer<TextClassificationConstants> settingsConsumer) {
final String originalValue =
@@ -87,6 +95,21 @@ public class TextClassificationConstantsTest {
}
}
+ private static void assertOverride(
+ String key, Predicate<TextClassificationConstants> settingsPredicate) {
+ final String originalValue =
+ DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key);
+ TextClassificationConstants settings = new TextClassificationConstants();
+ try {
+ setDeviceConfig(key, "true");
+ assertThat(settingsPredicate.test(settings)).isTrue();
+ setDeviceConfig(key, "false");
+ assertThat(settingsPredicate.test(settings)).isFalse();
+ } finally {
+ setDeviceConfig(key, originalValue);
+ }
+ }
+
private static void setDeviceConfig(String key, String value) {
DeviceConfig.setProperty(
DeviceConfig.NAMESPACE_TEXTCLASSIFIER, key, value, /* makeDefault */ false);
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
index 3d5494de05ad..15c90474c017 100644
--- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java
+++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
@@ -16,8 +16,6 @@
package android.widget;
-import static android.appwidget.flags.Flags.drawDataParcel;
-
import static com.android.internal.R.id.pending_intent_tag;
import static org.junit.Assert.assertArrayEquals;
@@ -65,7 +63,6 @@ import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
@@ -417,25 +414,6 @@ public class RemoteViewsTest {
assertNotNull(view.findViewById(R.id.light_background_text));
}
- @Test
- public void remoteCanvasCanAccessDrawInstructions() {
- if (!drawDataParcel()) {
- return;
- }
- final byte[] bytes = new byte[] {'h', 'e', 'l', 'l', 'o'};
- final RemoteViews.DrawInstructions drawInstructions =
- new RemoteViews.DrawInstructions.Builder(Collections.singletonList(bytes)).build();
- final RemoteViews rv = new RemoteViews(drawInstructions);
- final PendingIntent pi = PendingIntent.getActivity(mContext, 0,
- new Intent(Intent.ACTION_VIEW), PendingIntent.FLAG_IMMUTABLE);
- final Intent i = new Intent().putExtra("TEST", "Success");
- final int viewId = 1;
- rv.setPendingIntentTemplate(viewId, pi);
- rv.setOnClickFillInIntent(viewId, i);
- final View view = rv.apply(mContext, mContainer);
- assertEquals(drawInstructions, view.getTag());
- }
-
private RemoteViews createViewChained(int depth, String... texts) {
RemoteViews result = new RemoteViews(mPackage, R.layout.remote_view_host);
diff --git a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java
index 3df3b9d2c555..b9841ff997e7 100644
--- a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java
+++ b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowTest.java
@@ -21,17 +21,28 @@ import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_M
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY;
import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import android.app.Instrumentation;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.os.Binder;
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.view.ActionMode;
import android.view.ContextThemeWrapper;
+import android.view.ViewRootImpl;
+import android.view.WindowManager;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -40,6 +51,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.frameworks.coretests.R;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -54,6 +66,12 @@ public final class PhoneWindowTest {
private PhoneWindow mPhoneWindow;
private Context mContext;
+ private static Instrumentation sInstrumentation =
+ InstrumentationRegistry.getInstrumentation();
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
@Before
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getContext();
@@ -154,6 +172,48 @@ public final class PhoneWindowTest {
assertThat(colorDrawable.getColor(), is(Color.BLUE));
}
+ @Test
+ @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+ public void testWindowFrameRateHint_disabled() {
+ createPhoneWindowWithTheme(R.style.IsFrameRatePowerSavingsBalancedDisabled);
+ installDecor();
+
+ DecorView decorView = (DecorView) mPhoneWindow.getDecorView();
+
+ 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 = mContext.getSystemService(WindowManager.class);
+ wm.addView(decorView, wmlp);
+ });
+ sInstrumentation.waitForIdleSync();
+
+ ViewRootImpl viewRootImpl = decorView.getViewRootImpl();
+ assertFalse(viewRootImpl.isFrameRatePowerSavingsBalanced());
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+ public void testWindowFrameRateHint_enabled() {
+ createPhoneWindowWithTheme(R.style.IsFrameRatePowerSavingsBalancedEnabled);
+ installDecor();
+
+ DecorView decorView = (DecorView) mPhoneWindow.getDecorView();
+
+ 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 = mContext.getSystemService(WindowManager.class);
+ wm.addView(decorView, wmlp);
+ });
+ sInstrumentation.waitForIdleSync();
+
+ ViewRootImpl viewRootImpl = decorView.getViewRootImpl();
+ assertTrue(viewRootImpl.isFrameRatePowerSavingsBalanced());
+ }
+
private void createPhoneWindowWithTheme(int theme) {
mPhoneWindow = new PhoneWindow(new ContextThemeWrapper(mContext, theme));
}
diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateTest.java
index 396d403a1ada..c2877217a529 100644
--- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateTest.java
+++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateTest.java
@@ -16,8 +16,8 @@
package android.hardware.devicestate;
-import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
-import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
+import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE_IDENTIFIER;
+import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE_IDENTIFIER;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
@@ -39,35 +39,33 @@ import org.junit.runners.JUnit4;
public final class DeviceStateTest {
@Test
public void testConstruct() {
- final DeviceState state = new DeviceState(MINIMUM_DEVICE_STATE /* identifier */,
+ final DeviceState state = new DeviceState(MINIMUM_DEVICE_STATE_IDENTIFIER /* identifier */,
"TEST_CLOSED" /* name */, DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS /* flags */);
- assertEquals(state.getIdentifier(), MINIMUM_DEVICE_STATE);
+ assertEquals(state.getIdentifier(), MINIMUM_DEVICE_STATE_IDENTIFIER);
assertEquals(state.getName(), "TEST_CLOSED");
assertEquals(state.getFlags(), DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS);
}
@Test
public void testConstruct_nullName() {
- final DeviceState state = new DeviceState(MAXIMUM_DEVICE_STATE /* identifier */,
+ final DeviceState state = new DeviceState(MAXIMUM_DEVICE_STATE_IDENTIFIER /* identifier */,
null /* name */, 0/* flags */);
- assertEquals(state.getIdentifier(), MAXIMUM_DEVICE_STATE);
+ assertEquals(state.getIdentifier(), MAXIMUM_DEVICE_STATE_IDENTIFIER);
assertNull(state.getName());
assertEquals(state.getFlags(), 0);
}
@Test
public void testConstruct_tooLargeIdentifier() {
- assertThrows(IllegalArgumentException.class, () -> {
- final DeviceState state = new DeviceState(MAXIMUM_DEVICE_STATE + 1 /* identifier */,
- null /* name */, 0 /* flags */);
- });
+ assertThrows(IllegalArgumentException.class,
+ () -> new DeviceState(MAXIMUM_DEVICE_STATE_IDENTIFIER + 1 /* identifier */,
+ null /* name */, 0 /* flags */));
}
@Test
public void testConstruct_tooSmallIdentifier() {
- assertThrows(IllegalArgumentException.class, () -> {
- final DeviceState state = new DeviceState(MINIMUM_DEVICE_STATE - 1 /* identifier */,
- null /* name */, 0 /* flags */);
- });
+ assertThrows(IllegalArgumentException.class,
+ () -> new DeviceState(MINIMUM_DEVICE_STATE_IDENTIFIER - 1 /* identifier */,
+ null /* name */, 0 /* flags */));
}
}
diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
index 39cb616b1ed9..66be05ff233c 100644
--- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
+++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
@@ -61,6 +61,7 @@ import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.testing.PollingCheck;
import android.view.WindowManagerGlobal;
+import android.window.ActivityWindowInfo;
import android.window.SizeConfigurationBuckets;
import androidx.test.annotation.UiThreadTest;
@@ -354,7 +355,7 @@ public class ActivityThreadClientTest {
null /* activityOptions */, true /* isForward */, null /* profilerInfo */,
mThread /* client */, null /* asssitToken */, null /* shareableActivityToken */,
false /* launchedFromBubble */, null /* taskfragmentToken */,
- null /* initialCallerInfoAccessToken */);
+ null /* initialCallerInfoAccessToken */, new ActivityWindowInfo());
}
@Override
diff --git a/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java b/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java
index 51013e4b4f00..8093af9bb004 100644
--- a/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java
+++ b/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java
@@ -123,7 +123,7 @@ public class MemoryIntArrayTest {
parcel.recycle();
assertNotNull("Should marshall file descriptor", secondArray);
-
+ assertEquals("Marshalled size must be three", 3, secondArray.size());
assertEquals("First element should be 1", 1, secondArray.get(0));
assertEquals("First element should be 2", 2, secondArray.get(1));
assertEquals("First element should be 3", 3, secondArray.get(2));
diff --git a/core/tests/utiltests/src/android/util/RemoteMemoryIntArrayService.java b/core/tests/utiltests/src/android/util/RemoteMemoryIntArrayService.java
index 9264c6c86c3f..32dda6be1e2b 100644
--- a/core/tests/utiltests/src/android/util/RemoteMemoryIntArrayService.java
+++ b/core/tests/utiltests/src/android/util/RemoteMemoryIntArrayService.java
@@ -84,11 +84,7 @@ public class RemoteMemoryIntArrayService extends Service {
@Override
public int size() {
synchronized (mLock) {
- try {
- return mArray.size();
- } catch (IOException e) {
- throw new IllegalStateException(e);
- }
+ return mArray.size();
}
}
diff --git a/core/tests/vibrator/Android.bp b/core/tests/vibrator/Android.bp
index 09608e9bf507..3ebe150acd24 100644
--- a/core/tests/vibrator/Android.bp
+++ b/core/tests/vibrator/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_haptics_framework",
// See: http://go/android-license-faq
default_applicable_licenses: ["frameworks_base_license"],
}
diff --git a/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java
index 4f5f3c0ddeaf..7dd9e55f8f3e 100644
--- a/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java
@@ -106,6 +106,13 @@ public class PrebakedSegmentTest {
}
@Test
+ public void testScaleLinearly_ignoresAndReturnsSameEffect() {
+ PrebakedSegment prebaked = new PrebakedSegment(
+ VibrationEffect.EFFECT_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM);
+ assertSame(prebaked, prebaked.scaleLinearly(0.5f));
+ }
+
+ @Test
public void testDuration() {
assertEquals(-1, new PrebakedSegment(
VibrationEffect.EFFECT_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM)
diff --git a/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java
index ec5a084c2ddb..e9a08aef4856 100644
--- a/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java
@@ -129,6 +129,27 @@ public class PrimitiveSegmentTest {
}
@Test
+ public void testScaleLinearly() {
+ PrimitiveSegment initial = new PrimitiveSegment(
+ VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0);
+
+ assertEquals(1f, initial.scaleLinearly(1).getScale(), TOLERANCE);
+ assertEquals(0.5f, initial.scaleLinearly(0.5f).getScale(), TOLERANCE);
+ assertEquals(1f, initial.scaleLinearly(1.5f).getScale(), TOLERANCE);
+ assertEquals(0.8f, initial.scaleLinearly(0.8f).getScale(), TOLERANCE);
+ // Restores back to the exact original value since this is a linear scaling.
+ assertEquals(1f, initial.scaleLinearly(0.8f).scaleLinearly(1.25f).getScale(), TOLERANCE);
+
+ initial = new PrimitiveSegment(VibrationEffect.Composition.PRIMITIVE_CLICK, 0, 0);
+
+ assertEquals(0f, initial.scaleLinearly(1).getScale(), TOLERANCE);
+ assertEquals(0f, initial.scaleLinearly(0.5f).getScale(), TOLERANCE);
+ assertEquals(0f, initial.scaleLinearly(1.5f).getScale(), TOLERANCE);
+ assertEquals(0f, initial.scaleLinearly(1.5f).scaleLinearly(2 / 3f).getScale(), TOLERANCE);
+ assertEquals(0f, initial.scaleLinearly(0.8f).scaleLinearly(1.25f).getScale(), TOLERANCE);
+ }
+
+ @Test
public void testDuration() {
assertEquals(-1, new PrimitiveSegment(
VibrationEffect.Composition.PRIMITIVE_NOOP, 1, 10).getDuration());
diff --git a/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java
index 5caa86bb9fb5..01013ab69f85 100644
--- a/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java
@@ -131,6 +131,37 @@ public class RampSegmentTest {
}
@Test
+ public void testScaleLinearly() {
+ RampSegment initial = new RampSegment(0, 1, 0, 0, 0);
+
+ assertEquals(0f, initial.scaleLinearly(1f).getStartAmplitude(), TOLERANCE);
+ assertEquals(0f, initial.scaleLinearly(0.5f).getStartAmplitude(), TOLERANCE);
+ assertEquals(0f, initial.scaleLinearly(1.5f).getStartAmplitude(), TOLERANCE);
+ assertEquals(0f, initial.scaleLinearly(1.5f).scaleLinearly(2 / 3f).getStartAmplitude(),
+ TOLERANCE);
+ assertEquals(0f, initial.scaleLinearly(0.8f).scaleLinearly(1.25f).getStartAmplitude(),
+ TOLERANCE);
+
+ assertEquals(1f, initial.scaleLinearly(1f).getEndAmplitude(), TOLERANCE);
+ assertEquals(0.5f, initial.scaleLinearly(0.5f).getEndAmplitude(), TOLERANCE);
+ assertEquals(1f, initial.scaleLinearly(1.5f).getEndAmplitude(), TOLERANCE);
+ assertEquals(0.8f, initial.scaleLinearly(0.8f).getEndAmplitude(), TOLERANCE);
+ // Restores back to the exact original value since this is a linear scaling.
+ assertEquals(0.8f, initial.scaleLinearly(1.5f).scaleLinearly(0.8f).getEndAmplitude(),
+ TOLERANCE);
+
+ initial = new RampSegment(0.5f, 1, 0, 0, 0);
+
+ assertEquals(0.5f, initial.scaleLinearly(1).getStartAmplitude(), TOLERANCE);
+ assertEquals(0.25f, initial.scaleLinearly(0.5f).getStartAmplitude(), TOLERANCE);
+ assertEquals(0.75f, initial.scaleLinearly(1.5f).getStartAmplitude(), TOLERANCE);
+ assertEquals(0.4f, initial.scaleLinearly(0.8f).getStartAmplitude(), TOLERANCE);
+ // Restores back to the exact original value since this is a linear scaling.
+ assertEquals(0.5f, initial.scaleLinearly(0.8f).scaleLinearly(1.25f).getStartAmplitude(),
+ TOLERANCE);
+ }
+
+ @Test
public void testDuration() {
assertEquals(10, new RampSegment(0.5f, 1, 0, 0, 10).getDuration());
}
diff --git a/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java
index 44db30603089..40776abc0291 100644
--- a/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java
@@ -141,6 +141,37 @@ public class StepSegmentTest {
}
@Test
+ public void testScaleLinearly_fullAmplitude() {
+ StepSegment initial = new StepSegment(1f, 0, 0);
+
+ assertEquals(1f, initial.scaleLinearly(1).getAmplitude(), TOLERANCE);
+ assertEquals(0.5f, initial.scaleLinearly(0.5f).getAmplitude(), TOLERANCE);
+ assertEquals(1f, initial.scaleLinearly(1.5f).getAmplitude(), TOLERANCE);
+ assertEquals(0.8f, initial.scaleLinearly(0.8f).getAmplitude(), TOLERANCE);
+ // Restores back to the exact original value since this is a linear scaling.
+ assertEquals(1f, initial.scaleLinearly(0.8f).scaleLinearly(1.25f).getAmplitude(),
+ TOLERANCE);
+
+ initial = new StepSegment(0, 0, 0);
+
+ assertEquals(0f, initial.scaleLinearly(1).getAmplitude(), TOLERANCE);
+ assertEquals(0f, initial.scaleLinearly(0.5f).getAmplitude(), TOLERANCE);
+ assertEquals(0f, initial.scaleLinearly(1.5f).getAmplitude(), TOLERANCE);
+ }
+
+ @Test
+ public void testScaleLinearly_defaultAmplitude() {
+ StepSegment initial = new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE, 0, 0);
+
+ assertEquals(VibrationEffect.DEFAULT_AMPLITUDE, initial.scaleLinearly(1).getAmplitude(),
+ TOLERANCE);
+ assertEquals(VibrationEffect.DEFAULT_AMPLITUDE, initial.scaleLinearly(0.5f).getAmplitude(),
+ TOLERANCE);
+ assertEquals(VibrationEffect.DEFAULT_AMPLITUDE, initial.scaleLinearly(1.5f).getAmplitude(),
+ TOLERANCE);
+ }
+
+ @Test
public void testDuration() {
assertEquals(5, new StepSegment(0, 0, 5).getDuration());
}
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 1fd10031a129..238a3e10f058 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -199,3 +199,8 @@ filegroup {
name: "services.core.protolog.json",
srcs: ["services.core.protolog.json"],
}
+
+filegroup {
+ name: "file-core.protolog.pb",
+ srcs: ["core.protolog.pb"],
+}
diff --git a/data/etc/com.android.intentresolver.xml b/data/etc/com.android.intentresolver.xml
index af6492609157..afd4f7cdba53 100644
--- a/data/etc/com.android.intentresolver.xml
+++ b/data/etc/com.android.intentresolver.xml
@@ -17,8 +17,10 @@
<permissions>
<privapp-permissions package="com.android.intentresolver">
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
+ <permission name="android.permission.LOG_COMPAT_CHANGE"/>
<permission name="android.permission.MANAGE_USERS"/>
<permission name="android.permission.PACKAGE_USAGE_STATS"/>
<permission name="android.permission.QUERY_CLONED_APPS"/>
+ <permission name="android.permission.READ_COMPAT_CHANGE_CONFIG"/>
</privapp-permissions>
</permissions>
diff --git a/data/etc/core.protolog.pb b/data/etc/core.protolog.pb
new file mode 100644
index 000000000000..0415e44af72a
--- /dev/null
+++ b/data/etc/core.protolog.pb
Binary files differ
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index c8cbb9800626..42e3387e3eb5 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -2077,6 +2077,12 @@
"group": "WM_DEBUG_LOCKTASK",
"at": "com\/android\/server\/wm\/LockTaskController.java"
},
+ "-315778658": {
+ "message": "transferTouchGesture failed because args transferFromToken or transferToToken is null",
+ "level": "ERROR",
+ "group": "WM_DEBUG_EMBEDDED_WINDOWS",
+ "at": "com\/android\/server\/wm\/WindowManagerService.java"
+ },
"-312353598": {
"message": "Executing finish of activity: %s",
"level": "VERBOSE",
@@ -2293,12 +2299,6 @@
"group": "WM_DEBUG_ANIM",
"at": "com\/android\/server\/wm\/WindowState.java"
},
- "-90559682": {
- "message": "Config is skipping already pausing %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_CONFIGURATION",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"-87705714": {
"message": "findFocusedWindow: focusedApp=null using new focus @ %s",
"level": "VERBOSE",
@@ -3547,12 +3547,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "1011462000": {
- "message": "Re-launching after pause: %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/TaskFragment.java"
- },
"1015746067": {
"message": "Display id=%d is ignoring orientation request for %d, return %d following a per-app override for %s",
"level": "VERBOSE",
diff --git a/graphics/java/Android.bp b/graphics/java/Android.bp
index db37a38756d2..ece453d1a70e 100644
--- a/graphics/java/Android.bp
+++ b/graphics/java/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_android_gpu",
// 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"
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index b33a5d2dcef8..b6ce9b64323b 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -153,6 +153,18 @@ public class Canvas extends BaseCanvas {
}
/**
+ * Indicates whether this Canvas is drawing high contrast text.
+ *
+ * @see android.view.accessibility.AccessibilityManager#isHighTextContrastEnabled()
+ * @return True if high contrast text is enabled, false otherwise.
+ *
+ * @hide
+ */
+ public boolean isHighContrastTextEnabled() {
+ return nIsHighContrastText(mNativeCanvasWrapper);
+ }
+
+ /**
* Specify a bitmap for the canvas to draw into. All canvas state such as
* layers, filters, and the save/restore stack are reset. Additionally,
* the canvas' target density is updated to match that of the bitmap.
@@ -1128,6 +1140,30 @@ public class Canvas extends BaseCanvas {
return false;
}
+ /**
+ * Intersect the current clip with the specified shader.
+ * The shader will be treated as an alpha mask, taking the intersection of the two.
+ *
+ * @param shader The shader to intersect with the current clip
+ */
+ @FlaggedApi(Flags.FLAG_CLIP_SHADER)
+ public void clipShader(@NonNull Shader shader) {
+ nClipShader(mNativeCanvasWrapper, shader.getNativeInstance(),
+ Region.Op.INTERSECT.nativeInt);
+ }
+
+ /**
+ * Set the clip to the difference of the current clip and the shader.
+ * The shader will be treated as an alpha mask, taking the difference of the two.
+ *
+ * @param shader The shader to intersect with the current clip
+ */
+ @FlaggedApi(Flags.FLAG_CLIP_SHADER)
+ public void clipOutShader(@NonNull Shader shader) {
+ nClipShader(mNativeCanvasWrapper, shader.getNativeInstance(),
+ Region.Op.DIFFERENCE.nativeInt);
+ }
+
public @Nullable DrawFilter getDrawFilter() {
return mDrawFilter;
}
@@ -1428,6 +1464,8 @@ public class Canvas extends BaseCanvas {
@CriticalNative
private static native boolean nIsOpaque(long canvasHandle);
@CriticalNative
+ private static native boolean nIsHighContrastText(long canvasHandle);
+ @CriticalNative
private static native int nGetWidth(long canvasHandle);
@CriticalNative
private static native int nGetHeight(long canvasHandle);
@@ -1472,6 +1510,8 @@ public class Canvas extends BaseCanvas {
@CriticalNative
private static native boolean nClipPath(long nativeCanvas, long nativePath, int regionOp);
@CriticalNative
+ private static native void nClipShader(long nativeCanvas, long nativeShader, int regionOp);
+ @CriticalNative
private static native void nSetDrawFilter(long nativeCanvas, long nativeFilter);
@CriticalNative
private static native void nGetMatrix(long nativeCanvas, long nativeMatrix);
diff --git a/graphics/java/android/graphics/pdf/TEST_MAPPING b/graphics/java/android/graphics/pdf/TEST_MAPPING
index d763598f5ba0..afec35c76371 100644
--- a/graphics/java/android/graphics/pdf/TEST_MAPPING
+++ b/graphics/java/android/graphics/pdf/TEST_MAPPING
@@ -1,7 +1,12 @@
{
"presubmit": [
{
- "name": "CtsPdfTestCases"
+ "name": "CtsPdfTestCases",
+ "options": [
+ {
+ "include-filter": "android.graphics.pdf.cts.PdfDocumentTest"
+ }
+ ]
}
]
}
diff --git a/graphics/proto/Android.bp b/graphics/proto/Android.bp
index 1b192668b4a3..fdcfdd525967 100644
--- a/graphics/proto/Android.bp
+++ b/graphics/proto/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_android_gpu",
// 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"
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 4982f3732089..244fe3033dca 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -618,7 +618,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
* @see #isMgf1DigestsSpecified()
*/
@NonNull
- @FlaggedApi(android.security.Flags.FLAG_MGF1_DIGEST_SETTER)
+ @FlaggedApi(android.security.Flags.FLAG_MGF1_DIGEST_SETTER_V2)
public @KeyProperties.DigestEnum Set<String> getMgf1Digests() {
if (mMgf1Digests.isEmpty()) {
throw new IllegalStateException("Mask generation function (MGF) not specified");
@@ -633,7 +633,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
* @see #getMgf1Digests()
*/
@NonNull
- @FlaggedApi(android.security.Flags.FLAG_MGF1_DIGEST_SETTER)
+ @FlaggedApi(android.security.Flags.FLAG_MGF1_DIGEST_SETTER_V2)
public boolean isMgf1DigestsSpecified() {
return !mMgf1Digests.isEmpty();
}
@@ -1292,7 +1292,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
* <p>See {@link KeyProperties}.{@code DIGEST} constants.
*/
@NonNull
- @FlaggedApi(android.security.Flags.FLAG_MGF1_DIGEST_SETTER)
+ @FlaggedApi(android.security.Flags.FLAG_MGF1_DIGEST_SETTER_V2)
public Builder setMgf1Digests(@NonNull @KeyProperties.DigestEnum String... mgf1Digests) {
mMgf1Digests = Set.of(mgf1Digests);
return this;
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index 7b6b2d142f95..2495d1a85864 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -401,7 +401,7 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs {
* @see #isMgf1DigestsSpecified()
*/
@NonNull
- @FlaggedApi(android.security.Flags.FLAG_MGF1_DIGEST_SETTER)
+ @FlaggedApi(android.security.Flags.FLAG_MGF1_DIGEST_SETTER_V2)
public @KeyProperties.DigestEnum Set<String> getMgf1Digests() {
if (mMgf1Digests.isEmpty()) {
throw new IllegalStateException("Mask generation function (MGF) not specified");
@@ -416,7 +416,7 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs {
* @see #getMgf1Digests()
*/
@NonNull
- @FlaggedApi(android.security.Flags.FLAG_MGF1_DIGEST_SETTER)
+ @FlaggedApi(android.security.Flags.FLAG_MGF1_DIGEST_SETTER_V2)
public boolean isMgf1DigestsSpecified() {
return !mMgf1Digests.isEmpty();
}
@@ -799,7 +799,7 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs {
* <p>See {@link KeyProperties}.{@code DIGEST} constants.
*/
@NonNull
- @FlaggedApi(android.security.Flags.FLAG_MGF1_DIGEST_SETTER)
+ @FlaggedApi(android.security.Flags.FLAG_MGF1_DIGEST_SETTER_V2)
public Builder setMgf1Digests(@Nullable @KeyProperties.DigestEnum String... mgf1Digests) {
mMgf1Digests = Set.of(mgf1Digests);
return this;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index 83ddfc5cf1a1..e6c652c14c71 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -974,7 +974,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
private static boolean getMgf1DigestSetterFlag() {
try {
- return Flags.mgf1DigestSetter();
+ return Flags.mgf1DigestSetterV2();
} catch (SecurityException e) {
Log.w(TAG, "Cannot read MGF1 Digest setter flag value", e);
return false;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index 2d8c5a380c6b..e6a63b9c4c17 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -259,7 +259,7 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
private static boolean getMgf1DigestSetterFlag() {
try {
- return Flags.mgf1DigestSetter();
+ return Flags.mgf1DigestSetterV2();
} catch (SecurityException e) {
Log.w(NAME, "Cannot read MGF1 Digest setter flag value", e);
return false;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index b2e5b75cf0b5..ae3a854baf9f 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -72,6 +72,7 @@ import android.util.Pair;
import android.util.Size;
import android.util.SparseArray;
import android.view.WindowMetrics;
+import android.window.ActivityWindowInfo;
import android.window.TaskFragmentAnimationParams;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentOperation;
@@ -2864,11 +2865,27 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
*/
@Override
public boolean isActivityEmbedded(@NonNull Activity activity) {
+ Objects.requireNonNull(activity);
synchronized (mLock) {
+ if (Flags.activityWindowInfoFlag()) {
+ final ActivityWindowInfo activityWindowInfo = getActivityWindowInfo(activity);
+ return activityWindowInfo != null && activityWindowInfo.isEmbedded();
+ }
return mPresenter.isActivityEmbedded(activity.getActivityToken());
}
}
+ @Nullable
+ private static ActivityWindowInfo getActivityWindowInfo(@NonNull Activity activity) {
+ if (activity.isFinishing()) {
+ return null;
+ }
+ final ActivityThread.ActivityClientRecord record =
+ ActivityThread.currentActivityThread()
+ .getActivityClient(activity.getActivityToken());
+ return record != null ? record.getActivityWindowInfo() : null;
+ }
+
/**
* If the two rules have the same presentation, and the calculated {@link SplitAttributes}
* matches the {@link SplitAttributes} of {@link SplitContainer}, we can reuse the same
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 310300d2f32a..0ecf1f8f1feb 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -82,16 +82,18 @@ filegroup {
genrule {
name: "wm_shell_protolog_src",
srcs: [
+ ":protolog-impl",
":wm_shell_protolog-groups",
":wm_shell-sources",
],
tools: ["protologtool"],
cmd: "$(location protologtool) transform-protolog-calls " +
"--protolog-class com.android.internal.protolog.common.ProtoLog " +
- "--protolog-impl-class com.android.wm.shell.protolog.ShellProtoLogImpl " +
- "--protolog-cache-class com.android.wm.shell.protolog.ShellProtoLogCache " +
"--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " +
"--loggroups-jar $(location :wm_shell_protolog-groups) " +
+ "--viewer-config-file-path /system_ext/etc/wmshell.protolog.pb " +
+ "--legacy-viewer-config-file-path /system_ext/etc/wmshell.protolog.json.gz " +
+ "--legacy-output-file-path /data/misc/wmtrace/shell_log.winscope " +
"--output-srcjar $(out) " +
"$(locations :wm_shell-sources)",
out: ["wm_shell_protolog.srcjar"],
@@ -108,12 +110,30 @@ genrule {
"--protolog-class com.android.internal.protolog.common.ProtoLog " +
"--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " +
"--loggroups-jar $(location :wm_shell_protolog-groups) " +
- "--viewer-conf $(out) " +
+ "--viewer-config-type json " +
+ "--viewer-config $(out) " +
"$(locations :wm_shell-sources)",
out: ["wm_shell_protolog.json"],
}
genrule {
+ name: "gen-wmshell.protolog.pb",
+ srcs: [
+ ":wm_shell_protolog-groups",
+ ":wm_shell-sources",
+ ],
+ tools: ["protologtool"],
+ cmd: "$(location protologtool) generate-viewer-config " +
+ "--protolog-class com.android.internal.protolog.common.ProtoLog " +
+ "--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " +
+ "--loggroups-jar $(location :wm_shell_protolog-groups) " +
+ "--viewer-config-type proto " +
+ "--viewer-config $(out) " +
+ "$(locations :wm_shell-sources)",
+ out: ["wmshell.protolog.pb"],
+}
+
+genrule {
name: "protolog.json.gz",
srcs: [":generate-wm_shell_protolog.json"],
out: ["wmshell.protolog.json.gz"],
@@ -127,6 +147,13 @@ prebuilt_etc {
filename_from_src: true,
}
+prebuilt_etc {
+ name: "wmshell.protolog.pb",
+ system_ext_specific: true,
+ src: ":gen-wmshell.protolog.pb",
+ filename_from_src: true,
+}
+
// End ProtoLog
java_library {
@@ -206,6 +233,8 @@ android_robolectric_test {
srcs: [
"multivalentTests/src/**/*.kt",
],
+ // TODO(b/323188766): Include BubbleStackViewTest once the robolectric issue is fixed.
+ exclude_srcs: ["multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt"],
static_libs: [
"junit",
"androidx.test.runner",
diff --git a/libs/WindowManager/Shell/aconfig/OWNERS b/libs/WindowManager/Shell/aconfig/OWNERS
new file mode 100644
index 000000000000..9eba0f2dea7b
--- /dev/null
+++ b/libs/WindowManager/Shell/aconfig/OWNERS
@@ -0,0 +1,3 @@
+# Owners for flag changes
+madym@google.com
+hwwang@google.com \ No newline at end of file
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 9a66c0fa9eb9..0967f4e83c74 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -64,3 +64,10 @@ flag {
description: "Enables the new bubble bar UI for tablets"
bug: "286246694"
}
+
+flag {
+ name: "enable_bubbles_long_press_nav_handle"
+ namespace: "multitasking"
+ description: "Enables long-press action for nav handle when a bubble is expanded"
+ bug: "324910035"
+}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt
index 5825bbfbfefa..9cd14fca6a9d 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt
@@ -466,6 +466,26 @@ class BubblePositionerTest {
.isEqualTo(expectedExpandedViewY)
}
+ @Test
+ fun testGetTaskViewContentWidth_onLeft() {
+ positioner.update(defaultDeviceConfig.copy(insets = Insets.of(100, 0, 200, 0)))
+ val taskViewWidth = positioner.getTaskViewContentWidth(true /* onLeft */)
+ val paddings = positioner.getExpandedViewContainerPadding(true /* onLeft */,
+ false /* isOverflow */)
+ assertThat(taskViewWidth).isEqualTo(
+ positioner.screenRect.width() - paddings[0] - paddings[2])
+ }
+
+ @Test
+ fun testGetTaskViewContentWidth_onRight() {
+ positioner.update(defaultDeviceConfig.copy(insets = Insets.of(100, 0, 200, 0)))
+ val taskViewWidth = positioner.getTaskViewContentWidth(false /* onLeft */)
+ val paddings = positioner.getExpandedViewContainerPadding(false /* onLeft */,
+ false /* isOverflow */)
+ assertThat(taskViewWidth).isEqualTo(
+ positioner.screenRect.width() - paddings[0] - paddings[2])
+ }
+
private val defaultYPosition: Float
/**
* Calculates the Y position bubbles should be placed based on the config. Based on the
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
new file mode 100644
index 000000000000..8989fc543044
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2024 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.bubbles
+
+import android.content.Context
+import android.content.Intent
+import android.graphics.Color
+import android.graphics.drawable.Icon
+import android.os.UserHandle
+import android.view.IWindowManager
+import android.view.WindowManager
+import android.view.WindowManagerGlobal
+import androidx.test.annotation.UiThreadTest
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.internal.protolog.common.ProtoLog
+import com.android.launcher3.icons.BubbleIconFactory
+import com.android.wm.shell.R
+import com.android.wm.shell.bubbles.Bubbles.SysuiProxy
+import com.android.wm.shell.common.FloatingContentCoordinator
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.taskview.TaskView
+import com.android.wm.shell.taskview.TaskViewTaskController
+import com.google.common.truth.Truth.assertThat
+import com.google.common.util.concurrent.MoreExecutors.directExecutor
+import java.util.concurrent.Semaphore
+import java.util.concurrent.TimeUnit
+import java.util.function.Consumer
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+
+/** Unit tests for [BubbleStackView]. */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class BubbleStackViewTest {
+
+ private val context = ApplicationProvider.getApplicationContext<Context>()
+ private lateinit var positioner: BubblePositioner
+ private lateinit var iconFactory: BubbleIconFactory
+ private lateinit var expandedViewManager: FakeBubbleExpandedViewManager
+ private lateinit var bubbleStackView: BubbleStackView
+ private lateinit var shellExecutor: ShellExecutor
+ private lateinit var windowManager: IWindowManager
+ private lateinit var bubbleTaskViewFactory: BubbleTaskViewFactory
+ private lateinit var bubbleData: BubbleData
+
+ @Before
+ fun setUp() {
+ // Disable protolog tool when running the tests from studio
+ ProtoLog.REQUIRE_PROTOLOGTOOL = false
+ windowManager = WindowManagerGlobal.getWindowManagerService()!!
+ shellExecutor = TestShellExecutor()
+ val windowManager = context.getSystemService(WindowManager::class.java)
+ iconFactory =
+ BubbleIconFactory(
+ context,
+ context.resources.getDimensionPixelSize(R.dimen.bubble_size),
+ context.resources.getDimensionPixelSize(R.dimen.bubble_badge_size),
+ Color.BLACK,
+ context.resources.getDimensionPixelSize(
+ com.android.internal.R.dimen.importance_ring_stroke_width
+ )
+ )
+ positioner = BubblePositioner(context, windowManager)
+ val bubbleStackViewManager = FakeBubbleStackViewManager()
+ bubbleData =
+ BubbleData(
+ context,
+ BubbleLogger(UiEventLoggerFake()),
+ positioner,
+ BubbleEducationController(context),
+ shellExecutor
+ )
+
+ val sysuiProxy = mock<SysuiProxy>()
+ expandedViewManager = FakeBubbleExpandedViewManager()
+ bubbleTaskViewFactory = FakeBubbleTaskViewFactory()
+ bubbleStackView =
+ BubbleStackView(
+ context,
+ bubbleStackViewManager,
+ positioner,
+ bubbleData,
+ null,
+ FloatingContentCoordinator(),
+ { sysuiProxy },
+ shellExecutor
+ )
+ }
+
+ @UiThreadTest
+ @Test
+ fun addBubble() {
+ val bubble = createAndInflateBubble()
+ bubbleStackView.addBubble(bubble)
+ assertThat(bubbleStackView.bubbleCount).isEqualTo(1)
+ }
+
+ @UiThreadTest
+ @Test
+ fun tapBubbleToExpand() {
+ val bubble = createAndInflateBubble()
+ bubbleStackView.addBubble(bubble)
+ assertThat(bubbleStackView.bubbleCount).isEqualTo(1)
+
+ bubble.iconView!!.performClick()
+ // we're checking the expanded state in BubbleData because that's the source of truth. This
+ // will eventually propagate an update back to the stack view, but setting the entire
+ // pipeline is outside the scope of a unit test.
+ assertThat(bubbleData.isExpanded).isTrue()
+ }
+
+ private fun createAndInflateBubble(): Bubble {
+ val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
+ val icon = Icon.createWithResource(context.resources, R.drawable.bubble_ic_overflow_button)
+ val bubble = Bubble.createAppBubble(intent, UserHandle(1), icon, directExecutor())
+ bubble.setInflateSynchronously(true)
+ bubbleData.notificationEntryUpdated(bubble, true, false)
+
+ val semaphore = Semaphore(0)
+ val callback: BubbleViewInfoTask.Callback =
+ BubbleViewInfoTask.Callback { semaphore.release() }
+ bubble.inflate(
+ callback,
+ context,
+ expandedViewManager,
+ bubbleTaskViewFactory,
+ positioner,
+ bubbleStackView,
+ null,
+ iconFactory,
+ false
+ )
+
+ assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
+ assertThat(bubble.isInflated).isTrue()
+ return bubble
+ }
+
+ private class FakeBubbleStackViewManager : BubbleStackViewManager {
+
+ override fun onAllBubblesAnimatedOut() {}
+
+ override fun updateWindowFlagsForBackpress(interceptBack: Boolean) {}
+
+ override fun checkNotificationPanelExpandedState(callback: Consumer<Boolean>) {}
+
+ override fun hideCurrentInputMethod() {}
+ }
+
+ private class TestShellExecutor : ShellExecutor {
+
+ override fun execute(runnable: Runnable) {
+ runnable.run()
+ }
+
+ override fun executeDelayed(r: Runnable, delayMillis: Long) {
+ r.run()
+ }
+
+ override fun removeCallbacks(r: Runnable) {}
+
+ override fun hasCallback(r: Runnable): Boolean = false
+ }
+
+ private inner class FakeBubbleTaskViewFactory : BubbleTaskViewFactory {
+ override fun create(): BubbleTaskView {
+ val taskViewTaskController = mock<TaskViewTaskController>()
+ val taskView = TaskView(context, taskViewTaskController)
+ return BubbleTaskView(taskView, shellExecutor)
+ }
+ }
+
+ private inner class FakeBubbleExpandedViewManager : BubbleExpandedViewManager {
+
+ override val overflowBubbles: List<Bubble>
+ get() = emptyList()
+
+ override fun setOverflowListener(listener: BubbleData.Listener) {}
+
+ override fun collapseStack() {}
+
+ override fun updateWindowFlagsForBackpress(intercept: Boolean) {}
+
+ override fun promoteBubbleFromOverflow(bubble: Bubble) {}
+
+ override fun removeBubble(key: String, reason: Int) {}
+
+ override fun dismissBubble(bubble: Bubble, reason: Int) {}
+
+ override fun setAppBubbleTaskId(key: String, taskId: Int) {}
+
+ override fun isStackExpanded(): Boolean = false
+
+ override fun isShowingAsBubbleBar(): Boolean = false
+ }
+}
diff --git a/libs/WindowManager/Shell/res/color/desktop_mode_caption_button_color_selector_dark.xml b/libs/WindowManager/Shell/res/color/desktop_mode_caption_button_color_selector_dark.xml
new file mode 100644
index 000000000000..52a59671baa1
--- /dev/null
+++ b/libs/WindowManager/Shell/res/color/desktop_mode_caption_button_color_selector_dark.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 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.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_hovered="true"
+ android:color="@color/desktop_mode_caption_button_on_hover_dark"/>
+ <item android:color="@color/desktop_mode_caption_button"/>
+</selector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/color/desktop_mode_caption_button_color_selector_light.xml b/libs/WindowManager/Shell/res/color/desktop_mode_caption_button_color_selector_light.xml
new file mode 100644
index 000000000000..6d8a51cd6f8f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/color/desktop_mode_caption_button_color_selector_light.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 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.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_hovered="true"
+ android:color="@color/desktop_mode_caption_button_on_hover_light"/>
+ <item android:color="@color/desktop_mode_caption_button"/>
+</selector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/circular_progress.xml b/libs/WindowManager/Shell/res/drawable/circular_progress.xml
new file mode 100644
index 000000000000..948264579e1d
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/circular_progress.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 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.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@android:id/progress">
+ <rotate
+ android:pivotX="50%"
+ android:pivotY="50%"
+ android:fromDegrees="275"
+ android:toDegrees="275">
+ <shape
+ android:shape="ring"
+ android:thickness="3dp"
+ android:innerRadius="17dp"
+ android:useLevel="true">
+ </shape>
+ </rotate>
+ </item>
+</layer-list> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/rounded_button.xml b/libs/WindowManager/Shell/res/drawable/rounded_button.xml
new file mode 100644
index 000000000000..17a0bab56a74
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/rounded_button.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <corners android:radius="20dp" />
+</shape> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_expanded_view.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_expanded_view.xml
index e04ab817215c..34f03c2f226b 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_bar_expanded_view.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_expanded_view.xml
@@ -23,7 +23,8 @@
<com.android.wm.shell.bubbles.bar.BubbleBarHandleView
android:id="@+id/bubble_bar_handle_view"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content" />
+ android:layout_height="@dimen/bubble_bar_expanded_view_caption_height"
+ android:layout_width="@dimen/bubble_bar_expanded_view_caption_width"
+ android:layout_gravity="top|center_horizontal" />
</com.android.wm.shell.bubbles.bar.BubbleBarExpandedView>
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
index e4f793c2665b..d1b1af3e77ab 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
@@ -74,17 +74,11 @@
android:layout_height="40dp"
android:layout_weight="1"/>
- <ImageButton
- android:id="@+id/maximize_window"
- android:layout_width="40dp"
- android:layout_height="40dp"
- android:padding="9dp"
- android:layout_marginEnd="8dp"
- android:contentDescription="@string/maximize_button_text"
- android:src="@drawable/decor_desktop_mode_maximize_button_dark"
- android:scaleType="fitCenter"
- android:gravity="end"
- android:background="@null"/>
+ <com.android.wm.shell.windowdecor.MaximizeButtonView
+ android:id="@+id/maximize_button_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="end"/>
<ImageButton
android:id="@+id/close_window"
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
index 0db72f7be8e6..dbfd6e5d8d94 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
@@ -15,6 +15,7 @@
~ limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/maximize_menu"
style="?android:attr/buttonBarStyle"
android:layout_width="@dimen/desktop_mode_maximize_menu_width"
android:layout_height="@dimen/desktop_mode_maximize_menu_height"
diff --git a/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml b/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml
new file mode 100644
index 000000000000..bb6efcec1a70
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 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.
+ -->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+ <ProgressBar
+ android:id="@+id/progress_bar"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:progressDrawable="@drawable/circular_progress"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:indeterminate="false"
+ android:visibility="invisible"/>
+
+ <ImageButton
+ android:id="@+id/maximize_window"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:padding="9dp"
+ android:contentDescription="@string/maximize_button_text"
+ android:src="@drawable/decor_desktop_mode_maximize_button_dark"
+ android:scaleType="fitCenter"
+ android:background="@drawable/rounded_button"/>
+</merge> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
index 90b13cdc79b4..dd6f8455f82a 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Maak toe"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Vou uit"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Instellings"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Gaan by verdeelde skerm in"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Kieslys"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Prent-in-prent-kieslys"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> is in beeld-in-beeld"</string>
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
index 478585aace68..18a4ccf5c16d 100644
--- a/libs/WindowManager/Shell/res/values-am/strings.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"ዝጋ"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"ዘርጋ"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"ቅንብሮች"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"የተከፈለ ማያ ገጽን አስገባ"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"ምናሌ"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"የሥዕል-ላይ-ሥዕል ምናሌ"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> በሥዕል-ላይ-ሥዕል ውስጥ ነው"</string>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index b2a522c89f8c..7ca335e4a655 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"إغلاق"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"توسيع"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"الإعدادات"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"الدخول في وضع تقسيم الشاشة"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"القائمة"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"قائمة نافذة ضمن النافذة"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> يظهر في صورة داخل صورة"</string>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index 897c38f66e4b..944c4f25bf2a 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"বন্ধ কৰক"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"বিস্তাৰ কৰক"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"ছেটিং"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"বিভাজিত স্ক্ৰীন ম’ডলৈ যাওক"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"মেনু"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"চিত্ৰৰ ভিতৰৰ চিত্ৰ মেনু"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> চিত্ৰৰ ভিতৰৰ চিত্ৰত আছে"</string>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
index 4854e0db7ed5..c320e415d604 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Bağlayın"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Genişləndirin"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Ayarlar"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Bölünmüş ekrana daxil olun"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menyu"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Şəkildə Şəkil Menyusu"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> şəkil içində şəkildədir"</string>
diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
index cac4e67b1cfc..19ca4d300b7e 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Zatvori"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Proširi"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Podešavanja"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Uđi na podeljeni ekran"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Meni"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Meni slike u slici."</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> je slika u slici"</string>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index cac76df15910..74ae1d7637d0 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Закрыць"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Разгарнуць"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Налады"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Падзяліць экран"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Меню"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Меню рэжыму \"Відарыс у відарысе\""</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> з’яўляецца відарысам у відарысе"</string>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
index ac9a20806b72..1b753f5359ba 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Затваряне"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Разгъване"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Настройки"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Преминаване към разделен екран"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Меню"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Меню за режима „Картина в картината“"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> е в режима „Картина в картината“"</string>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index 3b83dcb461ee..2ea22cc1455a 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"বন্ধ করুন"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"বড় করুন"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"সেটিংস"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"\'স্প্লিট স্ক্রিন\' মোড চালু করুন"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"মেনু"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"ছবির-মধ্যে-ছবি মেনু"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"ছবির-মধ্যে-ছবি তে <xliff:g id="NAME">%s</xliff:g> আছেন"</string>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index 813d163d07fe..13655b3c5c85 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Zatvori"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Proširi"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Postavke"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Otvori podijeljeni ekran"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Meni"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Meni načina rada slike u slici"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> je u načinu priakza Slika u slici"</string>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
index d00c50bb0294..cb897c5b612d 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Tanca"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Desplega"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Configuració"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Entra al mode de pantalla dividida"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menú"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menú d\'imatge sobre imatge"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> està en mode d\'imatge sobre imatge"</string>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index 40132e4f67f8..ded2707afd1d 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Zavřít"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Rozbalit"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Nastavení"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Aktivovat rozdělenou obrazovku"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Nabídka"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Nabídka režimu obrazu v obraze"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"Aplikace <xliff:g id="NAME">%s</xliff:g> je v režimu obraz v obraze"</string>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
index 6e9738dc7398..2bdb29d67447 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Luk"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Udvid"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Indstillinger"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Åbn opdelt skærm"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menu for integreret billede"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> vises som integreret billede"</string>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index 5da224d35360..e99d9d0c269a 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Schließen"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Maximieren"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Einstellungen"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Splitscreen aktivieren"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menü"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menü „Bild im Bild“"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> ist in Bild im Bild"</string>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index 822b5526c6fd..d8bb740535fc 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Κλείσιμο"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Ανάπτυξη"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Ρυθμίσεις"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Μετάβαση σε διαχωρισμό οθόνης"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Μενού"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Μενού λειτουργίας Picture-in-Picture"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"Η λειτουργία picture-in-picture είναι ενεργή σε <xliff:g id="NAME">%s</xliff:g>."</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
index 76464b398f89..5e1b274705dd 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Close"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Expand"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Settings"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Enter split screen"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Picture-in-picture menu"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> is in picture-in-picture"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
index c0c46cd608ee..2525b321d9d7 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Close"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Expand"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Settings"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Enter split screen"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Picture-in-Picture Menu"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> is in picture-in-picture"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
index 76464b398f89..5e1b274705dd 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Close"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Expand"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Settings"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Enter split screen"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Picture-in-picture menu"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> is in picture-in-picture"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
index 76464b398f89..5e1b274705dd 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Close"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Expand"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Settings"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Enter split screen"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Picture-in-picture menu"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> is in picture-in-picture"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
index f089938fd9cb..0623bef925e2 100644
--- a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‏‎‎‎‏‎‎‎‎‎‎‎‏‎‎‎‏‏‎‎‎‎‏‎‏‎‎‎‏‎‎‏‎‏‎‎‎‏‎‏‎‏‏‎‎‎‏‏‎‏‎‏‏‎‏‎Close‎‏‎‎‏‎"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‏‏‏‎‎‏‎‏‏‎‏‏‏‏‏‎‏‎‎‏‏‏‎‏‎‏‏‏‎‎‎‏‎‏‏‏‎‏‎‎‏‏‏‎‎‎‏‏‎‎‎‎‏‎‎‎‎‎Expand‎‏‎‎‏‎"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‏‏‎‎‏‎‏‏‎‏‏‏‏‏‎‏‏‎‎‏‏‏‎‏‎‎‎‎‏‏‏‎‎‎‏‏‎‎‏‏‏‎‏‎‎‎‏‎‎‎‎‏‏‏‎‎Settings‎‏‎‎‏‎"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‏‏‏‏‎‏‎‏‎‏‎‎‏‏‏‎‎‎‏‏‏‎‏‎‎‎‎‏‏‎‎‏‎‎‎‏‏‏‏‏‎‏‏‏‎‏‎‏‏‏‎‏‏‏‎Enter split screen‎‏‎‎‏‎"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‏‎‏‏‎‎‏‏‏‏‏‏‏‎‎‎‎‏‏‎‎‏‎‎‏‏‎‎‏‎‎‎‎‏‎‏‎‎‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎‎‎‎Menu‎‏‎‎‏‎"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‏‎‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‎‎‏‏‏‏‎‏‏‏‏‏‎‎‎‎‎‏‎‎‎‏‏‏‎‎‏‎‏‏‏‏‎‎‏‎Picture-in-Picture Menu‎‏‎‎‏‎"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‏‎‎‎‏‏‏‏‎‎‎‎‎‏‎‎‏‏‏‎‎‏‏‎‎‏‏‏‎‎‏‎‎‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‏‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="NAME">%s</xliff:g>‎‏‎‎‏‏‏‎ is in picture-in-picture‎‏‎‎‏‎"</string>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
index 6bbc1e37671f..9fe77ddf7e28 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Cerrar"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Expandir"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Configuración"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Introducir pantalla dividida"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menú"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menú de pantalla en pantalla"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> está en modo de Pantalla en pantalla"</string>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index c662ff603dd7..b88f215eb54e 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Cerrar"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Mostrar"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Ajustes"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Introducir pantalla dividida"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menú"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menú de imagen en imagen"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> está en imagen en imagen"</string>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index ade5e2d18645..529b6d10b3c6 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Sule"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Laiendamine"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Seaded"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Ava jagatud ekraanikuva"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menüü"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menüü Pilt pildis"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> on režiimis Pilt pildis"</string>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
index d6cb66885cf5..7438f4240eae 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Itxi"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Zabaldu"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Ezarpenak"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Sartu pantaila zatituan"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menua"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Pantaila txiki gainjarriaren menua"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"Pantaila txiki gainjarrian dago <xliff:g id="NAME">%s</xliff:g>"</string>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index ba0f51cb1490..f7fcb2162603 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"بستن"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"بزرگ کردن"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"تنظیمات"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"ورود به حالت «صفحهٔ دونیمه»"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"منو"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"منو تصویر در تصویر"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> درحالت تصویر در تصویر است"</string>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
index a53f861e1d18..400107317637 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Sulje"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Laajenna"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Asetukset"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Avaa jaettu näyttö"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Valikko"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Kuva kuvassa ‑valikko"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> on kuva kuvassa ‑tilassa"</string>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
index 4563556657af..a883e087cb3b 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Fermer"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Développer"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Paramètres"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Entrer dans l\'écran partagé"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menu d\'incrustation d\'image"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> est en mode d\'incrustation d\'image"</string>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
index 895757184b23..357ff91df06d 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Fermer"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Développer"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Paramètres"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Accéder à l\'écran partagé"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menu \"Picture-in-picture\""</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> est en mode Picture-in-picture"</string>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index 54c864eced47..a62190754129 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Pechar"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Despregar"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Configuración"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Inserir pantalla dividida"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menú"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menú de pantalla superposta"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> está na pantalla superposta"</string>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index 2b092795b035..43c178f50d15 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"બંધ કરો"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"વિસ્તૃત કરો"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"સેટિંગ"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"વિભાજિત સ્ક્રીન મોડમાં દાખલ થાઓ"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"મેનૂ"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"ચિત્રમાં ચિત્ર મેનૂ"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> ચિત્રમાં-ચિત્રની અંદર છે"</string>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index 35b099ac6d38..9f6a57fa0d73 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"बंद करें"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"विस्तार करें"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"सेटिंग"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"स्प्लिट स्क्रीन मोड में जाएं"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"मेन्यू"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"पिक्चर में पिक्चर मेन्यू"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> \"पिक्चर में पिक्चर\" के अंदर है"</string>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index f2c3c22414df..4378c5642f5c 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Zatvori"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Proširivanje"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Postavke"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Otvorite podijeljeni zaslon"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Izbornik"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Izbornik slike u slici"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> jest na slici u slici"</string>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
index d94bb29f1d73..e5f199fea647 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Bezárás"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Kibontás"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Beállítások"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Váltás osztott képernyőre"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menü"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Kép a képben menü"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"A(z) <xliff:g id="NAME">%s</xliff:g> kép a képben funkciót használ"</string>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
index f2945c16e50b..e0a5afe13827 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Փակել"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Ընդարձակել"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Կարգավորումներ"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Մտնել տրոհված էկրանի ռեժիմ"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Ընտրացանկ"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"«Նկար նկարի մեջ» ռեժիմի ընտրացանկ"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g>-ը «Նկար նկարի մեջ» ռեժիմում է"</string>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index c39b429e489c..802583771852 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Tutup"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Luaskan"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Setelan"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Masuk ke mode layar terpisah"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menu Picture-in-Picture"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> adalah picture-in-picture"</string>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index 630eaa3855c1..cece56ec960f 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Loka"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Stækka"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Stillingar"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Opna skjáskiptingu"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Valmynd"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Valmynd fyrir mynd í mynd"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> er með mynd í mynd"</string>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index 77893c9e38cf..731db8c12825 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Chiudi"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Espandi"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Impostazioni"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Accedi a schermo diviso"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menu Picture in picture"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> è in Picture in picture"</string>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index 4f28c23ba5df..adf55f3696c3 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"סגירה"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"הרחבה"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"הגדרות"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"כניסה למסך המפוצל"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"תפריט"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"תפריט \'תמונה בתוך תמונה\'"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> במצב תמונה בתוך תמונה"</string>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index 60b4d7eb8b4d..35432229dc7b 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"閉じる"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"展開"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"設定"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"分割画面に切り替え"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"メニュー"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"ピクチャー イン ピクチャーのメニュー"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g>はピクチャー イン ピクチャーで表示中です"</string>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index 28d2257786a7..1e6e657b5cf8 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"დახურვა"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"გაშლა"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"პარამეტრები"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"გაყოფილ ეკრანში შესვლა"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"მენიუ"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"„ეკრანი ეკრანში“ რეჟიმის მენიუ"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> იყენებს რეჟიმს „ეკრანი ეკრანში“"</string>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
index 441df8d70e95..6d9ff26132ea 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Жабу"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Жаю"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Параметрлер"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Бөлінген экранға кіру"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Mәзір"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"\"Сурет ішіндегі сурет\" мәзірі"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> \"суреттегі сурет\" режимінде"</string>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index efa6418eaa47..586ef7327a70 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"បិទ"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"ពង្រីក"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"ការកំណត់"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"ចូលមុខងារ​បំបែកអេក្រង់"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"ម៉ឺនុយ"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"ម៉ឺនុយ​រូប​ក្នុងរូប"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> ស្ថិតក្នុងមុខងាររូបក្នុងរូប"</string>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index 0cda44509b54..78ca0c7979a9 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"ಮುಚ್ಚಿ"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"ವಿಸ್ತೃತಗೊಳಿಸು"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"ಸ್ಪ್ಲಿಟ್‌-ಸ್ಕ್ರೀನ್‌ಗೆ ಪ್ರವೇಶಿಸಿ"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"ಮೆನು"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"ಚಿತ್ರದಲ್ಲಿ ಚಿತ್ರ ಮೆನು"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> ಚಿತ್ರದಲ್ಲಿ ಚಿತ್ರವಾಗಿದೆ"</string>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
index 676506fc68dd..70aa3767d7dd 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"닫기"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"펼치기"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"설정"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"화면 분할 모드로 전환"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"메뉴"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"PIP 모드 메뉴"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g>에서 PIP 사용 중"</string>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index 57253ef55085..b2a0a49b401a 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Жабуу"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Жайып көрсөтүү"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Параметрлер"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Экранды бөлүү режимине өтүү"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Меню"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Сүрөт ичиндеги сүрөт менюсу"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> – сүрөт ичиндеги сүрөт"</string>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
index c5f6e2245b31..1cbdbd412631 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"ປິດ"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"ຂະຫຍາຍ"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"ການຕັ້ງຄ່າ"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"ເຂົ້າການແບ່ງໜ້າຈໍ"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"ເມນູ"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"ເມນູການສະແດງຜົນຊ້ອນກັນ"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> ແມ່ນເປັນການສະແດງຜົນຫຼາຍຢ່າງພ້ອມກັນ"</string>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index eeed5a416fdc..d154c57704a1 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Uždaryti"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Išskleisti"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Nustatymai"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Įjungti išskaidyto ekrano režimą"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Meniu"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Vaizdo vaizde meniu"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> rodom. vaizdo vaizde"</string>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
index 4324d468042b..ce269503ceef 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Aizvērt"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Izvērst"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Iestatījumi"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Piekļūt ekrāna sadalīšanas režīmam"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Izvēlne"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Izvēlne attēlam attēlā"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> ir attēlā attēlā"</string>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
index 471f5bdfcf1a..9d69c50626be 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Затвори"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Проширете"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Поставки"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Влези во поделен екран"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Мени"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Мени за „Слика во слика“"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> е во слика во слика"</string>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
index 5bc694a10747..c0e83386d572 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"അവസാനിപ്പിക്കുക"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"വികസിപ്പിക്കുക"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"ക്രമീകരണം"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"സ്ക്രീൻ വിഭജന മോഡിൽ പ്രവേശിക്കുക"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"മെനു"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"ചിത്രത്തിനുള്ളിൽ ചിത്രം മെനു"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> ചിത്രത്തിനുള്ളിൽ ചിത്രം രീതിയിലാണ്"</string>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index 0268c649380d..ba5d283fb8a7 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Хаах"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Дэлгэх"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Тохиргоо"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Хуваасан дэлгэцийг оруулна уу"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Цэс"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Дэлгэц доторх дэлгэцийн цэс"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> дэлгэцэн доторх дэлгэцэд байна"</string>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index 2e6163e65668..17601c18366a 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"बंद करा"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"विस्तृत करा"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"सेटिंग्ज"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"स्प्लिट स्क्रीन एंटर करा"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"मेनू"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"चित्रात-चित्र मेनू"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> चित्रामध्ये चित्र मध्ये आहे"</string>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index a60e61b892cb..d5547fa8a056 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Tutup"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Kembangkan"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Tetapan"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Masuk skrin pisah"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menu Gambar dalam Gambar"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> terdapat dalam gambar dalam gambar"</string>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
index 6b91d4676621..07bfc990d62d 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"ပိတ်ရန်"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"ချဲ့ရန်"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"ဆက်တင်များ"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"မျက်နှာပြင် ခွဲ၍ပြသခြင်းသို့ ဝင်ရန်"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"မီနူး"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"နှစ်ခုထပ်၍ ကြည့်ခြင်းမီနူး"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> သည် နှစ်ခုထပ်၍ကြည့်ခြင်း ဖွင့်ထားသည်"</string>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index ec9ece3484b8..f609d019daae 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Lukk"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Vis"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Innstillinger"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Aktivér delt skjerm"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Meny"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Bilde-i-bilde-meny"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> er i bilde-i-bilde"</string>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index 8bb07be12c48..f7d49908a9f7 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"बन्द गर्नुहोस्"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"विस्तृत गर्नुहोस्"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"सेटिङहरू"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"स्प्लिट स्क्रिन मोड प्रयोग गर्नुहोस्"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"मेनु"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"\"picture-in-picture\" मेनु"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> Picture-in-picture मा छ"</string>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index c6c60ae4b1f2..a38cb7547385 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Sluiten"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Uitvouwen"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Instellingen"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Gesplitst scherm openen"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Scherm-in-scherm-menu"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> is in scherm-in-scherm"</string>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
index 927dde40134d..e3097beb6166 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"ବଢ଼ାନ୍ତୁ"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"ସେଟିଂସ୍"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ ମୋଡ ବ୍ୟବହାର କରନ୍ତୁ"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"ମେନୁ"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"ପିକଚର-ଇନ-ପିକଚର ମେନୁ"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> \"ଛବି-ଭିତରେ-ଛବି\"ରେ ଅଛି"</string>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
index 0e12fb872005..3aea6f69749f 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"ਬੰਦ ਕਰੋ"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"ਵਿਸਤਾਰ ਕਰੋ"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"ਸੈਟਿੰਗਾਂ"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਵਿੱਚ ਦਾਖਲ ਹੋਵੋ"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"ਮੀਨੂ"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"ਤਸਵੀਰ-ਵਿੱਚ-ਤਸਵੀਰ ਮੀਨੂ"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> ਤਸਵੀਰ-ਅੰਦਰ-ਤਸਵੀਰ ਵਿੱਚ ਹੈ"</string>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index 75a8ce6bc16d..aec3722cefa5 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Zamknij"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Rozwiń"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Ustawienia"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Włącz podzielony ekran"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menu funkcji Obraz w obrazie."</string>
<string name="pip_notification_title" msgid="1347104727641353453">"Aplikacja <xliff:g id="NAME">%s</xliff:g> działa w trybie obraz w obrazie"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
index b84a0ded4939..49935ada4656 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Fechar"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Abrir"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Configurações"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Dividir tela"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menu do picture-in-picture"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> está em picture-in-picture"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
index d84bfcdd73ff..f636da7997eb 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Fechar"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Expandir"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Definições"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Aceder ao ecrã dividido"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menu de ecrã no ecrã"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"A app <xliff:g id="NAME">%s</xliff:g> está no modo de ecrã no ecrã"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index b84a0ded4939..49935ada4656 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Fechar"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Abrir"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Configurações"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Dividir tela"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menu do picture-in-picture"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> está em picture-in-picture"</string>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index eeea428cc8fa..c20f350ae198 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Închide"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Extinde"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Setări"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Accesează ecranul împărțit"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Meniu"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Meniu picture-in-picture"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> este în modul picture-in-picture"</string>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index 26b0f94eb585..49347d2d8086 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Закрыть"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Развернуть"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Настройки"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Включить разделение экрана"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Меню"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Меню \"Картинка в картинке\""</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> находится в режиме \"Картинка в картинке\""</string>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
index 9b9a430ce73f..e5a974683e49 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"වසන්න"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"දිග හරින්න"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"සැකසීම්"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"බෙදුම් තිරයට ඇතුළු වන්න"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"මෙනුව"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"පින්තූරය තුළ පින්තූරය මෙනුව"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> පින්තූරය-තුළ-පින්තූරය තුළ වේ"</string>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
index 4b2153180cbe..c2d20ddb0d3b 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Zavrieť"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Rozbaliť"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Nastavenia"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Prejsť na rozdelenú obrazovku"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Ponuka"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Ponuka obrazu v obraze"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> je v režime obraz v obraze"</string>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index 581cf5b815c6..cfe4480c6e1a 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Zapri"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Razširi"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Nastavitve"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Vklopi razdeljen zaslon"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Meni"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Meni za sliko v sliki"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> je v načinu slika v sliki"</string>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index 9dc7b7ebef99..cba98c2fb61a 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Mbyll"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Zgjero"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Cilësimet"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Hyr në ekranin e ndarë"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menyja"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menyja e \"Figurës brenda figurës\""</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> është në figurë brenda figurës"</string>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
index cd532f79dd78..5031f5bf5d64 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Затвори"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Прошири"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Подешавања"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Уђи на подељени екран"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Мени"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Мени слике у слици."</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> је слика у слици"</string>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
index 386dda7e088d..742be37b67ef 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Stäng"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Utöka"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Inställningar"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Starta delad skärm"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Meny"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Bild-i-bild-meny"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> visas i bild-i-bild"</string>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
index 69b2e34ada3c..68a7262d0eaf 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Funga"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Panua"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Mipangilio"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Weka skrini iliyogawanywa"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menyu"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menyu ya kipengele cha Kupachika Picha ndani ya Picha nyingine."</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> iko katika hali ya picha ndani ya picha nyingine"</string>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
index 037b5aba22f5..fe8fa057dc95 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"மூடு"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"விரி"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"அமைப்புகள்"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"திரைப் பிரிப்பு பயன்முறைக்குச் செல்"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"மெனு"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"பிக்ச்சர்-இன்-பிக்ச்சர் மெனு"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> தற்போது பிக்ச்சர்-இன்-பிக்ச்சரில் உள்ளது"</string>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index 694ecb951210..593c8fc28a0d 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"మూసివేయి"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"విస్తరింపజేయి"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"సెట్టింగ్‌లు"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"స్ప్లిట్ స్క్రీన్‌ను ఎంటర్ చేయండి"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"మెనూ"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"పిక్చర్-ఇన్-పిక్చర్ మెనూ"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> చిత్రంలో చిత్రం రూపంలో ఉంది"</string>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index d4b6aff2ee7d..cd3bf6a626c0 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"ปิด"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"ขยาย"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"การตั้งค่า"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"เข้าสู่โหมดแบ่งหน้าจอ"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"เมนู"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"เมนูการแสดงภาพซ้อนภาพ"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> ใช้การแสดงภาพซ้อนภาพ"</string>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index db9303c0fd9c..bf05e149ea57 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Isara"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Palawakin"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Mga Setting"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Pumasok sa split screen"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Menu ng Picture-in-Picture"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"Nasa picture-in-picture ang <xliff:g id="NAME">%s</xliff:g>"</string>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
index 818666c79973..2dfa38a76dfa 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Kapat"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Genişlet"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Ayarlar"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Bölünmüş ekrana geç"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menü"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Pencere içinde pencere menüsü"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g>, pencere içinde pencere özelliğini kullanıyor"</string>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
index 85fb8e114476..57ca64f5b159 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Закрити"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Розгорнути"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Налаштування"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Розділити екран"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Меню"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Меню \"Картинка в картинці\""</string>
<string name="pip_notification_title" msgid="1347104727641353453">"У додатку <xliff:g id="NAME">%s</xliff:g> є функція \"Картинка в картинці\""</string>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index 813870b134b4..077037373aa2 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"بند کریں"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"پھیلائیں"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"ترتیبات"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"اسپلٹ اسکرین تک رسائی"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"مینیو"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"تصویر میں تصویر کا مینیو"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> تصویر میں تصویر میں ہے"</string>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
index 7bcacbb93f1f..e2d1f47c210b 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Yopish"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Yoyish"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Sozlamalar"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Ajratilgan ekranga kirish"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menyu"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Tasvir ustida tasvir menyusi"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> tasvir ustida tasvir rejimida"</string>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
index 416dd91162c2..4608b2b10c3f 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Đóng"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Mở rộng"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Cài đặt"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Truy cập chế độ chia đôi màn hình"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Menu"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Trình đơn hình trong hình."</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> đang ở chế độ ảnh trong ảnh"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
index 9d4e6f0ce660..cbb857c58611 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"关闭"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"展开"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"设置"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"进入分屏模式"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"菜单"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"画中画菜单"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g>目前位于“画中画”中"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
index b5b94ec40fd1..d89b2c29216e 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"關閉"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"展開"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"設定"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"進入分割螢幕"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"選單"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"畫中畫選單"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"「<xliff:g id="NAME">%s</xliff:g>」目前在畫中畫模式"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
index 0f2a052dbbe0..4ce50a44e12a 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"關閉"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"展開"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"設定"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"進入分割畫面"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"選單"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"子母畫面選單"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"「<xliff:g id="NAME">%s</xliff:g>」目前在子母畫面中"</string>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
index a696f9ec6251..fa680f6eee89 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -20,7 +20,6 @@
<string name="pip_phone_close" msgid="5783752637260411309">"Vala"</string>
<string name="pip_phone_expand" msgid="2579292903468287504">"Nweba"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Amasethingi"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Faka ukuhlukanisa isikrini"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Imenyu"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Imenyu Yesithombe-Esithombeni"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"U-<xliff:g id="NAME">%s</xliff:g> ungaphakathi kwesithombe esiphakathi kwesithombe"</string>
diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml
index fae71efe3b39..758dbfd5f3c5 100644
--- a/libs/WindowManager/Shell/res/values/colors.xml
+++ b/libs/WindowManager/Shell/res/values/colors.xml
@@ -66,4 +66,9 @@
<color name="desktop_mode_maximize_menu_button_outline">#797869</color>
<color name="desktop_mode_maximize_menu_button_outline_on_hover">#606219</color>
<color name="desktop_mode_maximize_menu_button_on_hover">#E7E790</color>
+ <color name="desktop_mode_maximize_menu_progress_light">#33000000</color>
+ <color name="desktop_mode_maximize_menu_progress_dark">#33FFFFFF</color>
+ <color name="desktop_mode_caption_button_on_hover_light">#11000000</color>
+ <color name="desktop_mode_caption_button_on_hover_dark">#11FFFFFF</color>
+ <color name="desktop_mode_caption_button">#00000000</color>
</resources>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index f73775becac9..cbfa74e9332b 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -244,6 +244,8 @@
<dimen name="bubble_popup_padding">24dp</dimen>
<!-- The size of the caption bar inset at the top of bubble bar expanded view. -->
<dimen name="bubble_bar_expanded_view_caption_height">32dp</dimen>
+ <!-- The width of the caption bar at the top of bubble bar expanded view. -->
+ <dimen name="bubble_bar_expanded_view_caption_width">128dp</dimen>
<!-- The height of the dots shown for the caption menu in the bubble bar expanded view.. -->
<dimen name="bubble_bar_expanded_view_caption_dot_size">4dp</dimen>
<!-- The spacing between the dots for the caption menu in the bubble bar expanded view.. -->
@@ -501,6 +503,17 @@
fullscreen if dragged until the top bound of the task is within the area. -->
<dimen name="desktop_mode_transition_area_height">16dp</dimen>
+ <!-- The width of the area where a desktop task will transition to fullscreen. -->
+ <dimen name="desktop_mode_fullscreen_from_desktop_width">80dp</dimen>
+
+ <!-- The height of the area where a desktop task will transition to fullscreen. -->
+ <dimen name="desktop_mode_fullscreen_from_desktop_height">40dp</dimen>
+
+ <!-- The height on the screen where drag to the left or right edge will result in a
+ desktop task snapping to split size. The empty space between this and the top is to allow
+ for corner drags without transition. -->
+ <dimen name="desktop_mode_split_from_desktop_height">100dp</dimen>
+
<!-- The acceptable area ratio of fg icon area/bg icon area, i.e. (72 x 72) / (108 x 108) -->
<item type="dimen" format="float" name="splash_icon_enlarge_foreground_threshold">0.44</item>
<!-- Scaling factor applied to splash icons without provided background i.e. (192 / 160) -->
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java
index 88525aabe53b..93893e33d2d5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java
@@ -16,7 +16,10 @@
package com.android.wm.shell;
-import com.android.wm.shell.protolog.ShellProtoLogImpl;
+import com.android.internal.protolog.LegacyProtoLogImpl;
+import com.android.internal.protolog.common.ILogger;
+import com.android.internal.protolog.common.IProtoLog;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellInit;
@@ -24,19 +27,19 @@ import java.io.PrintWriter;
import java.util.Arrays;
/**
- * Controls the {@link ShellProtoLogImpl} in WMShell via adb shell commands.
+ * Controls the {@link ProtoLog} in WMShell via adb shell commands.
*
* Use with {@code adb shell dumpsys activity service SystemUIService WMShell protolog ...}.
*/
public class ProtoLogController implements ShellCommandHandler.ShellCommandActionHandler {
private final ShellCommandHandler mShellCommandHandler;
- private final ShellProtoLogImpl mShellProtoLog;
+ private final IProtoLog mShellProtoLog;
public ProtoLogController(ShellInit shellInit,
ShellCommandHandler shellCommandHandler) {
shellInit.addInitCallback(this::onInit, this);
mShellCommandHandler = shellCommandHandler;
- mShellProtoLog = ShellProtoLogImpl.getSingleInstance();
+ mShellProtoLog = ProtoLog.getSingleInstance();
}
void onInit() {
@@ -45,22 +48,35 @@ public class ProtoLogController implements ShellCommandHandler.ShellCommandActio
@Override
public boolean onShellCommand(String[] args, PrintWriter pw) {
+ final ILogger logger = pw::println;
switch (args[0]) {
case "status": {
- pw.println(mShellProtoLog.getStatus());
+ if (android.tracing.Flags.perfettoProtolog()) {
+ pw.println("(Deprecated) legacy command. Use Perfetto commands instead.");
+ return false;
+ }
+ ((LegacyProtoLogImpl) mShellProtoLog).getStatus();
return true;
}
case "start": {
- mShellProtoLog.startProtoLog(pw);
+ if (android.tracing.Flags.perfettoProtolog()) {
+ pw.println("(Deprecated) legacy command. Use Perfetto commands instead.");
+ return false;
+ }
+ ((LegacyProtoLogImpl) mShellProtoLog).startProtoLog(pw);
return true;
}
case "stop": {
- mShellProtoLog.stopProtoLog(pw, true /* writeToFile */);
+ if (android.tracing.Flags.perfettoProtolog()) {
+ pw.println("(Deprecated) legacy command. Use Perfetto commands instead.");
+ return false;
+ }
+ ((LegacyProtoLogImpl) mShellProtoLog).stopProtoLog(pw, true);
return true;
}
case "enable-text": {
String[] groups = Arrays.copyOfRange(args, 1, args.length);
- int result = mShellProtoLog.startTextLogging(groups, pw);
+ int result = mShellProtoLog.startLoggingToLogcat(groups, logger);
if (result == 0) {
pw.println("Starting logging on groups: " + Arrays.toString(groups));
return true;
@@ -69,7 +85,7 @@ public class ProtoLogController implements ShellCommandHandler.ShellCommandActio
}
case "disable-text": {
String[] groups = Arrays.copyOfRange(args, 1, args.length);
- int result = mShellProtoLog.stopTextLogging(groups, pw);
+ int result = mShellProtoLog.stopLoggingToLogcat(groups, logger);
if (result == 0) {
pw.println("Stopping logging on groups: " + Arrays.toString(groups));
return true;
@@ -78,19 +94,23 @@ public class ProtoLogController implements ShellCommandHandler.ShellCommandActio
}
case "enable": {
String[] groups = Arrays.copyOfRange(args, 1, args.length);
- return mShellProtoLog.startTextLogging(groups, pw) == 0;
+ return mShellProtoLog.startLoggingToLogcat(groups, logger) == 0;
}
case "disable": {
String[] groups = Arrays.copyOfRange(args, 1, args.length);
- return mShellProtoLog.stopTextLogging(groups, pw) == 0;
+ return mShellProtoLog.stopLoggingToLogcat(groups, logger) == 0;
}
case "save-for-bugreport": {
+ if (android.tracing.Flags.perfettoProtolog()) {
+ pw.println("(Deprecated) legacy command");
+ return false;
+ }
if (!mShellProtoLog.isProtoEnabled()) {
pw.println("Logging to proto is not enabled for WMShell.");
return false;
}
- mShellProtoLog.stopProtoLog(pw, true /* writeToFile */);
- mShellProtoLog.startProtoLog(pw);
+ ((LegacyProtoLogImpl) mShellProtoLog).stopProtoLog(pw, true /* writeToFile */);
+ ((LegacyProtoLogImpl) mShellProtoLog).startProtoLog(pw);
return true;
}
default: {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index fe65fdd30e48..d8d0d876b4f2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -643,19 +643,6 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
}
}
- /** Helper to set int metadata on the Surface corresponding to the task id. */
- public void setSurfaceMetadata(int taskId, int key, int value) {
- synchronized (mLock) {
- final TaskAppearedInfo info = mTasks.get(taskId);
- if (info == null || info.getLeash() == null) {
- return;
- }
- SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- t.setMetadata(info.getLeash(), key, value);
- t.apply();
- }
- }
-
private boolean updateTaskListenerIfNeeded(RunningTaskInfo taskInfo, SurfaceControl leash,
TaskListener oldListener, TaskListener newListener) {
if (oldListener == newListener) return false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java
index 2ec9e8b12fc6..19963675ff86 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java
@@ -71,6 +71,13 @@ public class Interpolators {
*/
public static final Interpolator EMPHASIZED_DECELERATE = new PathInterpolator(
0.05f, 0.7f, 0.1f, 1f);
+
+ /**
+ * The standard decelerating interpolator that should be used on every regular movement of
+ * content that is appearing e.g. when coming from off screen.
+ */
+ public static final Interpolator STANDARD_DECELERATE = new PathInterpolator(0f, 0f, 0f, 1f);
+
/**
* Interpolator to be used when animating a move based on a click. Pair with enough duration.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index e7f6f0d61847..2606fb661e80 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -114,6 +114,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
/** Tracks if we should start the back gesture on the next motion move event */
private boolean mShouldStartOnNextMoveEvent = false;
private boolean mOnBackStartDispatched = false;
+ private boolean mPointerPilfered = false;
private final FlingAnimationUtils mFlingAnimationUtils;
@@ -404,11 +405,12 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
@VisibleForTesting
void onPilferPointers() {
- mCurrentTracker.updateStartLocation();
+ mPointerPilfered = true;
// Dispatch onBackStarted, only to app callbacks.
// System callbacks will receive onBackStarted when the remote animation starts.
if (!shouldDispatchToAnimator() && mActiveCallback != null) {
- tryDispatchAppOnBackStarted(mActiveCallback, mCurrentTracker.createStartEvent(null));
+ mCurrentTracker.updateStartLocation();
+ tryDispatchOnBackStarted(mActiveCallback, mCurrentTracker.createStartEvent(null));
}
}
@@ -511,7 +513,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mActiveCallback = mBackNavigationInfo.getOnBackInvokedCallback();
// App is handling back animation. Cancel system animation latency tracking.
cancelLatencyTracking();
- tryDispatchAppOnBackStarted(mActiveCallback, touchTracker.createStartEvent(null));
+ tryDispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent(null));
}
}
@@ -555,14 +557,13 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
&& mBackNavigationInfo.isPrepareRemoteAnimation();
}
- private void tryDispatchAppOnBackStarted(
+ private void tryDispatchOnBackStarted(
IOnBackInvokedCallback callback,
BackMotionEvent backEvent) {
- if (mOnBackStartDispatched && callback != null) {
+ if (mOnBackStartDispatched || callback == null || !mPointerPilfered) {
return;
}
dispatchOnBackStarted(callback, backEvent);
- mOnBackStartDispatched = true;
}
private void dispatchOnBackStarted(
@@ -573,6 +574,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
try {
callback.onBackStarted(backEvent);
+ mOnBackStartDispatched = true;
} catch (RemoteException e) {
Log.e(TAG, "dispatchOnBackStarted error: ", e);
}
@@ -872,6 +874,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mActiveCallback = null;
mShouldStartOnNextMoveEvent = false;
mOnBackStartDispatched = false;
+ mPointerPilfered = false;
mShellBackAnimationRegistry.resetDefaultCrossActivity();
cancelLatencyTracking();
if (mBackNavigationInfo != null) {
@@ -957,15 +960,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mCurrentTracker.updateStartLocation();
BackMotionEvent startEvent =
mCurrentTracker.createStartEvent(apps[0]);
- // {@code mActiveCallback} is the callback from
- // the BackAnimationRunners and not a real app-side
- // callback. We also dispatch to the app-side callback
- // (which should be a system callback with PRIORITY_SYSTEM)
- // to keep consistent with app registered callbacks.
dispatchOnBackStarted(mActiveCallback, startEvent);
- tryDispatchAppOnBackStarted(
- mBackNavigationInfo.getOnBackInvokedCallback(),
- startEvent);
}
// Dispatch the first progress after animation start for
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.java
index 55982dca79b3..d6f7c367f772 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.java
@@ -40,7 +40,6 @@ import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
-import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.window.BackEvent;
import android.window.BackMotionEvent;
@@ -51,6 +50,7 @@ import com.android.internal.dynamicanimation.animation.SpringAnimation;
import com.android.internal.dynamicanimation.animation.SpringForce;
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.common.annotations.ShellMainThread;
import javax.inject.Inject;
@@ -65,7 +65,7 @@ public class CrossActivityBackAnimation extends ShellBackAnimation {
/** Duration of post animation after gesture committed. */
private static final int POST_ANIMATION_DURATION = 350;
- private static final Interpolator INTERPOLATOR = new DecelerateInterpolator();
+ private static final Interpolator INTERPOLATOR = Interpolators.STANDARD_DECELERATE;
private static final FloatProperty<CrossActivityBackAnimation> ENTER_PROGRESS_PROP =
new FloatProperty<>("enter-alpha") {
@Override
@@ -285,7 +285,7 @@ public class CrossActivityBackAnimation extends ShellBackAnimation {
ValueAnimator valueAnimator =
ValueAnimator.ofFloat(1f, 0f).setDuration(POST_ANIMATION_DURATION);
- valueAnimator.setInterpolator(new DecelerateInterpolator());
+ valueAnimator.setInterpolator(INTERPOLATOR);
valueAnimator.addUpdateListener(animation -> {
float progress = animation.getAnimatedFraction();
updatePostCommitEnteringAnimation(progress);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
index adc78391f033..4b3154190910 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
@@ -91,7 +91,8 @@ public class CrossTaskBackAnimation extends ShellBackAnimation {
private final PointF mInitialTouchPos = new PointF();
private final Interpolator mPostAnimationInterpolator = Interpolators.EMPHASIZED;
- private final Interpolator mProgressInterpolator = new DecelerateInterpolator();
+ private final Interpolator mProgressInterpolator = Interpolators.STANDARD_DECELERATE;
+ private final Interpolator mVerticalMoveInterpolator = new DecelerateInterpolator();
private final Matrix mTransformMatrix = new Matrix();
private final float[] mTmpFloat9 = new float[9];
@@ -169,7 +170,7 @@ public class CrossTaskBackAnimation extends ShellBackAnimation {
float yDirection = rawYDelta < 0 ? -1 : 1;
// limit yDelta interpretation to 1/2 of screen height in either direction
float deltaYRatio = Math.min(height / 2f, Math.abs(rawYDelta)) / (height / 2f);
- float interpolatedYRatio = mProgressInterpolator.getInterpolation(deltaYRatio);
+ float interpolatedYRatio = mVerticalMoveInterpolator.getInterpolation(deltaYRatio);
// limit y-shift so surface never passes 8dp screen margin
float deltaY = yDirection * interpolatedYRatio * Math.max(0f,
(height - scaledHeight) / 2f - mVerticalMargin);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 5c6f73f0a4a2..15350fb19209 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -122,6 +122,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
@@ -247,6 +248,9 @@ public class BubbleController implements ConfigurationChangeListener,
/** Saved font scale, used to detect font size changes in {@link #onConfigurationChanged}. */
private float mFontScale = 0;
+ /** Saved locale, used to detect local changes in {@link #onConfigurationChanged}. */
+ private Locale mLocale = null;
+
/** Saved direction, used to detect layout direction changes @link #onConfigChanged}. */
private int mLayoutDirection = View.LAYOUT_DIRECTION_UNDEFINED;
@@ -481,7 +485,12 @@ public class BubbleController implements ConfigurationChangeListener,
});
mOneHandedOptional.ifPresent(this::registerOneHandedState);
- mDragAndDropController.addListener(this::collapseStack);
+ mDragAndDropController.addListener(new DragAndDropController.DragAndDropListener() {
+ @Override
+ public void onDragStarted() {
+ collapseStack();
+ }
+ });
// Clear out any persisted bubbles on disk that no longer have a valid user.
List<UserInfo> users = mUserManager.getAliveUsers();
@@ -678,6 +687,17 @@ public class BubbleController implements ConfigurationChangeListener,
mDataRepository.removeBubblesForUser(removedUserId, parentUserId);
}
+ /** Called when sensitive notification state has changed */
+ public void onSensitiveNotificationProtectionStateChanged(
+ boolean sensitiveNotificationProtectionActive) {
+ if (mStackView != null) {
+ mStackView.onSensitiveNotificationProtectionStateChanged(
+ sensitiveNotificationProtectionActive);
+ ProtoLog.d(WM_SHELL_BUBBLES, "onSensitiveNotificationProtectionStateChanged=%b",
+ sensitiveNotificationProtectionActive);
+ }
+ }
+
/** Whether bubbles are showing in the bubble bar. */
public boolean isShowingAsBubbleBar() {
return canShowAsBubbleBar() && mBubbleStateListener != null;
@@ -1052,6 +1072,11 @@ public class BubbleController implements ConfigurationChangeListener,
mLayoutDirection = newConfig.getLayoutDirection();
mStackView.onLayoutDirectionChanged(mLayoutDirection);
}
+ Locale newLocale = newConfig.locale;
+ if (newLocale != null && !newLocale.equals(mLocale)) {
+ mLocale = newLocale;
+ mStackView.updateLocale();
+ }
}
}
@@ -1838,7 +1863,7 @@ public class BubbleController implements ConfigurationChangeListener,
+ " expanded=%b selectionChanged=%b selected=%s"
+ " suppressed=%s unsupressed=%s shouldShowEducation=%b",
update.addedBubble != null ? update.addedBubble.getKey() : "null",
- update.removedBubbles.isEmpty(),
+ !update.removedBubbles.isEmpty(),
update.updatedBubble != null ? update.updatedBubble.getKey() : "null",
update.orderChanged, update.expandedChanged, update.expanded,
update.selectionChanged,
@@ -2578,6 +2603,14 @@ public class BubbleController implements ConfigurationChangeListener,
mMainExecutor.execute(
() -> BubbleController.this.onNotificationPanelExpandedChanged(expanded));
}
+
+ @Override
+ public void onSensitiveNotificationProtectionStateChanged(
+ boolean sensitiveNotificationProtectionActive) {
+ mMainExecutor.execute(
+ () -> BubbleController.this.onSensitiveNotificationProtectionStateChanged(
+ sensitiveNotificationProtectionActive));
+ }
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 123693db3622..74f087b6d8f8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -517,6 +517,15 @@ public class BubbleExpandedView extends LinearLayout {
}
}
+ void updateLocale() {
+ if (mManageButton != null) {
+ mManageButton.setText(mContext.getString(R.string.manage_bubbles_text));
+ }
+ if (mOverflowView != null) {
+ mOverflowView.updateLocale();
+ }
+ }
+
void applyThemeAttrs() {
final TypedArray ta = mContext.obtainStyledAttributes(new int[]{
android.R.attr.dialogCornerRadius,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
index b06de4f4002c..633b01bde4ca 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
@@ -242,6 +242,11 @@ public class BubbleOverflowContainerView extends LinearLayout {
mEmptyStateSubtitle.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize);
}
+ public void updateLocale() {
+ mEmptyStateTitle.setText(mContext.getString(R.string.bubble_overflow_empty_title));
+ mEmptyStateSubtitle.setText(mContext.getString(R.string.bubble_overflow_empty_subtitle));
+ }
+
private final BubbleData.Listener mDataListener = new BubbleData.Listener() {
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index cda29c95281d..a5853d621cb5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -395,7 +395,7 @@ public class BubblePositioner {
public int getTaskViewContentWidth(boolean onLeft) {
int[] paddings = getExpandedViewContainerPadding(onLeft, /* isOverflow = */ false);
int pointerOffset = showBubblesVertically() ? getPointerSize() : 0;
- return mPositionRect.width() - paddings[0] - paddings[2] - pointerOffset;
+ return mScreenRect.width() - paddings[0] - paddings[2] - pointerOffset;
}
/** Gets the y position of the expanded view if it was top-aligned. */
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 b23fd5269eae..8fd6ffe15cfe 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
@@ -291,6 +291,11 @@ public class BubbleStackView extends FrameLayout
*/
private boolean mRemovingLastBubbleWhileExpanded = false;
+ /**
+ * Whether sensitive notification protection should disable flyout
+ */
+ private boolean mSensitiveNotificationProtectionActive = false;
+
/** Animator for animating the expanded view's alpha (including the TaskView inside it). */
private final ValueAnimator mExpandedViewAlphaAnimator = ValueAnimator.ofFloat(0f, 1f);
@@ -1447,6 +1452,12 @@ public class BubbleStackView extends FrameLayout
}
}
+ void updateLocale() {
+ if (mBubbleOverflow != null && mBubbleOverflow.getExpandedView() != null) {
+ mBubbleOverflow.getExpandedView().updateLocale();
+ }
+ }
+
private void updateOverflow() {
mBubbleOverflow.update();
mBubbleContainer.reorderView(mBubbleOverflow.getIconView(),
@@ -2199,6 +2210,11 @@ public class BubbleStackView extends FrameLayout
}
}
+ void onSensitiveNotificationProtectionStateChanged(
+ boolean sensitiveNotificationProtectionActive) {
+ mSensitiveNotificationProtectionActive = sensitiveNotificationProtectionActive;
+ }
+
/**
* Asks the BubbleController to hide the IME from anywhere, whether it's focused on Bubbles or
* not.
@@ -2842,6 +2858,7 @@ public class BubbleStackView extends FrameLayout
|| isExpanded()
|| mIsExpansionAnimating
|| mIsGestureInProgress
+ || mSensitiveNotificationProtectionActive
|| mBubbleToExpandAfterFlyoutCollapse != null
|| bubbleView == null) {
if (bubbleView != null && mFlyout.getVisibility() != VISIBLE) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 28af0ca6ac6c..26077cf7057b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -286,6 +286,16 @@ public interface Bubbles {
void onUserRemoved(int removedUserId);
/**
+ * Called when the Sensitive notification protection state has changed, such as when media
+ * projection starts and stops.
+ *
+ * @param sensitiveNotificationProtectionActive {@code true} if notifications should be
+ * protected
+ */
+ void onSensitiveNotificationProtectionStateChanged(
+ boolean sensitiveNotificationProtectionActive);
+
+ /**
* A listener to be notified of bubble state changes, used by launcher to render bubbles in
* its process.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/OWNERS
index 8271014d290e..08c70314973e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/OWNERS
@@ -1,2 +1,6 @@
# WM shell sub-module bubble owner
madym@google.com
+atsjenk@google.com
+liranb@google.com
+sukeshram@google.com
+mpodolian@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
index 4e995bc7c92e..8946f41e96a7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
@@ -15,6 +15,7 @@
*/
package com.android.wm.shell.bubbles.bar;
+import static android.view.View.ALPHA;
import static android.view.View.SCALE_X;
import static android.view.View.SCALE_Y;
import static android.view.View.TRANSLATION_X;
@@ -69,6 +70,7 @@ public class BubbleBarAnimationHelper {
private static final float EXPANDED_VIEW_IN_TARGET_SCALE = 0.2f;
private static final float EXPANDED_VIEW_DRAG_SCALE = 0.4f;
private static final float DISMISS_VIEW_SCALE = 1.25f;
+ private static final int HANDLE_ALPHA_ANIMATION_DURATION = 100;
/** Spring config for the expanded view scale-in animation. */
private final PhysicsAnimator.SpringConfig mScaleInSpringConfig =
@@ -248,15 +250,22 @@ public class BubbleBarAnimationHelper {
return;
}
setDragPivot(bbev);
- AnimatorSet animatorSet = new AnimatorSet();
// Corner radius gets scaled, apply the reverse scale to ensure we have the desired radius
final float cornerRadius = bbev.getDraggedCornerRadius() / EXPANDED_VIEW_DRAG_SCALE;
- animatorSet.playTogether(
+
+ AnimatorSet contentAnim = new AnimatorSet();
+ contentAnim.playTogether(
ObjectAnimator.ofFloat(bbev, SCALE_X, EXPANDED_VIEW_DRAG_SCALE),
ObjectAnimator.ofFloat(bbev, SCALE_Y, EXPANDED_VIEW_DRAG_SCALE),
ObjectAnimator.ofFloat(bbev, CORNER_RADIUS, cornerRadius)
);
- animatorSet.setDuration(EXPANDED_VIEW_DRAG_ANIMATION_DURATION).setInterpolator(EMPHASIZED);
+ contentAnim.setDuration(EXPANDED_VIEW_DRAG_ANIMATION_DURATION).setInterpolator(EMPHASIZED);
+
+ ObjectAnimator handleAnim = ObjectAnimator.ofFloat(bbev.getHandleView(), ALPHA, 0f)
+ .setDuration(HANDLE_ALPHA_ANIMATION_DURATION);
+
+ AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.playTogether(contentAnim, handleAnim);
animatorSet.addListener(new DragAnimatorListenerAdapter(bbev));
startNewDragAnimation(animatorSet);
}
@@ -297,15 +306,21 @@ public class BubbleBarAnimationHelper {
}
Point restPoint = getExpandedViewRestPosition(getExpandedViewSize());
- AnimatorSet animatorSet = new AnimatorSet();
- animatorSet.playTogether(
+ AnimatorSet contentAnim = new AnimatorSet();
+ contentAnim.playTogether(
ObjectAnimator.ofFloat(bbev, X, restPoint.x),
ObjectAnimator.ofFloat(bbev, Y, restPoint.y),
ObjectAnimator.ofFloat(bbev, SCALE_X, 1f),
ObjectAnimator.ofFloat(bbev, SCALE_Y, 1f),
ObjectAnimator.ofFloat(bbev, CORNER_RADIUS, bbev.getRestingCornerRadius())
);
- animatorSet.setDuration(EXPANDED_VIEW_ANIMATE_TO_REST_DURATION).setInterpolator(EMPHASIZED);
+ contentAnim.setDuration(EXPANDED_VIEW_ANIMATE_TO_REST_DURATION).setInterpolator(EMPHASIZED);
+
+ ObjectAnimator handleAlphaAnim = ObjectAnimator.ofFloat(bbev.getHandleView(), ALPHA, 1f)
+ .setDuration(HANDLE_ALPHA_ANIMATION_DURATION);
+
+ AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.playTogether(contentAnim, handleAlphaAnim);
animatorSet.addListener(new DragAnimatorListenerAdapter(bbev) {
@Override
public void onAnimationEnd(Animator animation) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
index eddd43f263d9..271fb9abce6a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
@@ -143,6 +143,8 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mCurrentCornerRadius);
}
});
+ // Set a touch sink to ensure that clicks on the caption area do not propagate to the parent
+ setOnTouchListener((v, event) -> true);
}
@Override
@@ -245,12 +247,8 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- int height = MeasureSpec.getSize(heightMeasureSpec);
- int menuViewHeight = Math.min(mCaptionHeight, height);
- measureChild(mHandleView, widthMeasureSpec, MeasureSpec.makeMeasureSpec(menuViewHeight,
- MeasureSpec.getMode(heightMeasureSpec)));
-
if (mTaskView != null) {
+ int height = MeasureSpec.getSize(heightMeasureSpec);
measureChild(mTaskView, widthMeasureSpec, MeasureSpec.makeMeasureSpec(height,
MeasureSpec.getMode(heightMeasureSpec)));
}
@@ -259,14 +257,11 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
- final int captionBottom = t + mCaptionHeight;
if (mTaskView != null) {
mTaskView.layout(l, t, r,
t + mTaskView.getMeasuredHeight());
mTaskView.setCaptionInsets(Insets.of(0, mCaptionHeight, 0, 0));
}
- // Handle draws on top of task view in the caption area.
- mHandleView.layout(l, t, r, captionBottom);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index 62f2726ad9bd..78a41f759d96 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -231,6 +231,7 @@ public class BubbleBarLayerView extends FrameLayout
// Touch delegate for the menu
BubbleBarHandleView view = mExpandedView.getHandleView();
view.getBoundsOnScreen(mHandleTouchBounds);
+ // Move top value up to ensure touch target is large enough
mHandleTouchBounds.top -= mPositioner.getBubblePaddingTop();
mHandleTouchDelegate = new TouchDelegate(mHandleTouchBounds,
mExpandedView.getHandleView());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
index 1959eb03a6b3..86cec02ab138 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
@@ -22,12 +22,7 @@ import static android.content.res.Configuration.UI_MODE_TYPE_CAR;
import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
import static android.os.Process.SYSTEM_UID;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
-import static android.util.RotationUtils.rotateBounds;
-import static android.util.RotationUtils.rotateInsets;
import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_270;
-import static android.view.Surface.ROTATION_90;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -40,11 +35,9 @@ import android.graphics.Rect;
import android.os.SystemProperties;
import android.provider.Settings;
import android.util.DisplayMetrics;
-import android.util.Size;
import android.view.Display;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
-import android.view.Gravity;
import android.view.InsetsState;
import android.view.Surface;
import android.view.WindowInsets;
@@ -226,25 +219,22 @@ public class DisplayLayout {
/**
* Apply a rotation to this layout and its parameters.
- * @param res
- * @param targetRotation
*/
- public void rotateTo(Resources res, @Surface.Rotation int targetRotation) {
- final int rotationDelta = (targetRotation - mRotation + 4) % 4;
- final boolean changeOrient = (rotationDelta % 2) != 0;
-
+ public void rotateTo(Resources res, @Surface.Rotation int toRotation) {
final int origWidth = mWidth;
final int origHeight = mHeight;
+ final int fromRotation = mRotation;
+ final int rotationDelta = (toRotation - fromRotation + 4) % 4;
+ final boolean changeOrient = (rotationDelta % 2) != 0;
- mRotation = targetRotation;
+ mRotation = toRotation;
if (changeOrient) {
mWidth = origHeight;
mHeight = origWidth;
}
- if (mCutout != null && !mCutout.isEmpty()) {
- mCutout = calculateDisplayCutoutForRotation(mCutout, rotationDelta, origWidth,
- origHeight);
+ if (mCutout != null) {
+ mCutout = mCutout.getRotated(origWidth, origHeight, fromRotation, toRotation);
}
recalcInsets(res);
@@ -398,96 +388,6 @@ public class DisplayLayout {
}
}
- /** Calculate the DisplayCutout for a particular display size/rotation. */
- public static DisplayCutout calculateDisplayCutoutForRotation(
- DisplayCutout cutout, int rotation, int displayWidth, int displayHeight) {
- if (cutout == null || cutout == DisplayCutout.NO_CUTOUT) {
- return null;
- }
- if (rotation == ROTATION_0) {
- return computeSafeInsets(cutout, displayWidth, displayHeight);
- }
- final Insets waterfallInsets = rotateInsets(cutout.getWaterfallInsets(), rotation);
- final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
- Rect[] cutoutRects = cutout.getBoundingRectsAll();
- final Rect[] newBounds = new Rect[cutoutRects.length];
- final Rect displayBounds = new Rect(0, 0, displayWidth, displayHeight);
- for (int i = 0; i < cutoutRects.length; ++i) {
- final Rect rect = new Rect(cutoutRects[i]);
- if (!rect.isEmpty()) {
- rotateBounds(rect, displayBounds, rotation);
- }
- newBounds[getBoundIndexFromRotation(i, rotation)] = rect;
- }
- final DisplayCutout.CutoutPathParserInfo info = cutout.getCutoutPathParserInfo();
- final DisplayCutout.CutoutPathParserInfo newInfo = new DisplayCutout.CutoutPathParserInfo(
- info.getDisplayWidth(), info.getDisplayHeight(), info.getPhysicalDisplayWidth(),
- info.getPhysicalDisplayHeight(), info.getDensity(), info.getCutoutSpec(), rotation,
- info.getScale(), info.getPhysicalPixelDisplaySizeRatio());
- return computeSafeInsets(
- DisplayCutout.constructDisplayCutout(newBounds, waterfallInsets, newInfo),
- rotated ? displayHeight : displayWidth,
- rotated ? displayWidth : displayHeight);
- }
-
- private static int getBoundIndexFromRotation(int index, int rotation) {
- return (index - rotation) < 0
- ? index - rotation + DisplayCutout.BOUNDS_POSITION_LENGTH
- : index - rotation;
- }
-
- /** Calculate safe insets. */
- public static DisplayCutout computeSafeInsets(DisplayCutout inner,
- int displayWidth, int displayHeight) {
- if (inner == DisplayCutout.NO_CUTOUT) {
- return null;
- }
-
- final Size displaySize = new Size(displayWidth, displayHeight);
- final Rect safeInsets = computeSafeInsets(displaySize, inner);
- return inner.replaceSafeInsets(safeInsets);
- }
-
- private static Rect computeSafeInsets(
- Size displaySize, DisplayCutout cutout) {
- if (displaySize.getWidth() == displaySize.getHeight()) {
- throw new UnsupportedOperationException("not implemented: display=" + displaySize
- + " cutout=" + cutout);
- }
-
- int leftInset = Math.max(cutout.getWaterfallInsets().left,
- findCutoutInsetForSide(displaySize, cutout.getBoundingRectLeft(), Gravity.LEFT));
- int topInset = Math.max(cutout.getWaterfallInsets().top,
- findCutoutInsetForSide(displaySize, cutout.getBoundingRectTop(), Gravity.TOP));
- int rightInset = Math.max(cutout.getWaterfallInsets().right,
- findCutoutInsetForSide(displaySize, cutout.getBoundingRectRight(), Gravity.RIGHT));
- int bottomInset = Math.max(cutout.getWaterfallInsets().bottom,
- findCutoutInsetForSide(displaySize, cutout.getBoundingRectBottom(),
- Gravity.BOTTOM));
-
- return new Rect(leftInset, topInset, rightInset, bottomInset);
- }
-
- private static int findCutoutInsetForSide(Size display, Rect boundingRect, int gravity) {
- if (boundingRect.isEmpty()) {
- return 0;
- }
-
- int inset = 0;
- switch (gravity) {
- case Gravity.TOP:
- return Math.max(inset, boundingRect.bottom);
- case Gravity.BOTTOM:
- return Math.max(inset, display.getHeight() - boundingRect.top);
- case Gravity.LEFT:
- return Math.max(inset, boundingRect.right);
- case Gravity.RIGHT:
- return Math.max(inset, display.getWidth() - boundingRect.left);
- default:
- throw new IllegalArgumentException("unknown gravity: " + gravity);
- }
- }
-
static boolean hasNavigationBar(DisplayInfo info, Context context, int displayId) {
if (displayId == Display.DEFAULT_DISPLAY) {
// Allow a system property to override this. Used by the emulator.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt
new file mode 100644
index 000000000000..4c34971c4fb1
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2024 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.common
+
+import android.app.PendingIntent
+import android.content.ComponentName
+import android.content.Context
+import android.content.pm.LauncherApps
+import android.content.pm.PackageManager
+import android.os.UserHandle
+import android.view.WindowManager
+import android.view.WindowManager.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI
+import com.android.internal.annotations.VisibleForTesting
+import com.android.wm.shell.R
+import com.android.wm.shell.protolog.ShellProtoLogGroup
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL
+import com.android.wm.shell.util.KtProtoLog
+import java.util.Arrays
+
+/**
+ * Helper for multi-instance related checks.
+ */
+class MultiInstanceHelper @JvmOverloads constructor(
+ private val context: Context,
+ private val packageManager: PackageManager,
+ private val staticAppsSupportingMultiInstance: Array<String> = context.resources
+ .getStringArray(R.array.config_appsSupportMultiInstancesSplit)) {
+
+ /**
+ * Returns whether a specific component desires to be launched in multiple instances.
+ */
+ @VisibleForTesting
+ fun supportsMultiInstanceSplit(componentName: ComponentName?): Boolean {
+ if (componentName == null || componentName.packageName == null) {
+ // TODO(b/262864589): Handle empty component case
+ return false
+ }
+
+ // Check the pre-defined allow list
+ val packageName = componentName.packageName
+ for (pkg in staticAppsSupportingMultiInstance) {
+ if (pkg == packageName) {
+ KtProtoLog.v(WM_SHELL, "application=%s in allowlist supports multi-instance",
+ packageName)
+ return true
+ }
+ }
+
+ // Check the activity property first
+ try {
+ val activityProp = packageManager.getProperty(
+ PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, componentName)
+ // If the above call doesn't throw a NameNotFoundException, then the activity property
+ // should override the application property value
+ if (activityProp.isBoolean) {
+ KtProtoLog.v(WM_SHELL, "activity=%s supports multi-instance", componentName)
+ return activityProp.boolean
+ } else {
+ KtProtoLog.w(WM_SHELL, "Warning: property=%s for activity=%s has non-bool type=%d",
+ PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName, activityProp.type)
+ }
+ } catch (nnfe: PackageManager.NameNotFoundException) {
+ // Not specified in the activity, fall through
+ }
+
+ // Check the application property otherwise
+ try {
+ val appProp = packageManager.getProperty(
+ PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName)
+ if (appProp.isBoolean) {
+ KtProtoLog.v(WM_SHELL, "application=%s supports multi-instance", packageName)
+ return appProp.boolean
+ } else {
+ KtProtoLog.w(WM_SHELL,
+ "Warning: property=%s for application=%s has non-bool type=%d",
+ PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName, appProp.type)
+ }
+ } catch (nnfe: PackageManager.NameNotFoundException) {
+ // Not specified in either application or activity
+ }
+ return false
+ }
+
+ companion object {
+ /** Returns the component from a PendingIntent */
+ @JvmStatic
+ fun getComponent(pendingIntent: PendingIntent?): ComponentName? {
+ return pendingIntent?.intent?.component
+ }
+
+ /** Returns the component from a shortcut */
+ @JvmStatic
+ fun getShortcutComponent(packageName: String, shortcutId: String,
+ user: UserHandle, launcherApps: LauncherApps): ComponentName? {
+ val query = LauncherApps.ShortcutQuery()
+ query.setPackage(packageName)
+ query.setShortcutIds(Arrays.asList(shortcutId))
+ query.setQueryFlags(LauncherApps.ShortcutQuery.FLAG_MATCH_ALL_KINDS_WITH_ALL_PINNED)
+ val shortcuts = launcherApps.getShortcuts(query, user)
+ val info = if (shortcuts != null && shortcuts.size > 0) shortcuts[0] else null
+ return info?.activity
+ }
+
+ /** Returns true if package names and user ids match. */
+ @JvmStatic
+ fun samePackage(packageName1: String?, packageName2: String?,
+ userId1: Int, userId2: Int): Boolean {
+ return (packageName1 != null && packageName1 == packageName2) && (userId1 == userId2)
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPip.aidl
index 8b3de6298b2a..b5f25433f9aa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPip.aidl
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.pip;
+package com.android.wm.shell.common.pip;
import android.app.PictureInPictureParams;
import android.view.SurfaceControl;
@@ -22,7 +22,7 @@ import android.content.ComponentName;
import android.content.pm.ActivityInfo;
import android.graphics.Rect;
-import com.android.wm.shell.pip.IPipAnimationListener;
+import com.android.wm.shell.common.pip.IPipAnimationListener;
/**
* Interface that is exposed to remote callers to manipulate the Pip feature.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPipAnimationListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPipAnimationListener.aidl
index 062e3ba26356..b8d1966100a4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPipAnimationListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPipAnimationListener.aidl
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.pip;
+package com.android.wm.shell.common.pip;
/**
* Listener interface that Launcher attaches to SystemUI to get Pip animation callbacks.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java
index a9f687fc9b2d..6ffeb97f50fa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java
@@ -125,11 +125,15 @@ public class PipBoundsAlgorithm {
public Rect getEntryDestinationBoundsIgnoringKeepClearAreas() {
final PipBoundsState.PipReentryState reentryState = mPipBoundsState.getReentryState();
- final Rect destinationBounds = reentryState != null
- ? getDefaultBounds(reentryState.getSnapFraction(), reentryState.getSize())
- : getDefaultBounds();
+ final Rect destinationBounds = getDefaultBounds();
+ if (reentryState != null) {
+ final Size scaledBounds = new Size(
+ Math.round(mPipBoundsState.getMaxSize().x * reentryState.getBoundsScale()),
+ Math.round(mPipBoundsState.getMaxSize().y * reentryState.getBoundsScale()));
+ destinationBounds.set(getDefaultBounds(reentryState.getSnapFraction(), scaledBounds));
+ }
- final boolean useCurrentSize = reentryState != null && reentryState.getSize() != null;
+ final boolean useCurrentSize = reentryState != null;
Rect aspectRatioBounds = transformBoundsToAspectRatioIfValid(destinationBounds,
mPipBoundsState.getAspectRatio(), false /* useCurrentMinEdgeSize */,
useCurrentSize);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
index df589df8b227..b57e2d2c6ac2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
@@ -298,8 +298,8 @@ public class PipBoundsState {
}
/** Save the reentry state to restore to when re-entering PIP mode. */
- public void saveReentryState(Size size, float fraction) {
- mPipReentryState = new PipReentryState(size, fraction);
+ public void saveReentryState(float fraction) {
+ mPipReentryState = new PipReentryState(mBoundsScale, fraction);
}
/** Returns the saved reentry state. */
@@ -601,17 +601,16 @@ public class PipBoundsState {
public static final class PipReentryState {
private static final String TAG = PipReentryState.class.getSimpleName();
- private final @Nullable Size mSize;
private final float mSnapFraction;
+ private final float mBoundsScale;
- PipReentryState(@Nullable Size size, float snapFraction) {
- mSize = size;
+ PipReentryState(float boundsScale, float snapFraction) {
+ mBoundsScale = boundsScale;
mSnapFraction = snapFraction;
}
- @Nullable
- public Size getSize() {
- return mSize;
+ public float getBoundsScale() {
+ return mBoundsScale;
}
public float getSnapFraction() {
@@ -621,7 +620,7 @@ public class PipBoundsState {
void dump(PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.println(prefix + TAG);
- pw.println(innerPrefix + "mSize=" + mSize);
+ pw.println(innerPrefix + "mBoundsScale=" + mBoundsScale);
pw.println(innerPrefix + "mSnapFraction=" + mSnapFraction);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipPerfHintController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipPerfHintController.java
new file mode 100644
index 000000000000..317e48e19c13
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipPerfHintController.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2024 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.common.pip;
+
+import static android.window.SystemPerformanceHinter.HINT_SF;
+
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE;
+
+import android.window.SystemPerformanceHinter;
+import android.window.SystemPerformanceHinter.HighPerfSession;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.annotations.ShellMainThread;
+
+import java.io.PrintWriter;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.function.Consumer;
+
+/**
+ * Manages system performance hints for PiP CUJs and interactions.
+ */
+public class PipPerfHintController {
+ private static final String TAG = PipPerfHintController.class.getSimpleName();
+
+ // Delay until signal about a session cleanup is sent.
+ private static final int SESSION_TIMEOUT_DELAY = 20_000;
+
+ // Maximum number of possible high perf session.
+ private static final int SESSION_POOL_SIZE = 20;
+
+ private final SystemPerformanceHinter mSystemPerformanceHinter;
+ @NonNull
+ private final PipDisplayLayoutState mPipDisplayLayoutState;
+ @NonNull
+ private final ShellExecutor mMainExecutor;
+
+
+ public PipPerfHintController(@NonNull PipDisplayLayoutState pipDisplayLayoutState,
+ @ShellMainThread ShellExecutor mainExecutor,
+ @NonNull SystemPerformanceHinter systemPerformanceHinter) {
+ mPipDisplayLayoutState = pipDisplayLayoutState;
+ mMainExecutor = mainExecutor;
+ mSystemPerformanceHinter = systemPerformanceHinter;
+ }
+
+ /**
+ * Starts a high perf session.
+ *
+ * @param timeoutCallback an optional callback to be executed upon session timeout.
+ * @return a wrapper around the session to allow for early closing; null if no free sessions
+ * left available in the pool.
+ */
+ @Nullable
+ public PipHighPerfSession startSession(@Nullable Consumer<PipHighPerfSession> timeoutCallback,
+ String reason) {
+ if (PipHighPerfSession.getActiveSessionsCount() == SESSION_POOL_SIZE) {
+ return null;
+ }
+
+ HighPerfSession highPerfSession = mSystemPerformanceHinter.startSession(HINT_SF,
+ mPipDisplayLayoutState.getDisplayId(), "pip-high-perf-session");
+ PipHighPerfSession pipHighPerfSession = new PipHighPerfSession(highPerfSession, reason);
+
+ if (timeoutCallback != null) {
+ mMainExecutor.executeDelayed(() -> {
+ if (PipHighPerfSession.hasClosedOrFinalized(pipHighPerfSession)) {
+ // If the session is either directly closed or GC collected before timeout
+ // was reached, do not send the timeout callback.
+ return;
+ }
+ // The session hasn't been closed yet, so do that now, along with any cleanup.
+ pipHighPerfSession.close();
+ ProtoLog.d(WM_SHELL_PICTURE_IN_PICTURE, "%s: high perf session %s timed out", TAG,
+ pipHighPerfSession.toString());
+ timeoutCallback.accept(pipHighPerfSession);
+ }, SESSION_TIMEOUT_DELAY);
+ }
+ ProtoLog.d(WM_SHELL_PICTURE_IN_PICTURE, "%s: high perf session %s is started",
+ TAG, pipHighPerfSession.toString());
+ return pipHighPerfSession;
+ }
+
+ /**
+ * Dumps the inner state.
+ */
+ public void dump(PrintWriter pw, String prefix) {
+ final String innerPrefix = prefix + " ";
+ pw.println(prefix + TAG);
+ pw.println(innerPrefix + "activeSessionCount="
+ + PipHighPerfSession.getActiveSessionsCount());
+ }
+
+ /**
+ * A wrapper around {@link HighPerfSession} to keep track of some extra metadata about
+ * the session's status.
+ */
+ public class PipHighPerfSession implements AutoCloseable{
+
+ // THe actual HighPerfSession we wrap around.
+ private final HighPerfSession mSession;
+
+ private final String mReason;
+
+ /**
+ * Keeps track of all active sessions using weakly referenced keys.
+ * This makes sure that that sessions do not get accidentally leaked if not closed.
+ */
+ private static Map<PipHighPerfSession, Boolean> sActiveSessions = new WeakHashMap<>();
+
+ private PipHighPerfSession(HighPerfSession session, String reason) {
+ mSession = session;
+ mReason = reason;
+ sActiveSessions.put(this, true);
+ }
+
+ /**
+ * Closes a high perf session.
+ */
+ @Override
+ public void close() {
+ sActiveSessions.remove(this);
+ mSession.close();
+ ProtoLog.d(WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: high perf session %s is closed",
+ TAG, toString());
+ }
+
+ @Override
+ public void finalize() {
+ // The entry should be removed from the weak hash map as well by default.
+ mSession.close();
+ }
+
+ @Override
+ public String toString() {
+ return "[" + super.toString() + "] initially started due to: " + mReason;
+ }
+
+ private static boolean hasClosedOrFinalized(PipHighPerfSession pipHighPerfSession) {
+ return !sActiveSessions.containsKey(pipHighPerfSession);
+ }
+
+ private static int getActiveSessionsCount() {
+ return sActiveSessions.size();
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
index 662f325be38c..f9259e79472e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
@@ -99,12 +99,6 @@ public class SplitScreenUtils {
return taskInfo != null ? taskInfo.userId : -1;
}
- /** Returns true if package names and user ids match. */
- public static boolean samePackage(String packageName1, String packageName2,
- int userId1, int userId2) {
- return (packageName1 != null && packageName1.equals(packageName2)) && (userId1 == userId2);
- }
-
/** Generates a common log message for split screen failures */
public static String splitFailureMessage(String caller, String reason) {
return "(" + caller + ") Splitscreen aborted: " + reason;
@@ -143,28 +137,4 @@ public class SplitScreenUtils {
return isLandscape;
}
}
-
- /** Returns the component from a PendingIntent */
- @Nullable
- public static ComponentName getComponent(@Nullable PendingIntent pendingIntent) {
- if (pendingIntent == null || pendingIntent.getIntent() == null) {
- return null;
- }
- return pendingIntent.getIntent().getComponent();
- }
-
- /** Returns the component from a shortcut */
- @Nullable
- public static ComponentName getShortcutComponent(@NonNull String packageName, String shortcutId,
- @NonNull UserHandle user, @NonNull LauncherApps launcherApps) {
- LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery();
- query.setPackage(packageName);
- query.setShortcutIds(Arrays.asList(shortcutId));
- query.setQueryFlags(FLAG_MATCH_ALL_KINDS_WITH_ALL_PINNED);
- List<ShortcutInfo> shortcuts = launcherApps.getShortcuts(query, user);
- ShortcutInfo info = shortcuts != null && shortcuts.size() > 0
- ? shortcuts.get(0)
- : null;
- return info != null ? info.getActivity() : null;
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
index 81d13999e73c..7c280994042b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
@@ -20,6 +20,7 @@ import static android.window.TaskConstants.TASK_CHILD_LAYER_COMPAT_UI;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.AppCompatTaskInfo;
import android.app.TaskInfo;
import android.content.Context;
import android.content.Intent;
@@ -88,7 +89,8 @@ class UserAspectRatioSettingsWindowManager extends CompatUIWindowManagerAbstract
mShellExecutor = shellExecutor;
mUserAspectRatioButtonShownChecker = userAspectRatioButtonStateChecker;
mUserAspectRatioButtonStateConsumer = userAspectRatioButtonShownConsumer;
- mHasUserAspectRatioSettingsButton = getHasUserAspectRatioSettingsButton(taskInfo);
+ mHasUserAspectRatioSettingsButton = shouldShowUserAspectRatioSettingsButton(
+ taskInfo.appCompatTaskInfo, taskInfo.baseIntent);
mCompatUIHintsState = compatUIHintsState;
mOnButtonClicked = onButtonClicked;
mDisappearTimeSupplier = disappearTimeSupplier;
@@ -134,7 +136,8 @@ class UserAspectRatioSettingsWindowManager extends CompatUIWindowManagerAbstract
public boolean updateCompatInfo(@NonNull TaskInfo taskInfo,
@NonNull ShellTaskOrganizer.TaskListener taskListener, boolean canShow) {
final boolean prevHasUserAspectRatioSettingsButton = mHasUserAspectRatioSettingsButton;
- mHasUserAspectRatioSettingsButton = getHasUserAspectRatioSettingsButton(taskInfo);
+ mHasUserAspectRatioSettingsButton = shouldShowUserAspectRatioSettingsButton(
+ taskInfo.appCompatTaskInfo, taskInfo.baseIntent);
if (!super.updateCompatInfo(taskInfo, taskListener, canShow)) {
return false;
@@ -227,12 +230,21 @@ class UserAspectRatioSettingsWindowManager extends CompatUIWindowManagerAbstract
return SystemClock.uptimeMillis() + hideDelay;
}
- private boolean getHasUserAspectRatioSettingsButton(@NonNull TaskInfo taskInfo) {
- final Intent intent = taskInfo.baseIntent;
- return taskInfo.appCompatTaskInfo.topActivityEligibleForUserAspectRatioButton
- && (taskInfo.appCompatTaskInfo.topActivityBoundsLetterboxed
- || taskInfo.appCompatTaskInfo.isUserFullscreenOverrideEnabled)
- && !taskInfo.appCompatTaskInfo.isSystemFullscreenOverrideEnabled
+ private boolean shouldShowUserAspectRatioSettingsButton(@NonNull AppCompatTaskInfo taskInfo,
+ @NonNull Intent intent) {
+ final Rect stableBounds = getTaskStableBounds();
+ final int letterboxHeight = taskInfo.topActivityLetterboxHeight;
+ final int letterboxWidth = taskInfo.topActivityLetterboxWidth;
+ // App is not visibly letterboxed if it covers status bar/bottom insets or matches the
+ // stable bounds, so don't show the button
+ if (stableBounds.height() <= letterboxHeight && stableBounds.width() <= letterboxWidth) {
+ return false;
+ }
+
+ return taskInfo.topActivityEligibleForUserAspectRatioButton
+ && (taskInfo.topActivityBoundsLetterboxed
+ || taskInfo.isUserFullscreenOverrideEnabled)
+ && !taskInfo.isSystemFullscreenOverrideEnabled
&& Intent.ACTION_MAIN.equals(intent.getAction())
&& intent.hasCategory(Intent.CATEGORY_LAUNCHER)
&& (!mUserAspectRatioButtonShownChecker.get() || isShowingButton());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
index d4ed0170dca8..216da070754b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
@@ -26,6 +26,7 @@ import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.LaunchAdjacentController;
+import com.android.wm.shell.common.MultiInstanceHelper;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
@@ -88,13 +89,14 @@ public class TvWMShellModule {
IconProvider iconProvider,
Optional<RecentTasksController> recentTasks,
LaunchAdjacentController launchAdjacentController,
+ MultiInstanceHelper multiInstanceHelper,
@ShellMainThread ShellExecutor mainExecutor,
Handler mainHandler,
SystemWindows systemWindows) {
return new TvSplitScreenController(context, shellInit, shellCommandHandler, shellController,
shellTaskOrganizer, syncQueue, rootTDAOrganizer, displayController,
displayImeController, displayInsetsController, transitions, transactionPool,
- iconProvider, recentTasks, launchAdjacentController, mainExecutor, mainHandler,
- systemWindows);
+ iconProvider, recentTasks, launchAdjacentController, multiInstanceHelper,
+ mainExecutor, mainHandler, systemWindows);
}
}
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 0d6a85271fd1..8b2ec0a35685 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
@@ -50,6 +50,7 @@ import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.DockStateReader;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.LaunchAdjacentController;
+import com.android.wm.shell.common.MultiInstanceHelper;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
@@ -66,6 +67,7 @@ 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.PipMediaController;
+import com.android.wm.shell.common.pip.PipPerfHintController;
import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.SizeSpecSource;
@@ -321,6 +323,12 @@ public abstract class WMShellBaseModule {
return Optional.of(perfHintController.getHinter());
}
+ @WMSingleton
+ @Provides
+ static MultiInstanceHelper provideMultiInstanceHelper(Context context) {
+ return new MultiInstanceHelper(context, context.getPackageManager());
+ }
+
//
// Back animation
//
@@ -396,6 +404,20 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
+ static Optional<PipPerfHintController> providePipPerfHintController(
+ PipDisplayLayoutState pipDisplayLayoutState,
+ @ShellMainThread ShellExecutor mainExecutor,
+ Optional<SystemPerformanceHinter> systemPerformanceHinterOptional) {
+ if (systemPerformanceHinterOptional.isPresent()) {
+ return Optional.of(new PipPerfHintController(pipDisplayLayoutState, mainExecutor,
+ systemPerformanceHinterOptional.get()));
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ @WMSingleton
+ @Provides
static PipBoundsState providePipBoundsState(Context context,
SizeSpecSource sizeSpecSource, PipDisplayLayoutState pipDisplayLayoutState) {
return new PipBoundsState(context, sizeSpecSource, pipDisplayLayoutState);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index bd9d89c9892e..f757e1c88cb8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -47,6 +47,7 @@ import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.LaunchAdjacentController;
+import com.android.wm.shell.common.MultiInstanceHelper;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerImpl;
@@ -64,7 +65,7 @@ import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler;
import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler;
import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler;
import com.android.wm.shell.draganddrop.DragAndDropController;
-import com.android.wm.shell.draganddrop.UnhandledDragController;
+import com.android.wm.shell.draganddrop.GlobalDragListener;
import com.android.wm.shell.freeform.FreeformComponents;
import com.android.wm.shell.freeform.FreeformTaskListener;
import com.android.wm.shell.freeform.FreeformTaskTransitionHandler;
@@ -347,12 +348,14 @@ public abstract class WMShellModule {
LaunchAdjacentController launchAdjacentController,
Optional<WindowDecorViewModel> windowDecorViewModel,
Optional<DesktopTasksController> desktopTasksController,
+ MultiInstanceHelper multiInstanceHelper,
@ShellMainThread ShellExecutor mainExecutor) {
return new SplitScreenController(context, shellInit, shellCommandHandler, shellController,
shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer, displayController,
displayImeController, displayInsetsController, dragAndDropController, transitions,
transactionPool, iconProvider, recentTasks, launchAdjacentController,
- windowDecorViewModel, desktopTasksController, mainExecutor);
+ windowDecorViewModel, desktopTasksController, null /* stageCoordinator */,
+ multiInstanceHelper, mainExecutor);
}
//
@@ -495,6 +498,7 @@ public abstract class WMShellModule {
ShellTaskOrganizer shellTaskOrganizer,
SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+ DragAndDropController dragAndDropController,
Transitions transitions,
EnterDesktopTaskTransitionHandler enterDesktopTransitionHandler,
ExitDesktopTaskTransitionHandler exitDesktopTransitionHandler,
@@ -503,14 +507,15 @@ public abstract class WMShellModule {
@DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository,
LaunchAdjacentController launchAdjacentController,
RecentsTransitionHandler recentsTransitionHandler,
+ MultiInstanceHelper multiInstanceHelper,
@ShellMainThread ShellExecutor mainExecutor
) {
return new DesktopTasksController(context, shellInit, shellCommandHandler, shellController,
displayController, shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer,
- transitions, enterDesktopTransitionHandler, exitDesktopTransitionHandler,
- toggleResizeDesktopTaskTransitionHandler, dragToDesktopTransitionHandler,
- desktopModeTaskRepository, launchAdjacentController, recentsTransitionHandler,
- mainExecutor);
+ dragAndDropController, transitions, enterDesktopTransitionHandler,
+ exitDesktopTransitionHandler, toggleResizeDesktopTaskTransitionHandler,
+ dragToDesktopTransitionHandler, desktopModeTaskRepository, launchAdjacentController,
+ recentsTransitionHandler, multiInstanceHelper, mainExecutor);
}
@WMSingleton
@@ -559,10 +564,10 @@ public abstract class WMShellModule {
@WMSingleton
@Provides
- static UnhandledDragController provideUnhandledDragController(
+ static GlobalDragListener provideGlobalDragListener(
IWindowManager wmService,
@ShellMainThread ShellExecutor mainExecutor) {
- return new UnhandledDragController(wmService, mainExecutor);
+ return new GlobalDragListener(wmService, mainExecutor);
}
@WMSingleton
@@ -574,9 +579,12 @@ public abstract class WMShellModule {
DisplayController displayController,
UiEventLogger uiEventLogger,
IconProvider iconProvider,
+ GlobalDragListener globalDragListener,
+ Transitions transitions,
@ShellMainThread ShellExecutor mainExecutor) {
- return new DragAndDropController(context, shellInit, shellController,
- shellCommandHandler, displayController, uiEventLogger, iconProvider, mainExecutor);
+ return new DragAndDropController(context, shellInit, shellController, shellCommandHandler,
+ displayController, uiEventLogger, iconProvider, globalDragListener, transitions,
+ mainExecutor);
}
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
index 805336943d89..1e3d7fb06da2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
@@ -36,6 +36,7 @@ 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.PipMediaController;
+import com.android.wm.shell.common.pip.PipPerfHintController;
import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.PipUtils;
@@ -143,10 +144,12 @@ public abstract class Pip1Module {
PipMotionHelper pipMotionHelper,
FloatingContentCoordinator floatingContentCoordinator,
PipUiEventLogger pipUiEventLogger,
- @ShellMainThread ShellExecutor mainExecutor) {
+ @ShellMainThread ShellExecutor mainExecutor,
+ Optional<PipPerfHintController> pipPerfHintControllerOptional) {
return new PipTouchHandler(context, shellInit, menuPhoneController, pipBoundsAlgorithm,
pipBoundsState, sizeSpecSource, pipTaskOrganizer, pipMotionHelper,
- floatingContentCoordinator, pipUiEventLogger, mainExecutor);
+ floatingContentCoordinator, pipUiEventLogger, mainExecutor,
+ pipPerfHintControllerOptional);
}
@WMSingleton
@@ -169,6 +172,7 @@ public abstract class Pip1Module {
PipTransitionController pipTransitionController,
PipParamsChangedForwarder pipParamsChangedForwarder,
Optional<SplitScreenController> splitScreenControllerOptional,
+ Optional<PipPerfHintController> pipPerfHintControllerOptional,
DisplayController displayController,
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
@@ -176,8 +180,8 @@ public abstract class Pip1Module {
syncTransactionQueue, pipTransitionState, pipBoundsState, pipDisplayLayoutState,
pipBoundsAlgorithm, menuPhoneController, pipAnimationController,
pipSurfaceTransactionHelper, pipTransitionController, pipParamsChangedForwarder,
- splitScreenControllerOptional, displayController, pipUiEventLogger,
- shellTaskOrganizer, mainExecutor);
+ splitScreenControllerOptional, pipPerfHintControllerOptional, displayController,
+ pipUiEventLogger, shellTaskOrganizer, mainExecutor);
}
@WMSingleton
@@ -209,10 +213,11 @@ public abstract class Pip1Module {
PipBoundsState pipBoundsState, PipTaskOrganizer pipTaskOrganizer,
PhonePipMenuController menuController, PipSnapAlgorithm pipSnapAlgorithm,
PipTransitionController pipTransitionController,
- FloatingContentCoordinator floatingContentCoordinator) {
+ FloatingContentCoordinator floatingContentCoordinator,
+ Optional<PipPerfHintController> pipPerfHintControllerOptional) {
return new PipMotionHelper(context, pipBoundsState, pipTaskOrganizer,
menuController, pipSnapAlgorithm, pipTransitionController,
- floatingContentCoordinator);
+ floatingContentCoordinator, pipPerfHintControllerOptional);
}
@WMSingleton
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 8eecf1c58db0..458ea05e620d 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
@@ -74,13 +74,18 @@ public abstract class Pip2Module {
ShellController shellController,
DisplayController displayController,
DisplayInsetsController displayInsetsController,
- PipDisplayLayoutState pipDisplayLayoutState) {
+ PipBoundsState pipBoundsState,
+ PipBoundsAlgorithm pipBoundsAlgorithm,
+ PipDisplayLayoutState pipDisplayLayoutState,
+ PipScheduler pipScheduler,
+ @ShellMainThread ShellExecutor mainExecutor) {
if (!PipUtils.isPip2ExperimentEnabled()) {
return Optional.empty();
} else {
return Optional.ofNullable(PipController.create(
context, shellInit, shellController, displayController, displayInsetsController,
- pipDisplayLayoutState));
+ pipBoundsState, pipBoundsAlgorithm, pipDisplayLayoutState, pipScheduler,
+ mainExecutor));
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java
index 1947097c2f15..54c2aeab4976 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java
@@ -34,6 +34,7 @@ import com.android.wm.shell.common.pip.LegacySizeSpecSource;
import com.android.wm.shell.common.pip.PipAppOpsListener;
import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipMediaController;
+import com.android.wm.shell.common.pip.PipPerfHintController;
import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.dagger.WMShellBaseModule;
@@ -212,6 +213,7 @@ public abstract class TvPipModule {
PipParamsChangedForwarder pipParamsChangedForwarder,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
Optional<SplitScreenController> splitScreenControllerOptional,
+ Optional<PipPerfHintController> pipPerfHintControllerOptional,
DisplayController displayController,
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) {
@@ -219,8 +221,8 @@ public abstract class TvPipModule {
syncTransactionQueue, pipTransitionState, tvPipBoundsState, pipDisplayLayoutState,
tvPipBoundsAlgorithm, tvPipMenuController, pipAnimationController,
pipSurfaceTransactionHelper, tvPipTransition, pipParamsChangedForwarder,
- splitScreenControllerOptional, displayController, pipUiEventLogger,
- shellTaskOrganizer, mainExecutor);
+ splitScreenControllerOptional, pipPerfHintControllerOptional, displayController,
+ pipUiEventLogger, shellTaskOrganizer, mainExecutor);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java
index e732a0354806..8305fa6b0fbf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java
@@ -47,4 +47,8 @@ public interface DesktopMode {
default void addDesktopGestureExclusionRegionListener(Consumer<Region> listener,
Executor callbackExecutor) { }
+
+ /** Called when requested to go to desktop mode from the current focused app. */
+ void enterDesktop(int displayId);
+
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
index 6250fc5820aa..7091c4b7210a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
@@ -19,8 +19,8 @@ package com.android.wm.shell.desktopmode;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
-
-import static com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.FINAL_FREEFORM_SCALE;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -28,11 +28,13 @@ import android.animation.RectEvaluator;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.app.ActivityManager;
+import android.app.WindowConfiguration;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.graphics.PointF;
import android.graphics.Rect;
+import android.graphics.Region;
import android.util.DisplayMetrics;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
@@ -41,6 +43,8 @@ import android.view.WindowManager;
import android.view.WindowlessWindowManager;
import android.view.animation.DecelerateInterpolator;
+import androidx.annotation.VisibleForTesting;
+
import com.android.wm.shell.R;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.common.DisplayController;
@@ -93,28 +97,114 @@ public class DesktopModeVisualIndicator {
/**
* Based on the coordinates of the current drag event, determine which indicator type we should
* display, including no visible indicator.
- * TODO(b/280828642): Update drag zones per starting windowing mode.
*/
- IndicatorType updateIndicatorType(PointF inputCoordinates) {
+ IndicatorType updateIndicatorType(PointF inputCoordinates, int windowingMode) {
final DisplayLayout layout = mDisplayController.getDisplayLayout(mTaskInfo.displayId);
// If we are in freeform, we don't want a visible indicator in the "freeform" drag zone.
- IndicatorType result = mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
- ? IndicatorType.NO_INDICATOR : IndicatorType.TO_DESKTOP_INDICATOR;
- int transitionAreaHeight = mContext.getResources().getDimensionPixelSize(
- com.android.wm.shell.R.dimen.desktop_mode_transition_area_height);
- int transitionAreaWidth = mContext.getResources().getDimensionPixelSize(
+ IndicatorType result = IndicatorType.NO_INDICATOR;
+ final int transitionAreaWidth = mContext.getResources().getDimensionPixelSize(
com.android.wm.shell.R.dimen.desktop_mode_transition_area_width);
- if (inputCoordinates.y <= transitionAreaHeight) {
+ // Because drags in freeform use task position for indicator calculation, we need to
+ // account for the possibility of the task going off the top of the screen by captionHeight
+ final int captionHeight = mContext.getResources().getDimensionPixelSize(
+ com.android.wm.shell.R.dimen.desktop_mode_freeform_decor_caption_height);
+ final Region fullscreenRegion = calculateFullscreenRegion(layout, windowingMode,
+ captionHeight);
+ final Region splitLeftRegion = calculateSplitLeftRegion(layout, windowingMode,
+ transitionAreaWidth, captionHeight);
+ final Region splitRightRegion = calculateSplitRightRegion(layout, windowingMode,
+ transitionAreaWidth, captionHeight);
+ final Region toDesktopRegion = calculateToDesktopRegion(layout, windowingMode,
+ splitLeftRegion, splitRightRegion, fullscreenRegion);
+ if (fullscreenRegion.contains((int) inputCoordinates.x, (int) inputCoordinates.y)) {
result = IndicatorType.TO_FULLSCREEN_INDICATOR;
- } else if (inputCoordinates.x <= transitionAreaWidth) {
+ }
+ if (splitLeftRegion.contains((int) inputCoordinates.x, (int) inputCoordinates.y)) {
result = IndicatorType.TO_SPLIT_LEFT_INDICATOR;
- } else if (inputCoordinates.x >= layout.width() - transitionAreaWidth) {
+ }
+ if (splitRightRegion.contains((int) inputCoordinates.x, (int) inputCoordinates.y)) {
result = IndicatorType.TO_SPLIT_RIGHT_INDICATOR;
}
+ if (toDesktopRegion.contains((int) inputCoordinates.x, (int) inputCoordinates.y)) {
+ result = IndicatorType.TO_DESKTOP_INDICATOR;
+ }
transitionIndicator(result);
return result;
}
+ @VisibleForTesting
+ Region calculateFullscreenRegion(DisplayLayout layout,
+ @WindowConfiguration.WindowingMode int windowingMode, int captionHeight) {
+ final Region region = new Region();
+ int edgeTransitionHeight = mContext.getResources().getDimensionPixelSize(
+ com.android.wm.shell.R.dimen.desktop_mode_transition_area_height);
+ // A thin, short Rect at the top of the screen.
+ if (windowingMode == WINDOWING_MODE_FREEFORM) {
+ int fromFreeformWidth = mContext.getResources().getDimensionPixelSize(
+ com.android.wm.shell.R.dimen.desktop_mode_fullscreen_from_desktop_width);
+ int fromFreeformHeight = mContext.getResources().getDimensionPixelSize(
+ com.android.wm.shell.R.dimen.desktop_mode_fullscreen_from_desktop_height);
+ region.union(new Rect((layout.width() / 2) - (fromFreeformWidth / 2),
+ -captionHeight,
+ (layout.width() / 2) + (fromFreeformWidth / 2),
+ fromFreeformHeight));
+ }
+ // A screen-wide, shorter Rect if the task is in fullscreen or split.
+ if (windowingMode == WINDOWING_MODE_FULLSCREEN
+ || windowingMode == WINDOWING_MODE_MULTI_WINDOW) {
+ region.union(new Rect(0,
+ -captionHeight,
+ layout.width(),
+ edgeTransitionHeight));
+ }
+ return region;
+ }
+
+ @VisibleForTesting
+ Region calculateToDesktopRegion(DisplayLayout layout,
+ @WindowConfiguration.WindowingMode int windowingMode,
+ Region splitLeftRegion, Region splitRightRegion,
+ Region toFullscreenRegion) {
+ final Region region = new Region();
+ // If in desktop, we need no region. Otherwise it's the same for all windowing modes.
+ if (windowingMode != WINDOWING_MODE_FREEFORM) {
+ region.union(new Rect(0, 0, layout.width(), layout.height()));
+ region.op(splitLeftRegion, Region.Op.DIFFERENCE);
+ region.op(splitRightRegion, Region.Op.DIFFERENCE);
+ region.op(toFullscreenRegion, Region.Op.DIFFERENCE);
+ }
+ return region;
+ }
+
+ @VisibleForTesting
+ Region calculateSplitLeftRegion(DisplayLayout layout,
+ @WindowConfiguration.WindowingMode int windowingMode,
+ int transitionEdgeWidth, int captionHeight) {
+ final Region region = new Region();
+ // In freeform, keep the top corners clear.
+ int transitionHeight = windowingMode == WINDOWING_MODE_FREEFORM
+ ? mContext.getResources().getDimensionPixelSize(
+ com.android.wm.shell.R.dimen.desktop_mode_split_from_desktop_height) :
+ -captionHeight;
+ region.union(new Rect(0, transitionHeight, transitionEdgeWidth, layout.height()));
+ return region;
+ }
+
+ @VisibleForTesting
+ Region calculateSplitRightRegion(DisplayLayout layout,
+ @WindowConfiguration.WindowingMode int windowingMode,
+ int transitionEdgeWidth, int captionHeight) {
+ final Region region = new Region();
+ // In freeform, keep the top corners clear.
+ int transitionHeight = windowingMode == WINDOWING_MODE_FREEFORM
+ ? mContext.getResources().getDimensionPixelSize(
+ com.android.wm.shell.R.dimen.desktop_mode_split_from_desktop_height) :
+ -captionHeight;
+ region.union(new Rect(layout.width() - transitionEdgeWidth, transitionHeight,
+ layout.width(), layout.height()));
+ return region;
+ }
+
/**
* Create a fullscreen indicator with no animation
*/
@@ -175,7 +265,6 @@ public class DesktopModeVisualIndicator {
mDisplayController.getDisplayLayout(mTaskInfo.displayId));
animator.start();
mCurrentType = IndicatorType.NO_INDICATOR;
-
}
/**
@@ -298,7 +387,8 @@ public class DesktopModeVisualIndicator {
layout.width() - padding,
layout.height() - padding);
case TO_DESKTOP_INDICATOR:
- final float adjustmentPercentage = 1f - FINAL_FREEFORM_SCALE;
+ final float adjustmentPercentage = 1f
+ - DesktopTasksController.DESKTOP_MODE_INITIAL_BOUNDS_SCALE;
return new Rect((int) (adjustmentPercentage * layout.width() / 2),
(int) (adjustmentPercentage * layout.height() / 2),
(int) (layout.width() - (adjustmentPercentage * layout.width() / 2)),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 837cb99602c8..dcffb2d3e8fa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -17,20 +17,24 @@
package com.android.wm.shell.desktopmode
import android.app.ActivityManager.RunningTaskInfo
+import android.app.ActivityOptions
+import android.app.PendingIntent
import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
import android.app.WindowConfiguration.WindowingMode
import android.content.Context
+import android.content.Intent
import android.graphics.Point
import android.graphics.PointF
import android.graphics.Rect
import android.graphics.Region
import android.os.IBinder
import android.os.SystemProperties
-import android.util.DisplayMetrics.DENSITY_DEFAULT
+import android.view.Display.DEFAULT_DISPLAY
import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_CHANGE
import android.view.WindowManager.TRANSIT_NONE
@@ -45,9 +49,12 @@ import com.android.internal.policy.ScreenDecorationsUtils
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.common.ExecutorUtils
import com.android.wm.shell.common.ExternalInterfaceBinder
import com.android.wm.shell.common.LaunchAdjacentController
+import com.android.wm.shell.common.MultiInstanceHelper
+import com.android.wm.shell.common.MultiInstanceHelper.Companion.getComponent
import com.android.wm.shell.common.RemoteCallable
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SingleInstanceRemoteListener
@@ -58,11 +65,12 @@ import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOT
import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository.VisibleTasksListener
import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.DragToDesktopStateListener
+import com.android.wm.shell.draganddrop.DragAndDropController
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.recents.RecentsTransitionHandler
import com.android.wm.shell.recents.RecentsTransitionStateListener
import com.android.wm.shell.splitscreen.SplitScreenController
-import com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_ENTER_DESKTOP
+import com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DESKTOP_MODE
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
import com.android.wm.shell.sysui.ShellInit
@@ -86,6 +94,7 @@ class DesktopTasksController(
private val shellTaskOrganizer: ShellTaskOrganizer,
private val syncQueue: SyncTransactionQueue,
private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
+ private val dragAndDropController: DragAndDropController,
private val transitions: Transitions,
private val enterDesktopTaskTransitionHandler: EnterDesktopTaskTransitionHandler,
private val exitDesktopTaskTransitionHandler: ExitDesktopTaskTransitionHandler,
@@ -95,8 +104,10 @@ class DesktopTasksController(
private val desktopModeTaskRepository: DesktopModeTaskRepository,
private val launchAdjacentController: LaunchAdjacentController,
private val recentsTransitionHandler: RecentsTransitionHandler,
+ private val multiInstanceHelper: MultiInstanceHelper,
@ShellMainThread private val mainExecutor: ShellExecutor
-) : RemoteCallable<DesktopTasksController>, Transitions.TransitionHandler {
+) : RemoteCallable<DesktopTasksController>, Transitions.TransitionHandler,
+ DragAndDropController.DragAndDropListener {
private val desktopMode: DesktopModeImpl
private var visualIndicator: DesktopModeVisualIndicator? = null
@@ -173,6 +184,7 @@ class DesktopTasksController(
}
}
)
+ dragAndDropController.addListener(this)
}
fun setOnTaskResizeAnimationListener(listener: OnTaskResizeAnimationListener) {
@@ -240,6 +252,42 @@ class DesktopTasksController(
return desktopModeTaskRepository.getVisibleTaskCount(displayId)
}
+ /** Enter desktop by using the focused task in given `displayId` */
+ fun enterDesktop(displayId: Int) {
+ val allFocusedTasks =
+ shellTaskOrganizer.getRunningTasks(displayId).filter { taskInfo ->
+ taskInfo.isFocused &&
+ (taskInfo.windowingMode == WINDOWING_MODE_FULLSCREEN ||
+ taskInfo.windowingMode == WINDOWING_MODE_MULTI_WINDOW) &&
+ taskInfo.activityType != ACTIVITY_TYPE_HOME
+ }
+ if (allFocusedTasks.isNotEmpty()) {
+ when (allFocusedTasks.size) {
+ 2 -> {
+ // Split-screen case where there are two focused tasks, then we find the child
+ // task to move to desktop.
+ val splitFocusedTask =
+ if (allFocusedTasks[0].taskId == allFocusedTasks[1].parentTaskId)
+ allFocusedTasks[1]
+ else allFocusedTasks[0]
+ moveToDesktop(splitFocusedTask)
+ }
+ 1 -> {
+ // Fullscreen case where we move the current focused task.
+ moveToDesktop(allFocusedTasks[0].taskId)
+ }
+ else -> {
+ KtProtoLog.w(
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksController: Cannot enter desktop, expected less " +
+ "than 3 focused tasks but found %d",
+ allFocusedTasks.size
+ )
+ }
+ }
+ }
+ }
+
/** Move a task with given `taskId` to desktop */
fun moveToDesktop(
taskId: Int,
@@ -355,7 +403,7 @@ class DesktopTasksController(
splitScreenController.prepareExitSplitScreen(
wct,
splitScreenController.getStageOfTask(taskInfo.taskId),
- EXIT_REASON_ENTER_DESKTOP
+ EXIT_REASON_DESKTOP_MODE
)
getOtherSplitTask(taskInfo.taskId)?.let { otherTaskInfo ->
wct.removeTask(otherTaskInfo.token)
@@ -500,11 +548,7 @@ class DesktopTasksController(
if (taskInfo.configuration.windowConfiguration.bounds == stableBounds) {
// The desktop task is currently occupying the whole stable bounds, toggle to the
// default bounds.
- getDefaultDesktopTaskBounds(
- density = taskInfo.configuration.densityDpi.toFloat() / DENSITY_DEFAULT,
- stableBounds = stableBounds,
- outBounds = destinationBounds
- )
+ getDefaultDesktopTaskBounds(displayLayout, destinationBounds)
} else {
// Toggle to the stable bounds.
destinationBounds.set(stableBounds)
@@ -559,15 +603,17 @@ class DesktopTasksController(
}
}
- private fun getDefaultDesktopTaskBounds(density: Float, stableBounds: Rect, outBounds: Rect) {
- val width = (DESKTOP_MODE_DEFAULT_WIDTH_DP * density + 0.5f).toInt()
- val height = (DESKTOP_MODE_DEFAULT_HEIGHT_DP * density + 0.5f).toInt()
- outBounds.set(0, 0, width, height)
- // Center the task in stable bounds
+ private fun getDefaultDesktopTaskBounds(displayLayout: DisplayLayout, outBounds: Rect) {
+ // TODO(b/319819547): Account for app constraints so apps do not become letterboxed
+ val screenBounds = Rect(0, 0, displayLayout.width(), displayLayout.height())
+ // Update width and height with default desktop mode values
+ val desiredWidth = screenBounds.width().times(DESKTOP_MODE_INITIAL_BOUNDS_SCALE).toInt()
+ val desiredHeight = screenBounds.height().times(DESKTOP_MODE_INITIAL_BOUNDS_SCALE).toInt()
+ outBounds.set(0, 0, desiredWidth, desiredHeight)
+ // Center the task in screen bounds
outBounds.offset(
- stableBounds.centerX() - outBounds.centerX(),
- stableBounds.centerY() - outBounds.centerY()
- )
+ screenBounds.centerX() - outBounds.centerX(),
+ screenBounds.centerY() - outBounds.centerY())
}
/**
@@ -893,7 +939,7 @@ class DesktopTasksController(
}
// Then, update the indicator type.
val indicator = visualIndicator ?: return
- indicator.updateIndicatorType(PointF(inputX, taskTop))
+ indicator.updateIndicatorType(PointF(inputX, taskTop), taskInfo.windowingMode)
}
/**
@@ -919,19 +965,13 @@ class DesktopTasksController(
}
if (inputCoordinate.x <= transitionAreaWidth) {
releaseVisualIndicator()
- val wct = WindowContainerTransaction()
- addMoveToSplitChanges(wct, taskInfo)
- splitScreenController.requestEnterSplitSelect(taskInfo, wct,
- SPLIT_POSITION_TOP_OR_LEFT, taskBounds)
+ snapToHalfScreen(taskInfo, SnapPosition.LEFT)
return
}
if (inputCoordinate.x >= (displayController.getDisplayLayout(taskInfo.displayId)?.width()
?.minus(transitionAreaWidth) ?: return)) {
releaseVisualIndicator()
- val wct = WindowContainerTransaction()
- addMoveToSplitChanges(wct, taskInfo)
- splitScreenController.requestEnterSplitSelect(taskInfo, wct,
- SPLIT_POSITION_BOTTOM_OR_RIGHT, taskBounds)
+ snapToHalfScreen(taskInfo, SnapPosition.RIGHT)
return
}
// A freeform drag-move ended, remove the indicator immediately.
@@ -992,6 +1032,50 @@ class DesktopTasksController(
desktopModeTaskRepository.setExclusionRegionListener(listener, callbackExecutor)
}
+ override fun onUnhandledDrag(
+ launchIntent: PendingIntent,
+ dragSurface: SurfaceControl,
+ onFinishCallback: Consumer<Boolean>
+ ): Boolean {
+ // TODO(b/320797628): Pass through which display we are dropping onto
+ val activeTasks = desktopModeTaskRepository.getActiveTasks(DEFAULT_DISPLAY)
+ if (!activeTasks.any { desktopModeTaskRepository.isVisibleTask(it) }) {
+ // Not currently in desktop mode, ignore the drop
+ return false
+ }
+
+ val launchComponent = getComponent(launchIntent)
+ if (!multiInstanceHelper.supportsMultiInstanceSplit(launchComponent)) {
+ // TODO(b/320797628): Should only return early if there is an existing running task, and
+ // notify the user as well. But for now, just ignore the drop.
+ KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "Dropped intent does not support multi-instance")
+ return false
+ }
+
+ // Start a new transition to launch the app
+ val opts = ActivityOptions.makeBasic().apply {
+ launchWindowingMode = WINDOWING_MODE_FREEFORM
+ pendingIntentLaunchFlags =
+ Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK
+ setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
+ isPendingIntentBackgroundActivityLaunchAllowedByPermission = true
+ }
+ val wct = WindowContainerTransaction()
+ wct.sendPendingIntent(launchIntent, null, opts.toBundle())
+ transitions.startTransition(TRANSIT_OPEN, wct, null /* handler */)
+
+ // Report that this is handled by the listener
+ onFinishCallback.accept(true)
+
+ // We've assumed responsibility of cleaning up the drag surface, so do that now
+ // TODO(b/320797628): Do an actual animation here for the drag surface
+ val t = SurfaceControl.Transaction()
+ t.remove(dragSurface)
+ t.apply()
+ return true
+ }
+
private fun dump(pw: PrintWriter, prefix: String) {
val innerPrefix = "$prefix "
pw.println("${prefix}DesktopTasksController")
@@ -1018,6 +1102,12 @@ class DesktopTasksController(
this@DesktopTasksController.setTaskRegionListener(listener, callbackExecutor)
}
}
+
+ override fun enterDesktop(displayId: Int) {
+ mainExecutor.execute {
+ this@DesktopTasksController.enterDesktop(displayId)
+ }
+ }
}
/** The interface for calls from outside the host process. */
@@ -1138,13 +1228,9 @@ class DesktopTasksController(
SystemProperties.getInt("persist.wm.debug.desktop_mode_density", 284)
private val DESKTOP_DENSITY_ALLOWED_RANGE = (100..1000)
- // Override default freeform task width when desktop mode is enabled. In dips.
- private val DESKTOP_MODE_DEFAULT_WIDTH_DP =
- SystemProperties.getInt("persist.wm.debug.desktop_mode.default_width", 840)
-
- // Override default freeform task height when desktop mode is enabled. In dips.
- private val DESKTOP_MODE_DEFAULT_HEIGHT_DP =
- SystemProperties.getInt("persist.wm.debug.desktop_mode.default_height", 630)
+ @JvmField
+ val DESKTOP_MODE_INITIAL_BOUNDS_SCALE = SystemProperties
+ .getInt("persist.wm.debug.freeform_initial_bounds_scale", 75) / 100f
/**
* Check if desktop density override is enabled
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
index 07cf202ddfac..79bb5408df82 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
@@ -54,8 +54,6 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition
private final Transitions mTransitions;
private final Supplier<SurfaceControl.Transaction> mTransactionSupplier;
- // The size of the screen after drag relative to the fullscreen size
- public static final float FINAL_FREEFORM_SCALE = 0.6f;
public static final int FREEFORM_ANIMATION_DURATION = 336;
private final List<IBinder> mPendingTransitionTokens = new ArrayList<>();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
index 269c3699ac0a..1afbdf90eac0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
@@ -35,7 +35,9 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_DRAG_AND_DROP;
+import android.app.ActivityManager;
import android.app.ActivityTaskManager;
+import android.app.PendingIntent;
import android.content.ClipDescription;
import android.content.ComponentCallbacks2;
import android.content.Context;
@@ -51,6 +53,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.FrameLayout;
+import android.window.WindowContainerTransaction;
import androidx.annotation.BinderThread;
import androidx.annotation.NonNull;
@@ -71,14 +74,18 @@ import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.function.Consumer;
+import java.util.function.Function;
/**
* Handles the global drag and drop handling for the Shell.
*/
public class DragAndDropController implements RemoteCallable<DragAndDropController>,
+ GlobalDragListener.GlobalDragListenerCallback,
DisplayController.OnDisplaysChangedListener,
View.OnDragListener, ComponentCallbacks2 {
@@ -90,6 +97,8 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
private final DisplayController mDisplayController;
private final DragAndDropEventLogger mLogger;
private final IconProvider mIconProvider;
+ private final GlobalDragListener mGlobalDragListener;
+ private final Transitions mTransitions;
private SplitScreenController mSplitScreen;
private ShellExecutor mMainExecutor;
private ArrayList<DragAndDropListener> mListeners = new ArrayList<>();
@@ -97,12 +106,29 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
// Map of displayId -> per-display info
private final SparseArray<PerDisplay> mDisplayDropTargets = new SparseArray<>();
+ // The current display if a drag is in progress
+ private int mActiveDragDisplay = -1;
+
/**
- * Listener called during drag events, currently just onDragStarted.
+ * Listener called during drag events.
*/
public interface DragAndDropListener {
/** Called when a drag has started. */
- void onDragStarted();
+ default void onDragStarted() {}
+
+ /** Called when a drag has ended. */
+ default void onDragEnded() {}
+
+ /**
+ * Called when an unhandled drag has occurred. The impl must return true if it decides to
+ * handled the unhandled drag, and it must also call `onFinishCallback` to complete the
+ * drag.
+ */
+ default boolean onUnhandledDrag(@NonNull PendingIntent launchIntent,
+ @NonNull SurfaceControl dragSurface,
+ @NonNull Consumer<Boolean> onFinishCallback) {
+ return false;
+ }
}
public DragAndDropController(Context context,
@@ -112,6 +138,8 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
DisplayController displayController,
UiEventLogger uiEventLogger,
IconProvider iconProvider,
+ GlobalDragListener globalDragListener,
+ Transitions transitions,
ShellExecutor mainExecutor) {
mContext = context;
mShellController = shellController;
@@ -119,6 +147,8 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
mDisplayController = displayController;
mLogger = new DragAndDropEventLogger(uiEventLogger);
mIconProvider = iconProvider;
+ mGlobalDragListener = globalDragListener;
+ mTransitions = transitions;
mMainExecutor = mainExecutor;
shellInit.addInitCallback(this::onInit, this);
}
@@ -136,6 +166,7 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
mShellController.addExternalInterface(KEY_EXTRA_SHELL_DRAG_AND_DROP,
this::createExternalInterface, this);
mShellCommandHandler.addDumpCallback(this::dump, this);
+ mGlobalDragListener.setListener(this);
}
private ExternalInterfaceBinder createExternalInterface() {
@@ -169,10 +200,18 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
mListeners.remove(listener);
}
- private void notifyDragStarted() {
+ /**
+ * Notifies all listeners and returns whether any listener handled the callback.
+ */
+ private boolean notifyListeners(Function<DragAndDropListener, Boolean> callback) {
for (int i = 0; i < mListeners.size(); i++) {
- mListeners.get(i).onDragStarted();
+ boolean handled = callback.apply(mListeners.get(i));
+ if (handled) {
+ // Return once the callback reports it has handled it
+ return true;
+ }
}
+ return false;
}
@Override
@@ -258,6 +297,7 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
}
if (event.getAction() == ACTION_DRAG_STARTED) {
+ mActiveDragDisplay = displayId;
pd.isHandlingDrag = DragUtils.canHandleDrag(event);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
"Clip description: handlingDrag=%b itemCount=%d mimeTypes=%s",
@@ -283,7 +323,11 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
pd.dragSession.update();
pd.dragLayout.prepare(pd.dragSession, loggerSessionId);
setDropTargetWindowVisibility(pd, View.VISIBLE);
- notifyDragStarted();
+ notifyListeners(l -> {
+ l.onDragStarted();
+ // Return false to continue dispatch to next listener
+ return false;
+ });
break;
case ACTION_DRAG_ENTERED:
pd.dragLayout.show();
@@ -317,11 +361,43 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll
});
}
mLogger.logEnd();
+ mActiveDragDisplay = -1;
+ notifyListeners(l -> {
+ l.onDragEnded();
+ // Return false to continue dispatch to next listener
+ return false;
+ });
break;
}
return true;
}
+ @Override
+ public void onCrossWindowDrop(@NonNull ActivityManager.RunningTaskInfo taskInfo) {
+ // Bring the task forward when an item is dropped on it
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ wct.reorder(taskInfo.token, true /* onTop */);
+ mTransitions.startTransition(WindowManager.TRANSIT_TO_FRONT, wct, null);
+ }
+
+ @Override
+ public void onUnhandledDrop(@NonNull DragEvent dragEvent,
+ @NonNull Consumer<Boolean> onFinishCallback) {
+ final PendingIntent launchIntent = DragUtils.getLaunchIntent(dragEvent);
+ if (launchIntent == null) {
+ // No intent to launch, report that this is unhandled by the listener
+ onFinishCallback.accept(false);
+ return;
+ }
+
+ final boolean handled = notifyListeners(
+ l -> l.onUnhandledDrag(launchIntent, dragEvent.getDragSurface(), onFinishCallback));
+ if (!handled) {
+ // Nobody handled this, we still have to notify WM
+ onFinishCallback.accept(false);
+ }
+ }
+
/**
* Handles dropping on the drop target.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java
index 7c0883d2538f..f7bcc9477aa1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java
@@ -20,9 +20,14 @@ import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
+import android.app.PendingIntent;
+import android.content.ClipData;
import android.content.ClipDescription;
import android.view.DragEvent;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
/** Collection of utility classes for handling drag and drop. */
public class DragUtils {
private static final String TAG = "DragUtils";
@@ -45,6 +50,31 @@ public class DragUtils {
}
/**
+ * Returns a launchable intent in the given `DragEvent` or `null` if there is none.
+ */
+ @Nullable
+ public static PendingIntent getLaunchIntent(@NonNull DragEvent dragEvent) {
+ return getLaunchIntent(dragEvent.getClipData());
+ }
+
+ /**
+ * Returns a launchable intent in the given `ClipData` or `null` if there is none.
+ */
+ @Nullable
+ public static PendingIntent getLaunchIntent(@NonNull ClipData data) {
+ for (int i = 0; i < data.getItemCount(); i++) {
+ final ClipData.Item item = data.getItemAt(i);
+ if (item.getIntentSender() != null) {
+ final PendingIntent intent = new PendingIntent(item.getIntentSender().getTarget());
+ if (intent != null && intent.isActivity()) {
+ return intent;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
* Returns a list of the mime types provided in the clip description.
*/
public static String getMimeTypesConcatenated(ClipDescription description) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/UnhandledDragController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/GlobalDragListener.kt
index ccf48d0de9ed..8826141fb406 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/UnhandledDragController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/GlobalDragListener.kt
@@ -15,12 +15,13 @@
*/
package com.android.wm.shell.draganddrop
+import android.app.ActivityManager
import android.os.RemoteException
import android.util.Log
import android.view.DragEvent
import android.view.IWindowManager
+import android.window.IGlobalDragListener
import android.window.IUnhandledDragCallback
-import android.window.IUnhandledDragListener
import androidx.annotation.VisibleForTesting
import com.android.internal.protolog.common.ProtoLog
import com.android.wm.shell.common.ShellExecutor
@@ -29,26 +30,38 @@ import java.util.function.Consumer
/**
* Manages the listener and callbacks for unhandled global drags.
+ * This is only used by DragAndDropController and should not be used directly by other classes.
*/
-class UnhandledDragController(
- val wmService: IWindowManager,
- mainExecutor: ShellExecutor
+class GlobalDragListener(
+ private val wmService: IWindowManager,
+ private val mainExecutor: ShellExecutor
) {
- private var callback: UnhandledDragAndDropCallback? = null
+ private var callback: GlobalDragListenerCallback? = null
+
+ private val globalDragListener: IGlobalDragListener =
+ object : IGlobalDragListener.Stub() {
+ override fun onCrossWindowDrop(taskInfo: ActivityManager.RunningTaskInfo) {
+ mainExecutor.execute() {
+ this@GlobalDragListener.onCrossWindowDrop(taskInfo)
+ }
+ }
- private val unhandledDragListener: IUnhandledDragListener =
- object : IUnhandledDragListener.Stub() {
override fun onUnhandledDrop(event: DragEvent, callback: IUnhandledDragCallback) {
mainExecutor.execute() {
- this@UnhandledDragController.onUnhandledDrop(event, callback)
+ this@GlobalDragListener.onUnhandledDrop(event, callback)
}
}
}
/**
- * Listener called when an unhandled drag is started.
+ * Callbacks for global drag events.
*/
- interface UnhandledDragAndDropCallback {
+ interface GlobalDragListenerCallback {
+ /**
+ * Called when a global drag is successfully handled by another window.
+ */
+ fun onCrossWindowDrop(taskInfo: ActivityManager.RunningTaskInfo) {}
+
/**
* Called when a global drag is unhandled (ie. dropped outside of all visible windows, or
* dropped on a window that does not want to handle it).
@@ -62,7 +75,7 @@ class UnhandledDragController(
/**
* Sets a listener for callbacks when an unhandled drag happens.
*/
- fun setListener(listener: UnhandledDragAndDropCallback?) {
+ fun setListener(listener: GlobalDragListenerCallback?) {
val updateWm = (callback == null && listener != null)
|| (callback != null && listener == null)
callback = listener
@@ -71,8 +84,8 @@ class UnhandledDragController(
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
"%s unhandled drag listener",
if (callback != null) "Registering" else "Unregistering")
- wmService.setUnhandledDragListener(
- if (callback != null) unhandledDragListener else null)
+ wmService.setGlobalDragListener(
+ if (callback != null) globalDragListener else null)
} catch (e: RemoteException) {
Log.e(TAG, "Failed to set unhandled drag listener")
}
@@ -80,11 +93,19 @@ class UnhandledDragController(
}
@VisibleForTesting
+ fun onCrossWindowDrop(taskInfo: ActivityManager.RunningTaskInfo) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
+ "onCrossWindowDrop: %s", taskInfo)
+ callback?.onCrossWindowDrop(taskInfo)
+ }
+
+ @VisibleForTesting
fun onUnhandledDrop(dragEvent: DragEvent, wmCallback: IUnhandledDragCallback) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
"onUnhandledDrop: %s", dragEvent)
if (callback == null) {
wmCallback.notifyUnhandledDropComplete(false)
+ return
}
callback?.onUnhandledDrop(dragEvent) {
@@ -95,6 +116,6 @@ class UnhandledDragController(
}
companion object {
- private val TAG = UnhandledDragController::class.java.simpleName
+ private val TAG = GlobalDragListener::class.java.simpleName
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 271a939c4882..c7daf561f682 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -86,6 +86,7 @@ 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.PipMenuController;
+import com.android.wm.shell.common.pip.PipPerfHintController;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.pip.phone.PipMotionHelper;
@@ -140,6 +141,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
private final int mCrossFadeAnimationDuration;
private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
private final Optional<SplitScreenController> mSplitScreenOptional;
+ @Nullable private final PipPerfHintController mPipPerfHintController;
protected final ShellTaskOrganizer mTaskOrganizer;
protected final ShellExecutor mMainExecutor;
@@ -157,10 +159,30 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
private final PipAnimationController.PipAnimationCallback mPipAnimationCallback =
new PipAnimationController.PipAnimationCallback() {
private boolean mIsCancelled;
+ @Nullable private PipPerfHintController.PipHighPerfSession mPipHighPerfSession;
+
+ private void onHighPerfSessionTimeout(
+ PipPerfHintController.PipHighPerfSession session) {}
+
+ private void cleanUpHighPerfSessionMaybe() {
+ if (mPipHighPerfSession != null) {
+ // Close the high perf session once pointer interactions are over;
+ mPipHighPerfSession.close();
+ mPipHighPerfSession = null;
+ }
+ }
+
@Override
public void onPipAnimationStart(TaskInfo taskInfo,
PipAnimationController.PipTransitionAnimator animator) {
+ if (mPipPerfHintController != null) {
+ // Start a high perf session with a timeout callback.
+ mPipHighPerfSession = mPipPerfHintController.startSession(
+ this::onHighPerfSessionTimeout,
+ "PipTaskOrganizer::mPipAnimationCallback");
+ }
+
final int direction = animator.getTransitionDirection();
mIsCancelled = false;
sendOnPipTransitionStarted(direction);
@@ -169,6 +191,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
@Override
public void onPipAnimationEnd(TaskInfo taskInfo, SurfaceControl.Transaction tx,
PipAnimationController.PipTransitionAnimator animator) {
+ // Close the high perf session if needed.
+ cleanUpHighPerfSessionMaybe();
+
final int direction = animator.getTransitionDirection();
if (mIsCancelled) {
sendOnPipTransitionFinished(direction);
@@ -356,6 +381,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
@NonNull PipTransitionController pipTransitionController,
@NonNull PipParamsChangedForwarder pipParamsChangedForwarder,
Optional<SplitScreenController> splitScreenOptional,
+ Optional<PipPerfHintController> pipPerfHintControllerOptional,
@NonNull DisplayController displayController,
@NonNull PipUiEventLogger pipUiEventLogger,
@NonNull ShellTaskOrganizer shellTaskOrganizer,
@@ -381,6 +407,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mSurfaceControlTransactionFactory =
new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
mSplitScreenOptional = splitScreenOptional;
+ mPipPerfHintController = pipPerfHintControllerOptional.orElse(null);
mTaskOrganizer = shellTaskOrganizer;
mMainExecutor = mainExecutor;
@@ -1969,9 +1996,13 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
pw.println(innerPrefix + "mToken=" + mToken
+ " binder=" + (mToken != null ? mToken.asBinder() : null));
pw.println(innerPrefix + "mLeash=" + mLeash);
+ pw.println(innerPrefix + "mPipOverlay=" + mPipOverlay);
pw.println(innerPrefix + "mState=" + mPipTransitionState.getTransitionState());
pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams);
mPipTransitionController.dump(pw, innerPrefix);
+ if (mPipPerfHintController != null) {
+ mPipPerfHintController.dump(pw, innerPrefix);
+ }
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index 6191fea1595a..32442f740a52 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -191,7 +191,7 @@ public abstract class PipTransitionController implements Transitions.TransitionH
try {
ActivityTaskManager.getService().onPictureInPictureUiStateChanged(
new PictureInPictureUiState.Builder()
- .setEnteringPip(true)
+ .setTransitioningToPip(true)
.build());
} catch (RemoteException | IllegalStateException e) {
ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
@@ -210,7 +210,7 @@ public abstract class PipTransitionController implements Transitions.TransitionH
try {
ActivityTaskManager.getService().onPictureInPictureUiStateChanged(
new PictureInPictureUiState.Builder()
- .setEnteringPip(false)
+ .setTransitioningToPip(false)
.build());
} catch (RemoteException | IllegalStateException e) {
ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
@@ -305,6 +305,12 @@ public abstract class PipTransitionController implements Transitions.TransitionH
public void end() {
}
+ /** Starts the {@link android.window.SystemPerformanceHinter.HighPerfSession}. */
+ public void startHighPerfSession() {}
+
+ /** Closes the {@link android.window.SystemPerformanceHinter.HighPerfSession}. */
+ public void closeHighPerfSession() {}
+
/**
* Callback interface for PiP transitions (both from and to PiP mode)
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 05d4f53986b8..46840773cfc6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -48,7 +48,6 @@ import android.graphics.Rect;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.util.Pair;
-import android.util.Size;
import android.view.DisplayInfo;
import android.view.InsetsState;
import android.view.SurfaceControl;
@@ -75,6 +74,8 @@ import com.android.wm.shell.common.SingleInstanceRemoteListener;
import com.android.wm.shell.common.TabletopModeController;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.common.pip.IPip;
+import com.android.wm.shell.common.pip.IPipAnimationListener;
import com.android.wm.shell.common.pip.PipAppOpsListener;
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
@@ -85,8 +86,6 @@ import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
-import com.android.wm.shell.pip.IPip;
-import com.android.wm.shell.pip.IPipAnimationListener;
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
@@ -1042,22 +1041,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
/** Save the state to restore to on re-entry. */
public void saveReentryState(Rect pipBounds) {
float snapFraction = mPipBoundsAlgorithm.getSnapFraction(pipBounds);
-
- if (!mPipBoundsState.hasUserResizedPip()) {
- mPipBoundsState.saveReentryState(null /* bounds */, snapFraction);
- return;
- }
-
- Size reentrySize = new Size(pipBounds.width(), pipBounds.height());
-
- // TODO: b/279937014 Investigate why userResizeBounds are empty with shell transitions on
- // fallback to using the userResizeBounds if userResizeBounds are not empty
- if (!mTouchHandler.getUserResizeBounds().isEmpty()) {
- Rect userResizeBounds = mTouchHandler.getUserResizeBounds();
- reentrySize = new Size(userResizeBounds.width(), userResizeBounds.height());
- }
-
- mPipBoundsState.saveReentryState(reentrySize, snapFraction);
+ mPipBoundsState.saveReentryState(snapFraction);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index c708b86e88c5..df67707e2014 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -42,6 +42,7 @@ import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
import com.android.wm.shell.common.pip.PipAppOpsListener;
import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipPerfHintController;
import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
@@ -50,6 +51,7 @@ import com.android.wm.shell.protolog.ShellProtoLogGroup;
import kotlin.Unit;
import kotlin.jvm.functions.Function0;
+import java.util.Optional;
import java.util.function.Consumer;
/**
@@ -84,6 +86,9 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
/** Coordinator instance for resolving conflicts with other floating content. */
private FloatingContentCoordinator mFloatingContentCoordinator;
+ @Nullable private final PipPerfHintController mPipPerfHintController;
+ @Nullable private PipPerfHintController.PipHighPerfSession mPipHighPerfSession;
+
/**
* PhysicsAnimator instance for animating {@link PipBoundsState#getMotionBoundsState()}
* using physics animations.
@@ -169,13 +174,15 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
public PipMotionHelper(Context context, @NonNull PipBoundsState pipBoundsState,
PipTaskOrganizer pipTaskOrganizer, PhonePipMenuController menuController,
PipSnapAlgorithm snapAlgorithm, PipTransitionController pipTransitionController,
- FloatingContentCoordinator floatingContentCoordinator) {
+ FloatingContentCoordinator floatingContentCoordinator,
+ Optional<PipPerfHintController> pipPerfHintControllerOptional) {
mContext = context;
mPipTaskOrganizer = pipTaskOrganizer;
mPipBoundsState = pipBoundsState;
mMenuController = menuController;
mSnapAlgorithm = snapAlgorithm;
mFloatingContentCoordinator = floatingContentCoordinator;
+ mPipPerfHintController = pipPerfHintControllerOptional.orElse(null);
pipTransitionController.registerPipTransitionCallback(mPipTransitionCallback);
mResizePipUpdateListener = (target, values) -> {
if (mPipBoundsState.getMotionBoundsState().isInMotion()) {
@@ -386,6 +393,16 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
movetoTarget(velX, velY, postBoundsUpdateCallback, true /* isStash */);
}
+ private void onHighPerfSessionTimeout(PipPerfHintController.PipHighPerfSession session) {}
+
+ private void cleanUpHighPerfSessionMaybe() {
+ if (mPipHighPerfSession != null) {
+ // Close the high perf session once pointer interactions are over;
+ mPipHighPerfSession.close();
+ mPipHighPerfSession = null;
+ }
+ }
+
private void movetoTarget(
float velocityX,
float velocityY,
@@ -591,6 +608,11 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
(int) toY + getBounds().height()));
if (!mTemporaryBoundsPhysicsAnimator.isRunning()) {
+ if (mPipPerfHintController != null) {
+ // Start a high perf session with a timeout callback.
+ mPipHighPerfSession = mPipPerfHintController.startSession(
+ this::onHighPerfSessionTimeout, "startBoundsAnimator");
+ }
if (postBoundsUpdateCallback != null) {
mTemporaryBoundsPhysicsAnimator
.addUpdateListener(mResizePipUpdateListener)
@@ -633,6 +655,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
mPipBoundsState.getMotionBoundsState().onPhysicsAnimationEnded();
mSpringingToTouch = false;
mDismissalPending = false;
+ cleanUpHighPerfSessionMaybe();
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index 5f9195a37c1b..89d3dd63a08e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -17,6 +17,7 @@ package com.android.wm.shell.pip.phone;
import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_NONE;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Point;
@@ -39,6 +40,7 @@ import com.android.wm.shell.R;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipPerfHintController;
import com.android.wm.shell.common.pip.PipPinchResizingAlgorithm;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.pip.PipAnimationController;
@@ -98,6 +100,12 @@ public class PipResizeGestureHandler {
private InputMonitor mInputMonitor;
private InputEventReceiver mInputEventReceiver;
+ @Nullable
+ private final PipPerfHintController mPipPerfHintController;
+
+ @Nullable
+ private PipPerfHintController.PipHighPerfSession mPipHighPerfSession;
+
private int mCtrlType;
private int mOhmOffset;
@@ -107,10 +115,11 @@ public class PipResizeGestureHandler {
PipDismissTargetHandler pipDismissTargetHandler,
Runnable updateMovementBoundsRunnable,
PipUiEventLogger pipUiEventLogger, PhonePipMenuController menuActivityController,
- ShellExecutor mainExecutor) {
+ ShellExecutor mainExecutor, @Nullable PipPerfHintController pipPerfHintController) {
mContext = context;
mDisplayId = context.getDisplayId();
mMainExecutor = mainExecutor;
+ mPipPerfHintController = pipPerfHintController;
mPipBoundsAlgorithm = pipBoundsAlgorithm;
mPipBoundsState = pipBoundsState;
mMotionHelper = motionHelper;
@@ -266,6 +275,16 @@ public class PipResizeGestureHandler {
return mIsSysUiStateValid;
}
+ private void onHighPerfSessionTimeout(PipPerfHintController.PipHighPerfSession session) {}
+
+ private void cleanUpHighPerfSessionMaybe() {
+ if (mPipHighPerfSession != null) {
+ // Close the high perf session once pointer interactions are over;
+ mPipHighPerfSession.close();
+ mPipHighPerfSession = null;
+ }
+ }
+
@VisibleForTesting
void onPinchResize(MotionEvent ev) {
int action = ev.getActionMasked();
@@ -275,6 +294,7 @@ public class PipResizeGestureHandler {
mSecondIndex = -1;
mAllowGesture = false;
finishResize();
+ cleanUpHighPerfSessionMaybe();
}
if (ev.getPointerCount() != 2) {
@@ -296,6 +316,12 @@ public class PipResizeGestureHandler {
mLastPoint.set(mDownPoint);
mLastSecondPoint.set(mLastSecondPoint);
mLastResizeBounds.set(mDownBounds);
+
+ // start the high perf session as the second pointer gets detected
+ if (mPipPerfHintController != null) {
+ mPipHighPerfSession = mPipPerfHintController.startSession(
+ this::onHighPerfSessionTimeout, "onPinchResize");
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchGesture.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchGesture.java
index 1a3cc8b1c1d2..fcac2c6b90ac 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchGesture.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchGesture.java
@@ -39,4 +39,9 @@ public abstract class PipTouchGesture {
public boolean onUp(PipTouchState touchState) {
return false;
}
+
+ /**
+ * Cleans up the high performance hint session if needed.
+ */
+ public void cleanUpHighPerfSessionMaybe() {}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index d5925d11ccf0..e7dd31cc1fa9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -25,8 +25,10 @@ import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTI
import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_FULL;
import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_NONE;
import static com.android.wm.shell.pip.phone.PipMenuView.ANIM_TYPE_NONE;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Context;
@@ -53,16 +55,17 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.pip.PipDoubleTapHelper;
+import com.android.wm.shell.common.pip.PipPerfHintController;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.common.pip.SizeSpecSource;
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
-import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.sysui.ShellInit;
import java.io.PrintWriter;
+import java.util.Optional;
/**
* Manages all the touch handling for PIP on the Phone, including moving, dismissing and expanding
@@ -83,6 +86,7 @@ public class PipTouchHandler {
private final PipDismissTargetHandler mPipDismissTargetHandler;
private final PipTaskOrganizer mPipTaskOrganizer;
private final ShellExecutor mMainExecutor;
+ @Nullable private final PipPerfHintController mPipPerfHintController;
private PipResizeGestureHandler mPipResizeGestureHandler;
@@ -172,9 +176,11 @@ public class PipTouchHandler {
PipMotionHelper pipMotionHelper,
FloatingContentCoordinator floatingContentCoordinator,
PipUiEventLogger pipUiEventLogger,
- ShellExecutor mainExecutor) {
+ ShellExecutor mainExecutor,
+ Optional<PipPerfHintController> pipPerfHintControllerOptional) {
mContext = context;
mMainExecutor = mainExecutor;
+ mPipPerfHintController = pipPerfHintControllerOptional.orElse(null);
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
mPipBoundsAlgorithm = pipBoundsAlgorithm;
mPipBoundsState = pipBoundsState;
@@ -208,7 +214,7 @@ public class PipTouchHandler {
new PipResizeGestureHandler(context, pipBoundsAlgorithm, pipBoundsState,
mMotionHelper, mTouchState, pipTaskOrganizer, mPipDismissTargetHandler,
this::updateMovementBounds, pipUiEventLogger,
- menuController, mainExecutor);
+ menuController, mainExecutor, mPipPerfHintController);
mConnection = new PipAccessibilityInteractionConnection(mContext, pipBoundsState,
mMotionHelper, pipTaskOrganizer, mPipBoundsAlgorithm.getSnapAlgorithm(),
this::onAccessibilityShowMenu, this::updateMovementBounds,
@@ -520,6 +526,7 @@ public class PipTouchHandler {
}
if (mPipResizeGestureHandler.hasOngoingGesture()) {
+ mGesture.cleanUpHighPerfSessionMaybe();
mPipDismissTargetHandler.hideDismissTargetMaybe();
return true;
}
@@ -542,7 +549,7 @@ public class PipTouchHandler {
// Ignore the motion event When the entry animation is waiting to be started
if (!mTouchState.isUserInteracting() && mPipTaskOrganizer.isEntryScheduled()) {
- ProtoLog.wtf(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ ProtoLog.wtf(WM_SHELL_PICTURE_IN_PICTURE,
"%s: Waiting to start the entry animation, skip the motion event.", TAG);
return true;
}
@@ -789,12 +796,31 @@ public class PipTouchHandler {
private final PointF mDelta = new PointF();
private boolean mShouldHideMenuAfterFling;
+ @Nullable private PipPerfHintController.PipHighPerfSession mPipHighPerfSession;
+
+ private void onHighPerfSessionTimeout(PipPerfHintController.PipHighPerfSession session) {}
+
+ @Override
+ public void cleanUpHighPerfSessionMaybe() {
+ if (mPipHighPerfSession != null) {
+ // Close the high perf session once pointer interactions are over;
+ mPipHighPerfSession.close();
+ mPipHighPerfSession = null;
+ }
+ }
+
@Override
public void onDown(PipTouchState touchState) {
if (!touchState.isUserInteracting()) {
return;
}
+ if (mPipPerfHintController != null) {
+ // Cache the PiP high perf session to close it upon touch up.
+ mPipHighPerfSession = mPipPerfHintController.startSession(
+ this::onHighPerfSessionTimeout, "DefaultPipTouchGesture#onDown");
+ }
+
Rect bounds = getPossiblyMotionBounds();
mDelta.set(0f, 0f);
mStartPosition.set(bounds.left, bounds.top);
@@ -937,6 +963,7 @@ public class PipTouchHandler {
mTouchState.scheduleDoubleTapTimeoutCallback();
}
}
+ cleanUpHighPerfSessionMaybe();
return true;
}
@@ -1028,7 +1055,7 @@ public class PipTouchHandler {
}
final Size estimatedMinMenuSize = mMenuController.getEstimatedMinMenuSize();
if (estimatedMinMenuSize == null) {
- ProtoLog.wtf(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ ProtoLog.wtf(WM_SHELL_PICTURE_IN_PICTURE,
"%s: Failed to get estimated menu size", TAG);
return false;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
index cac63eb2a2ad..614ef2ab9831 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
@@ -29,6 +29,7 @@ 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.PipMenuController;
+import com.android.wm.shell.common.pip.PipPerfHintController;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.pip.PipAnimationController;
@@ -59,6 +60,7 @@ public class TvPipTaskOrganizer extends PipTaskOrganizer {
@NonNull TvPipTransition tvPipTransition,
@NonNull PipParamsChangedForwarder pipParamsChangedForwarder,
Optional<SplitScreenController> splitScreenOptional,
+ Optional<PipPerfHintController> pipPerfHintControllerOptional,
@NonNull DisplayController displayController,
@NonNull PipUiEventLogger pipUiEventLogger,
@NonNull ShellTaskOrganizer shellTaskOrganizer,
@@ -66,8 +68,8 @@ public class TvPipTaskOrganizer extends PipTaskOrganizer {
super(context, syncTransactionQueue, pipTransitionState, pipBoundsState,
pipDisplayLayoutState, boundsHandler, pipMenuController, pipAnimationController,
surfaceTransactionHelper, tvPipTransition, pipParamsChangedForwarder,
- splitScreenOptional, displayController, pipUiEventLogger, shellTaskOrganizer,
- mainExecutor);
+ splitScreenOptional, pipPerfHintControllerOptional, displayController,
+ pipUiEventLogger, shellTaskOrganizer, mainExecutor);
mTvPipTransition = tvPipTransition;
}
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
index 186cb615f4ec..e73a85003881 100644
--- 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
@@ -18,14 +18,31 @@ package com.android.wm.shell.pip2.phone;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
+import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
+import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_PIP;
+
+import android.app.PictureInPictureParams;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
+import android.graphics.Rect;
import android.view.InsetsState;
+import android.view.SurfaceControl;
+
+import androidx.annotation.BinderThread;
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.ExternalInterfaceBinder;
+import com.android.wm.shell.common.RemoteCallable;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.pip.IPip;
+import com.android.wm.shell.common.pip.IPipAnimationListener;
+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.protolog.ShellProtoLogGroup;
@@ -37,32 +54,54 @@ 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 {
+ DisplayController.OnDisplaysChangedListener, RemoteCallable<PipController> {
private static final String TAG = PipController.class.getSimpleName();
private Context mContext;
private ShellController mShellController;
private DisplayController mDisplayController;
private DisplayInsetsController mDisplayInsetsController;
+ private PipBoundsState mPipBoundsState;
+ private PipBoundsAlgorithm mPipBoundsAlgorithm;
private PipDisplayLayoutState mPipDisplayLayoutState;
+ private PipScheduler mPipScheduler;
+ private ShellExecutor mMainExecutor;
private PipController(Context context,
ShellInit shellInit,
ShellController shellController,
DisplayController displayController,
DisplayInsetsController displayInsetsController,
- PipDisplayLayoutState pipDisplayLayoutState) {
+ PipBoundsState pipBoundsState,
+ PipBoundsAlgorithm pipBoundsAlgorithm,
+ PipDisplayLayoutState pipDisplayLayoutState,
+ PipScheduler pipScheduler,
+ ShellExecutor mainExecutor) {
mContext = context;
mShellController = shellController;
mDisplayController = displayController;
mDisplayInsetsController = displayInsetsController;
+ mPipBoundsState = pipBoundsState;
+ mPipBoundsAlgorithm = pipBoundsAlgorithm;
mPipDisplayLayoutState = pipDisplayLayoutState;
+ mPipScheduler = pipScheduler;
+ mMainExecutor = mainExecutor;
if (PipUtils.isPip2ExperimentEnabled()) {
shellInit.addInitCallback(this::onInit, this);
}
}
+ @Override
+ public Context getContext() {
+ return mContext;
+ }
+
+ @Override
+ public ShellExecutor getRemoteCallExecutor() {
+ return mMainExecutor;
+ }
+
private void onInit() {
// Ensure that we have the display info in case we get calls to update the bounds before the
// listener calls back
@@ -80,6 +119,10 @@ public class PipController implements ConfigurationChangeListener,
.getDisplayLayout(mPipDisplayLayoutState.getDisplayId()));
}
});
+
+ // Allow other outside processes to bind to PiP controller using the key below.
+ mShellController.addExternalInterface(KEY_EXTRA_SHELL_PIP,
+ this::createExternalInterface, this);
}
/**
@@ -90,16 +133,24 @@ public class PipController implements ConfigurationChangeListener,
ShellController shellController,
DisplayController displayController,
DisplayInsetsController displayInsetsController,
- PipDisplayLayoutState pipDisplayLayoutState) {
+ PipBoundsState pipBoundsState,
+ PipBoundsAlgorithm pipBoundsAlgorithm,
+ PipDisplayLayoutState pipDisplayLayoutState,
+ PipScheduler pipScheduler,
+ ShellExecutor mainExecutor) {
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);
+ displayInsetsController, pipBoundsState, pipBoundsAlgorithm, pipDisplayLayoutState,
+ pipScheduler, mainExecutor);
}
+ private ExternalInterfaceBinder createExternalInterface() {
+ return new IPipImpl(this);
+ }
@Override
public void onConfigurationChanged(Configuration newConfiguration) {
@@ -130,4 +181,86 @@ public class PipController implements ConfigurationChangeListener,
private void onDisplayChanged(DisplayLayout layout) {
mPipDisplayLayoutState.setDisplayLayout(layout);
}
+
+ private Rect getSwipePipToHomeBounds(ComponentName componentName, ActivityInfo activityInfo,
+ PictureInPictureParams pictureInPictureParams,
+ int launcherRotation, Rect hotseatKeepClearArea) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "getSwipePipToHomeBounds: %s", componentName);
+ mPipBoundsState.setBoundsStateForEntry(componentName, activityInfo, pictureInPictureParams,
+ mPipBoundsAlgorithm);
+ return mPipBoundsAlgorithm.getEntryDestinationBounds();
+ }
+
+ private void onSwipePipToHomeAnimationStart(int taskId, ComponentName componentName,
+ Rect destinationBounds, SurfaceControl overlay, Rect appBounds) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "onSwipePipToHomeAnimationStart: %s", componentName);
+ mPipScheduler.setInSwipePipToHomeTransition(true);
+ // TODO: cache the overlay if provided for reparenting later.
+ }
+
+ /**
+ * The interface for calls from outside the host process.
+ */
+ @BinderThread
+ private static class IPipImpl extends IPip.Stub implements ExternalInterfaceBinder {
+ private PipController mController;
+
+ IPipImpl(PipController controller) {
+ mController = controller;
+ }
+
+ /**
+ * Invalidates this instance, preventing future calls from updating the controller.
+ */
+ @Override
+ public void invalidate() {
+ mController = null;
+ }
+
+ @Override
+ public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
+ PictureInPictureParams pictureInPictureParams, int launcherRotation,
+ Rect keepClearArea) {
+ Rect[] result = new Rect[1];
+ executeRemoteCallWithTaskPermission(mController, "startSwipePipToHome",
+ (controller) -> {
+ result[0] = controller.getSwipePipToHomeBounds(componentName, activityInfo,
+ pictureInPictureParams, launcherRotation, keepClearArea);
+ }, true /* blocking */);
+ return result[0];
+ }
+
+ @Override
+ public void stopSwipePipToHome(int taskId, ComponentName componentName,
+ Rect destinationBounds, SurfaceControl overlay, Rect appBounds) {
+ if (overlay != null) {
+ overlay.setUnreleasedWarningCallSite("PipController.stopSwipePipToHome");
+ }
+ executeRemoteCallWithTaskPermission(mController, "stopSwipePipToHome",
+ (controller) -> controller.onSwipePipToHomeAnimationStart(
+ taskId, componentName, destinationBounds, overlay, appBounds));
+ }
+
+ @Override
+ public void abortSwipePipToHome(int taskId, ComponentName componentName) {}
+
+ @Override
+ public void setShelfHeight(boolean visible, int height) {}
+
+ @Override
+ public void setLauncherKeepClearAreaHeight(boolean visible, int height) {}
+
+ @Override
+ public void setLauncherAppIconSize(int iconSizePx) {}
+
+ @Override
+ public void setPipAnimationListener(IPipAnimationListener listener) {
+ // TODO: set a proper animation listener to update the Launcher state as needed.
+ }
+
+ @Override
+ public void setPipAnimationTypeToAlpha() {}
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
index 57b73b3019f4..895c793007a5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
@@ -63,6 +63,9 @@ public class PipScheduler {
@Nullable
private SurfaceControl mPinnedTaskLeash;
+ // true if Launcher has started swipe PiP to home animation
+ private boolean mInSwipePipToHomeTransition;
+
/**
* Temporary PiP CUJ codes to schedule PiP related transitions directly from Shell.
* This is used for a broadcast receiver to resolve intents. This should be removed once
@@ -168,6 +171,14 @@ public class PipScheduler {
mPipTransitionController.startResizeTransition(wct, onFinishResizeCallback);
}
+ void setInSwipePipToHomeTransition(boolean inSwipePipToHome) {
+ mInSwipePipToHomeTransition = true;
+ }
+
+ boolean isInSwipePipToHomeTransition() {
+ return mInSwipePipToHomeTransition;
+ }
+
void onExitPip() {
mPipTaskToken = null;
mPinnedTaskLeash = null;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index fbf4d13a0c19..dfb04758c851 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -152,6 +152,12 @@ public class PipTransition extends PipTransitionController {
@NonNull Transitions.TransitionFinishCallback finishCallback) {
if (transition == mEnterTransition) {
mEnterTransition = null;
+ if (mPipScheduler.isInSwipePipToHomeTransition()) {
+ // If this is the second transition as a part of swipe PiP to home cuj,
+ // handle this transition as a special case with no-op animation.
+ return handleSwipePipToHomeTransition(info, startTransaction, finishTransaction,
+ finishCallback);
+ }
if (isLegacyEnter(info)) {
// If this is a legacy-enter-pip (auto-enter is off and PiP activity went to pause),
// then we should run an ALPHA type (cross-fade) animation.
@@ -207,6 +213,25 @@ public class PipTransition extends PipTransitionController {
return true;
}
+ private boolean handleSwipePipToHomeTransition(@NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ TransitionInfo.Change pipChange = getPipChange(info);
+ if (pipChange == null) {
+ return false;
+ }
+ mPipScheduler.setInSwipePipToHomeTransition(false);
+ mPipTaskToken = pipChange.getContainer();
+
+ // cache the PiP task token and leash
+ mPipScheduler.setPipTaskToken(mPipTaskToken);
+
+ startTransaction.apply();
+ finishCallback.onTransitionFinished(null);
+ return true;
+ }
+
private boolean startBoundsTypeEnterAnimation(@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
index 05e4af3302af..ad29d15019c5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
@@ -26,6 +26,8 @@ import com.android.internal.protolog.common.IProtoLogGroup;
public enum ShellProtoLogGroup implements IProtoLogGroup {
// NOTE: Since we enable these from the same WM ShellCommand, these names should not conflict
// with those in the framework ProtoLogGroup
+ WM_SHELL(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
+ Consts.TAG_WM_SHELL),
WM_SHELL_INIT(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
Consts.TAG_WM_SHELL),
WM_SHELL_TASK_ORG(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java
deleted file mode 100644
index 93ffb3dc8115..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.protolog;
-
-import android.annotation.Nullable;
-
-import com.android.internal.protolog.BaseProtoLogImpl;
-import com.android.internal.protolog.ProtoLogViewerConfigReader;
-import com.android.internal.protolog.common.IProtoLogGroup;
-
-import java.io.File;
-import java.io.PrintWriter;
-
-
-/**
- * A service for the ProtoLog logging system.
- */
-public class ShellProtoLogImpl extends BaseProtoLogImpl {
- private static final String TAG = "ProtoLogImpl";
- private static final int BUFFER_CAPACITY = 1024 * 1024;
- // TODO: find a proper location to save the protolog message file
- private static final String LOG_FILENAME = "/data/misc/wmtrace/shell_log.winscope";
- private static final String VIEWER_CONFIG_FILENAME = "/system_ext/etc/wmshell.protolog.json.gz";
-
- private static ShellProtoLogImpl sServiceInstance = null;
-
- static {
- addLogGroupEnum(ShellProtoLogGroup.values());
- }
-
- /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
- public static void d(IProtoLogGroup group, int messageHash, int paramsMask,
- @Nullable String messageString,
- Object... args) {
- getSingleInstance()
- .log(LogLevel.DEBUG, group, messageHash, paramsMask, messageString, args);
- }
-
- /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
- public static void v(IProtoLogGroup group, int messageHash, int paramsMask,
- @Nullable String messageString,
- Object... args) {
- getSingleInstance().log(LogLevel.VERBOSE, group, messageHash, paramsMask, messageString,
- args);
- }
-
- /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
- public static void i(IProtoLogGroup group, int messageHash, int paramsMask,
- @Nullable String messageString,
- Object... args) {
- getSingleInstance().log(LogLevel.INFO, group, messageHash, paramsMask, messageString, args);
- }
-
- /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
- public static void w(IProtoLogGroup group, int messageHash, int paramsMask,
- @Nullable String messageString,
- Object... args) {
- getSingleInstance().log(LogLevel.WARN, group, messageHash, paramsMask, messageString, args);
- }
-
- /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
- public static void e(IProtoLogGroup group, int messageHash, int paramsMask,
- @Nullable String messageString,
- Object... args) {
- getSingleInstance()
- .log(LogLevel.ERROR, group, messageHash, paramsMask, messageString, args);
- }
-
- /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
- public static void wtf(IProtoLogGroup group, int messageHash, int paramsMask,
- @Nullable String messageString,
- Object... args) {
- getSingleInstance().log(LogLevel.WTF, group, messageHash, paramsMask, messageString, args);
- }
-
- /** Returns true iff logging is enabled for the given {@code IProtoLogGroup}. */
- public static boolean isEnabled(IProtoLogGroup group) {
- return group.isLogToLogcat()
- || (group.isLogToProto() && getSingleInstance().isProtoEnabled());
- }
-
- /**
- * Returns the single instance of the ProtoLogImpl singleton class.
- */
- public static synchronized ShellProtoLogImpl getSingleInstance() {
- if (sServiceInstance == null) {
- sServiceInstance = new ShellProtoLogImpl();
- }
- return sServiceInstance;
- }
-
- public int startTextLogging(String[] groups, PrintWriter pw) {
- mViewerConfig.loadViewerConfig(pw, VIEWER_CONFIG_FILENAME);
- return setLogging(true /* setTextLogging */, true, pw, groups);
- }
-
- public int stopTextLogging(String[] groups, PrintWriter pw) {
- return setLogging(true /* setTextLogging */, false, pw, groups);
- }
-
- private ShellProtoLogImpl() {
- super(new File(LOG_FILENAME), VIEWER_CONFIG_FILENAME, BUFFER_CAPACITY,
- new ProtoLogViewerConfigReader());
- }
-}
-
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index dffcc6df79d8..b5ea1b1b43ea 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -24,6 +24,7 @@ import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
import static android.view.WindowManager.TRANSIT_SLEEP;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
import static com.android.wm.shell.util.SplitBounds.KEY_EXTRA_SPLIT_BOUNDS;
@@ -929,7 +930,14 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
Slog.e(TAG, "Duplicate call to finish");
return;
}
- if (!toHome) {
+
+ boolean returningToApp = !toHome
+ && !mWillFinishToHome
+ && mPausingTasks != null
+ && mState == STATE_NORMAL;
+ if (returningToApp && allAppsAreTranslucent(mPausingTasks)) {
+ mHomeTransitionObserver.notifyHomeVisibilityChanged(true);
+ } else if (!toHome) {
// For some transitions, we may have notified home activity that it became visible.
// We need to notify the observer that we are no longer going home.
mHomeTransitionObserver.notifyHomeVisibilityChanged(false);
@@ -948,7 +956,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
if (toHome) wct.reorder(mRecentsTask, true /* toTop */);
else wct.restoreTransientOrder(mRecentsTask);
}
- if (!toHome && !mWillFinishToHome && mPausingTasks != null && mState == STATE_NORMAL) {
+ if (returningToApp) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, " returning to app");
// The gesture is returning to the pausing-task(s) rather than continuing with
// recents, so end the transition by moving the app back to the top (and also
@@ -1048,6 +1056,18 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
}
}
+ private boolean allAppsAreTranslucent(ArrayList<TaskState> tasks) {
+ if (tasks == null || tasks.isEmpty()) {
+ return false;
+ }
+ for (int i = tasks.size() - 1; i >= 0; --i) {
+ if (!tasks.get(i).mIsTranslucent) {
+ return false;
+ }
+ }
+ return true;
+ }
+
private void cleanUpPausingOrClosingTask(TaskState task, WindowContainerTransaction wct,
SurfaceControl.Transaction finishTransaction, boolean sendUserLeaveHint) {
if (!sendUserLeaveHint && task.isLeaf()) {
@@ -1118,6 +1138,9 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
/** The surface/leash of the task provided by Core. */
SurfaceControl mTaskSurface;
+ /** True when the task is translucent. */
+ final boolean mIsTranslucent;
+
/** The (local) animation-leash created for this task. Only non-null for leafs. */
@Nullable
SurfaceControl mLeash;
@@ -1126,6 +1149,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
mToken = change.getContainer();
mTaskInfo = change.getTaskInfo();
mTaskSurface = change.getLeash();
+ mIsTranslucent = (change.getFlags() & FLAG_TRANSLUCENT) != 0;
mLeash = leash;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 1b124c2168df..53dd981755d2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -23,25 +23,22 @@ import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
-import static android.view.WindowManager.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI;
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
+import static com.android.wm.shell.common.MultiInstanceHelper.getComponent;
+import static com.android.wm.shell.common.MultiInstanceHelper.getShortcutComponent;
+import static com.android.wm.shell.common.MultiInstanceHelper.samePackage;
import static com.android.wm.shell.common.split.SplitScreenConstants.KEY_EXTRA_WIDGET_INTENT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
-import static com.android.wm.shell.common.split.SplitScreenUtils.getComponent;
-import static com.android.wm.shell.common.split.SplitScreenUtils.getShortcutComponent;
import static com.android.wm.shell.common.split.SplitScreenUtils.isValidToSplit;
import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition;
-import static com.android.wm.shell.common.split.SplitScreenUtils.samePackage;
import static com.android.wm.shell.common.split.SplitScreenUtils.splitFailureMessage;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SPLIT_SCREEN;
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
@@ -51,7 +48,6 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.LauncherApps;
-import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.graphics.Rect;
import android.os.Bundle;
@@ -73,6 +69,8 @@ import android.window.WindowContainerTransaction;
import androidx.annotation.BinderThread;
import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.InstanceId;
@@ -86,6 +84,7 @@ import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.ExternalInterfaceBinder;
import com.android.wm.shell.common.LaunchAdjacentController;
+import com.android.wm.shell.common.MultiInstanceHelper;
import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SingleInstanceRemoteListener;
@@ -138,7 +137,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
public static final int EXIT_REASON_CHILD_TASK_ENTER_PIP = 9;
public static final int EXIT_REASON_RECREATE_SPLIT = 10;
public static final int EXIT_REASON_FULLSCREEN_SHORTCUT = 11;
- public static final int EXIT_REASON_ENTER_DESKTOP = 12;
+ public static final int EXIT_REASON_DESKTOP_MODE = 12;
@IntDef(value = {
EXIT_REASON_UNKNOWN,
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW,
@@ -152,7 +151,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
EXIT_REASON_CHILD_TASK_ENTER_PIP,
EXIT_REASON_RECREATE_SPLIT,
EXIT_REASON_FULLSCREEN_SHORTCUT,
- EXIT_REASON_ENTER_DESKTOP
+ EXIT_REASON_DESKTOP_MODE
})
@Retention(RetentionPolicy.SOURCE)
@interface ExitReason{}
@@ -176,7 +175,6 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
private final ShellTaskOrganizer mTaskOrganizer;
private final SyncTransactionQueue mSyncQueue;
private final Context mContext;
- private final PackageManager mPackageManager;
private final LauncherApps mLauncherApps;
private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
private final ShellExecutor mMainExecutor;
@@ -192,9 +190,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
private final LaunchAdjacentController mLaunchAdjacentController;
private final Optional<WindowDecorViewModel> mWindowDecorViewModel;
private final Optional<DesktopTasksController> mDesktopTasksController;
+ private final MultiInstanceHelper mMultiInstanceHelpher;
private final SplitScreenShellCommandHandler mSplitScreenShellCommandHandler;
- // A static allow list of apps which support multi-instance
- private final String[] mAppsSupportingMultiInstance;
@VisibleForTesting
StageCoordinator mStageCoordinator;
@@ -204,6 +201,10 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
private SurfaceControl mGoingToRecentsTasksLayer;
private SurfaceControl mStartingSplitTasksLayer;
+ /**
+ * @param stageCoordinator if null, a stage coordinator will be created when this controller is
+ * initialized. Can be non-null for testing purposes.
+ */
public SplitScreenController(Context context,
ShellInit shellInit,
ShellCommandHandler shellCommandHandler,
@@ -222,13 +223,14 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
LaunchAdjacentController launchAdjacentController,
Optional<WindowDecorViewModel> windowDecorViewModel,
Optional<DesktopTasksController> desktopTasksController,
+ @Nullable StageCoordinator stageCoordinator,
+ MultiInstanceHelper multiInstanceHelper,
ShellExecutor mainExecutor) {
mShellCommandHandler = shellCommandHandler;
mShellController = shellController;
mTaskOrganizer = shellTaskOrganizer;
mSyncQueue = syncQueue;
mContext = context;
- mPackageManager = context.getPackageManager();
mLauncherApps = context.getSystemService(LauncherApps.class);
mRootTDAOrganizer = rootTDAOrganizer;
mMainExecutor = mainExecutor;
@@ -243,65 +245,14 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
mLaunchAdjacentController = launchAdjacentController;
mWindowDecorViewModel = windowDecorViewModel;
mDesktopTasksController = desktopTasksController;
+ mStageCoordinator = stageCoordinator;
+ mMultiInstanceHelpher = multiInstanceHelper;
mSplitScreenShellCommandHandler = new SplitScreenShellCommandHandler(this);
// TODO(b/238217847): Temporarily add this check here until we can remove the dynamic
// override for this controller from the base module
if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) {
shellInit.addInitCallback(this::onInit, this);
}
-
- // TODO(255224696): Remove the config once having a way for client apps to opt-in
- // multi-instances split.
- mAppsSupportingMultiInstance = mContext.getResources()
- .getStringArray(R.array.config_appsSupportMultiInstancesSplit);
- }
-
- @VisibleForTesting
- SplitScreenController(Context context,
- ShellInit shellInit,
- ShellCommandHandler shellCommandHandler,
- ShellController shellController,
- ShellTaskOrganizer shellTaskOrganizer,
- SyncTransactionQueue syncQueue,
- RootTaskDisplayAreaOrganizer rootTDAOrganizer,
- DisplayController displayController,
- DisplayImeController displayImeController,
- DisplayInsetsController displayInsetsController,
- DragAndDropController dragAndDropController,
- Transitions transitions,
- TransactionPool transactionPool,
- IconProvider iconProvider,
- RecentTasksController recentTasks,
- LaunchAdjacentController launchAdjacentController,
- WindowDecorViewModel windowDecorViewModel,
- DesktopTasksController desktopTasksController,
- ShellExecutor mainExecutor,
- StageCoordinator stageCoordinator,
- String[] appsSupportingMultiInstance) {
- mShellCommandHandler = shellCommandHandler;
- mShellController = shellController;
- mTaskOrganizer = shellTaskOrganizer;
- mSyncQueue = syncQueue;
- mContext = context;
- mPackageManager = context.getPackageManager();
- mLauncherApps = context.getSystemService(LauncherApps.class);
- mRootTDAOrganizer = rootTDAOrganizer;
- mMainExecutor = mainExecutor;
- mDisplayController = displayController;
- mDisplayImeController = displayImeController;
- mDisplayInsetsController = displayInsetsController;
- mDragAndDropController = dragAndDropController;
- mTransitions = transitions;
- mTransactionPool = transactionPool;
- mIconProvider = iconProvider;
- mRecentTasksOptional = Optional.of(recentTasks);
- mLaunchAdjacentController = launchAdjacentController;
- mWindowDecorViewModel = Optional.of(windowDecorViewModel);
- mDesktopTasksController = Optional.of(desktopTasksController);
- mStageCoordinator = stageCoordinator;
- mSplitScreenShellCommandHandler = new SplitScreenShellCommandHandler(this);
- shellInit.addInitCallback(this::onInit, this);
- mAppsSupportingMultiInstance = appsSupportingMultiInstance;
}
public SplitScreen asSplitScreen() {
@@ -529,8 +480,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
/** Move the specified task to fullscreen, regardless of focus state. */
- public void moveTaskToFullscreen(int taskId) {
- mStageCoordinator.moveTaskToFullscreen(taskId);
+ public void moveTaskToFullscreen(int taskId, int exitReason) {
+ mStageCoordinator.moveTaskToFullscreen(taskId, exitReason);
}
public boolean isLaunchToSplit(TaskInfo taskInfo) {
@@ -613,8 +564,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
if (samePackage(packageName, getPackageName(reverseSplitPosition(position)),
user.getIdentifier(), getUserId(reverseSplitPosition(position)))) {
- if (supportsMultiInstanceSplit(getShortcutComponent(packageName, shortcutId, user,
- mLauncherApps))) {
+ if (mMultiInstanceHelpher.supportsMultiInstanceSplit(
+ getShortcutComponent(packageName, shortcutId, user, mLauncherApps))) {
activityOptions.setApplyMultipleTaskFlagForShortcut(true);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
} else if (isSplitScreenVisible()) {
@@ -647,7 +598,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
final int userId1 = shortcutInfo.getUserId();
final int userId2 = SplitScreenUtils.getUserId(taskId, mTaskOrganizer);
if (samePackage(packageName1, packageName2, userId1, userId2)) {
- if (supportsMultiInstanceSplit(shortcutInfo.getActivity())) {
+ if (mMultiInstanceHelpher.supportsMultiInstanceSplit(shortcutInfo.getActivity())) {
activityOptions.setApplyMultipleTaskFlagForShortcut(true);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
} else {
@@ -679,7 +630,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
final int userId1 = shortcutInfo.getUserId();
final int userId2 = SplitScreenUtils.getUserId(taskId, mTaskOrganizer);
if (samePackage(packageName1, packageName2, userId1, userId2)) {
- if (supportsMultiInstanceSplit(shortcutInfo.getActivity())) {
+ if (mMultiInstanceHelpher.supportsMultiInstanceSplit(shortcutInfo.getActivity())) {
activityOptions.setApplyMultipleTaskFlagForShortcut(true);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
} else {
@@ -718,7 +669,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
final int userId2 = SplitScreenUtils.getUserId(taskId, mTaskOrganizer);
if (samePackage(packageName1, packageName2, userId1, userId2)) {
- if (supportsMultiInstanceSplit(getComponent(pendingIntent))) {
+ if (mMultiInstanceHelpher.supportsMultiInstanceSplit(getComponent(pendingIntent))) {
fillInIntent = new Intent();
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
@@ -748,7 +699,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
final int userId2 = SplitScreenUtils.getUserId(taskId, mTaskOrganizer);
boolean setSecondIntentMultipleTask = false;
if (samePackage(packageName1, packageName2, userId1, userId2)) {
- if (supportsMultiInstanceSplit(getComponent(pendingIntent))) {
+ if (mMultiInstanceHelpher.supportsMultiInstanceSplit(getComponent(pendingIntent))) {
setSecondIntentMultipleTask = true;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
} else {
@@ -783,7 +734,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent1);
final String packageName2 = SplitScreenUtils.getPackageName(pendingIntent2);
if (samePackage(packageName1, packageName2, userId1, userId2)) {
- if (supportsMultiInstanceSplit(getComponent(pendingIntent1))) {
+ if (mMultiInstanceHelpher.supportsMultiInstanceSplit(getComponent(pendingIntent1))) {
fillInIntent1 = new Intent();
fillInIntent1.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
fillInIntent2 = new Intent();
@@ -820,7 +771,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
? ActivityOptions.fromBundle(options2) : ActivityOptions.makeBasic();
boolean setSecondIntentMultipleTask = false;
if (samePackage(packageName1, packageName2, userId1, userId2)) {
- if (supportsMultiInstanceSplit(getComponent(pendingIntent1))) {
+ if (mMultiInstanceHelpher.supportsMultiInstanceSplit(getComponent(pendingIntent1))) {
fillInIntent1 = new Intent();
fillInIntent1.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
setSecondIntentMultipleTask = true;
@@ -882,7 +833,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
return;
}
if (samePackage(packageName1, packageName2, userId1, userId2)) {
- if (supportsMultiInstanceSplit(getComponent(intent))) {
+ if (mMultiInstanceHelpher.supportsMultiInstanceSplit(getComponent(intent))) {
// Flag with MULTIPLE_TASK if this is launching the same activity into both sides of
// the split and there is no reusable background task.
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
@@ -942,66 +893,6 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
/**
- * Returns whether a specific component desires to be launched in multiple instances for
- * split screen.
- */
- @VisibleForTesting
- boolean supportsMultiInstanceSplit(@Nullable ComponentName componentName) {
- if (componentName == null || componentName.getPackageName() == null) {
- // TODO(b/262864589): Handle empty component case
- return false;
- }
-
- // Check the pre-defined allow list
- final String packageName = componentName.getPackageName();
- for (int i = 0; i < mAppsSupportingMultiInstance.length; i++) {
- if (mAppsSupportingMultiInstance[i].equals(packageName)) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
- "application=%s in allowlist supports multi-instance", packageName);
- return true;
- }
- }
-
- // Check the activity property first
- try {
- final PackageManager.Property activityProp = mPackageManager.getProperty(
- PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, componentName);
- // If the above call doesn't throw a NameNotFoundException, then the activity property
- // should override the application property value
- if (activityProp.isBoolean()) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
- "activity=%s supports multi-instance", componentName);
- return activityProp.getBoolean();
- } else {
- ProtoLog.w(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
- "Warning: property=%s for activity=%s has non-bool type=%d",
- PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName,
- activityProp.getType());
- }
- } catch (PackageManager.NameNotFoundException nnfe) {
- // Not specified in the activity, fall through
- }
-
- // Check the application property otherwise
- try {
- final PackageManager.Property appProp = mPackageManager.getProperty(
- PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName);
- if (appProp.isBoolean()) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
- "application=%s supports multi-instance", packageName);
- return appProp.getBoolean();
- } else {
- ProtoLog.w(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
- "Warning: property=%s for application=%s has non-bool type=%d",
- PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName, appProp.getType());
- }
- } catch (PackageManager.NameNotFoundException nnfe) {
- // Not specified in either application or activity
- }
- return false;
- }
-
- /**
* Determines whether the widgetIntent needs to be modified if multiple tasks of its
* corresponding package/app are supported. There are 4 possible paths:
* <li> We select a widget for second app which is the same as the first app </li>
@@ -1144,8 +1035,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
return "CHILD_TASK_ENTER_PIP";
case EXIT_REASON_RECREATE_SPLIT:
return "RECREATE_SPLIT";
- case EXIT_REASON_ENTER_DESKTOP:
- return "ENTER_DESKTOP";
+ case EXIT_REASON_DESKTOP_MODE:
+ return "DESKTOP_MODE";
default:
return "unknown reason, reason int = " + exitReason;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
index f4ab2266179a..a0bf843444df 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
@@ -25,7 +25,7 @@ import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED_
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DEVICE_FOLDED;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DRAG_DIVIDER;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__FULLSCREEN_SHORTCUT;
-import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__ENTER_DESKTOP;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DESKTOP_MODE;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RECREATE_SPLIT;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__ROOT_TASK_VANISHED;
@@ -43,7 +43,7 @@ import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DEVICE_FOLDED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
-import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_ENTER_DESKTOP;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DESKTOP_MODE;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_FULLSCREEN_SHORTCUT;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RECREATE_SPLIT;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME;
@@ -194,8 +194,8 @@ public class SplitscreenEventLogger {
return SPLITSCREEN_UICHANGED__EXIT_REASON__RECREATE_SPLIT;
case EXIT_REASON_FULLSCREEN_SHORTCUT:
return SPLITSCREEN_UICHANGED__EXIT_REASON__FULLSCREEN_SHORTCUT;
- case EXIT_REASON_ENTER_DESKTOP:
- return SPLITSCREEN_UICHANGED__EXIT_REASON__ENTER_DESKTOP;
+ case EXIT_REASON_DESKTOP_MODE:
+ return SPLITSCREEN_UICHANGED__EXIT_REASON__DESKTOP_MODE;
case EXIT_REASON_UNKNOWN:
// Fall through
default:
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 70b2f211d943..fa14b4c64fe0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -2973,7 +2973,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
/** Move the specified task to fullscreen, regardless of focus state. */
- public void moveTaskToFullscreen(int taskId) {
+ public void moveTaskToFullscreen(int taskId, int exitReason) {
boolean leftOrTop;
if (mMainStage.containsTask(taskId)) {
leftOrTop = (mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT);
@@ -2982,7 +2982,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
} else {
return;
}
- mSplitLayout.flingDividerToDismiss(!leftOrTop, EXIT_REASON_FULLSCREEN_SHORTCUT);
+ mSplitLayout.flingDividerToDismiss(!leftOrTop, exitReason);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
index aec4d1176dc0..e330f3ab65ab 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
@@ -28,6 +28,7 @@ import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.LaunchAdjacentController;
+import com.android.wm.shell.common.MultiInstanceHelper;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
@@ -79,6 +80,7 @@ public class TvSplitScreenController extends SplitScreenController {
IconProvider iconProvider,
Optional<RecentTasksController> recentTasks,
LaunchAdjacentController launchAdjacentController,
+ MultiInstanceHelper multiInstanceHelper,
ShellExecutor mainExecutor,
Handler mainHandler,
SystemWindows systemWindows) {
@@ -86,7 +88,7 @@ public class TvSplitScreenController extends SplitScreenController {
syncQueue, rootTDAOrganizer, displayController, displayImeController,
displayInsetsController, null, transitions, transactionPool,
iconProvider, recentTasks, launchAdjacentController, Optional.empty(),
- Optional.empty(), mainExecutor);
+ Optional.empty(), null /* stageCoordinator */, multiInstanceHelper, mainExecutor);
mTaskOrganizer = shellTaskOrganizer;
mSyncQueue = syncQueue;
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 5e79681e060b..b8a0f6703b97 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
@@ -493,11 +493,9 @@ public class Transitions implements RemoteCallable<Transitions>,
final SurfaceControl leash = change.getLeash();
final int mode = info.getChanges().get(i).getMode();
- if (mode == TRANSIT_TO_FRONT
- && ((change.getStartAbsBounds().height() != change.getEndAbsBounds().height()
- || change.getStartAbsBounds().width() != change.getEndAbsBounds().width()))) {
- // When the window is moved to front with a different size, make sure the crop is
- // updated to prevent it from using the old crop.
+ if (mode == TRANSIT_TO_FRONT) {
+ // When the window is moved to front, make sure the crop is updated to prevent it
+ // from using the old crop.
t.setWindowCrop(leash, change.getEndAbsBounds().width(),
change.getEndAbsBounds().height());
}
@@ -1170,7 +1168,11 @@ public class Transitions implements RemoteCallable<Transitions>,
mPendingTransitions.add(0, active);
}
- /** Start a new transition directly. */
+ /**
+ * Start a new transition directly.
+ * @param handler if null, the transition will be dispatched to the registered set of transition
+ * handlers to be handled
+ */
public IBinder startTransition(@WindowManager.TransitionType int type,
@NonNull WindowContainerTransaction wct, @Nullable TransitionHandler handler) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Directly starting a new transition "
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/KtProtoLog.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/util/KtProtoLog.kt
index 9b48a542720c..7a50814f0275 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/KtProtoLog.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/KtProtoLog.kt
@@ -18,7 +18,7 @@ package com.android.wm.shell.util
import android.util.Log
import com.android.internal.protolog.common.IProtoLogGroup
-import com.android.wm.shell.protolog.ShellProtoLogImpl
+import com.android.internal.protolog.common.ProtoLog
/**
* Log messages using an API similar to [com.android.internal.protolog.common.ProtoLog]. Useful for
@@ -31,42 +31,42 @@ class KtProtoLog {
companion object {
/** @see [com.android.internal.protolog.common.ProtoLog.d] */
fun d(group: IProtoLogGroup, messageString: String, vararg args: Any) {
- if (ShellProtoLogImpl.isEnabled(group)) {
+ if (ProtoLog.isEnabled(group)) {
Log.d(group.tag, String.format(messageString, *args))
}
}
/** @see [com.android.internal.protolog.common.ProtoLog.v] */
fun v(group: IProtoLogGroup, messageString: String, vararg args: Any) {
- if (ShellProtoLogImpl.isEnabled(group)) {
+ if (ProtoLog.isEnabled(group)) {
Log.v(group.tag, String.format(messageString, *args))
}
}
/** @see [com.android.internal.protolog.common.ProtoLog.i] */
fun i(group: IProtoLogGroup, messageString: String, vararg args: Any) {
- if (ShellProtoLogImpl.isEnabled(group)) {
+ if (ProtoLog.isEnabled(group)) {
Log.i(group.tag, String.format(messageString, *args))
}
}
/** @see [com.android.internal.protolog.common.ProtoLog.w] */
fun w(group: IProtoLogGroup, messageString: String, vararg args: Any) {
- if (ShellProtoLogImpl.isEnabled(group)) {
+ if (ProtoLog.isEnabled(group)) {
Log.w(group.tag, String.format(messageString, *args))
}
}
/** @see [com.android.internal.protolog.common.ProtoLog.e] */
fun e(group: IProtoLogGroup, messageString: String, vararg args: Any) {
- if (ShellProtoLogImpl.isEnabled(group)) {
+ if (ProtoLog.isEnabled(group)) {
Log.e(group.tag, String.format(messageString, *args))
}
}
/** @see [com.android.internal.protolog.common.ProtoLog.wtf] */
fun wtf(group: IProtoLogGroup, messageString: String, vararg args: Any) {
- if (ShellProtoLogImpl.isEnabled(group)) {
+ if (ProtoLog.isEnabled(group)) {
Log.wtf(group.tag, String.format(messageString, *args))
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 7db3d382ed8e..c1406d052195 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -21,11 +21,13 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
+import static android.view.MotionEvent.ACTION_HOVER_ENTER;
+import static android.view.MotionEvent.ACTION_HOVER_EXIT;
import static android.view.WindowInsets.Type.statusBars;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
-import static com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.FINAL_FREEFORM_SCALE;
import static com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.FREEFORM_ANIMATION_DURATION;
import static com.android.wm.shell.windowdecor.MoveToDesktopAnimator.DRAG_FREEFORM_SCALE;
@@ -310,8 +312,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
private class DesktopModeTouchEventListener extends GestureDetector.SimpleOnGestureListener
implements View.OnClickListener, View.OnTouchListener, View.OnLongClickListener,
- DragDetector.MotionEventHandler {
-
+ View.OnGenericMotionListener , DragDetector.MotionEventHandler {
+ private static final int CLOSE_MAXIMIZE_MENU_DELAY_MS = 150;
private final int mTaskId;
private final WindowContainerToken mTaskToken;
private final DragPositioningCallback mDragPositioningCallback;
@@ -322,6 +324,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
private boolean mHasLongClicked;
private boolean mShouldClick;
private int mDragPointerId = -1;
+ private final Runnable mCloseMaximizeWindowRunnable;
private DesktopModeTouchEventListener(
RunningTaskInfo taskInfo,
@@ -331,6 +334,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mDragPositioningCallback = dragPositioningCallback;
mDragDetector = new DragDetector(this);
mGestureDetector = new GestureDetector(mContext, this);
+ mCloseMaximizeWindowRunnable = () -> {
+ final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
+ if (decoration == null) return;
+ decoration.closeMaximizeMenu();
+ };
}
@Override
@@ -339,7 +347,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
final int id = v.getId();
if (id == R.id.close_window) {
if (isTaskInSplitScreen(mTaskId)) {
- mSplitScreenController.moveTaskToFullscreen(getOtherSplitTask(mTaskId).taskId);
+ mSplitScreenController.moveTaskToFullscreen(getOtherSplitTask(mTaskId).taskId,
+ SplitScreenController.EXIT_REASON_DESKTOP_MODE);
} else {
mTaskOperations.closeTask(mTaskToken);
}
@@ -365,7 +374,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
} else if (id == R.id.fullscreen_button) {
decoration.closeHandleMenu();
if (isTaskInSplitScreen(mTaskId)) {
- mSplitScreenController.moveTaskToFullscreen(mTaskId);
+ mSplitScreenController.moveTaskToFullscreen(mTaskId,
+ SplitScreenController.EXIT_REASON_DESKTOP_MODE);
} else {
mDesktopTasksController.ifPresent(c ->
c.moveToFullscreen(mTaskId));
@@ -384,13 +394,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mDesktopTasksController.ifPresent(c -> c.moveToNextDisplay(mTaskId));
}
} else if (id == R.id.maximize_window) {
- if (decoration.isMaximizeMenuActive()) {
- decoration.closeMaximizeMenu();
- return;
- }
final RunningTaskInfo taskInfo = decoration.mTaskInfo;
- mDesktopTasksController.ifPresent(c -> c.toggleDesktopTaskSize(taskInfo));
decoration.closeHandleMenu();
+ decoration.closeMaximizeMenu();
+ mDesktopTasksController.ifPresent(c -> c.toggleDesktopTaskSize(taskInfo));
} else if (id == R.id.maximize_menu_maximize_button) {
final RunningTaskInfo taskInfo = decoration.mTaskInfo;
mDesktopTasksController.ifPresent(c -> c.toggleDesktopTaskSize(taskInfo));
@@ -427,8 +434,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
}
final long eventDuration = e.getEventTime() - e.getDownTime();
- final boolean shouldLongClick = id == R.id.maximize_window && !mIsDragging
- && !mHasLongClicked && eventDuration >= ViewConfiguration.getLongPressTimeout();
+ final boolean isTouchScreen =
+ (e.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN;
+ final boolean shouldLongClick = isTouchScreen && id == R.id.maximize_window
+ && !mIsDragging && !mHasLongClicked
+ && eventDuration >= ViewConfiguration.getLongPressTimeout();
if (shouldLongClick) {
v.performLongClick();
mHasLongClicked = true;
@@ -454,6 +464,36 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
return false;
}
+ @Override
+ public boolean onGenericMotion(View v, MotionEvent ev) {
+ final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
+ final int id = v.getId();
+ if (ev.getAction() == ACTION_HOVER_ENTER) {
+ if (!decoration.isMaximizeMenuActive() && id == R.id.maximize_window) {
+ decoration.onMaximizeWindowHoverEnter();
+ } else if (id == R.id.maximize_window
+ || MaximizeMenu.Companion.isMaximizeMenuView(id)) {
+ // Re-hovering over any of the maximize menu views should keep the menu open by
+ // cancelling any attempts to close the menu.
+ mMainHandler.removeCallbacks(mCloseMaximizeWindowRunnable);
+ }
+ return true;
+ } else if (ev.getAction() == ACTION_HOVER_EXIT) {
+ if (!decoration.isMaximizeMenuActive() && id == R.id.maximize_window) {
+ decoration.onMaximizeWindowHoverExit();
+ } else if (id == R.id.maximize_window
+ || MaximizeMenu.Companion.isMaximizeMenuView(id)) {
+ // Close menu if not hovering over maximize menu or maximize button after a
+ // delay to give user a chance to re-enter view or to move from one maximize
+ // menu view to another.
+ mMainHandler.postDelayed(mCloseMaximizeWindowRunnable,
+ CLOSE_MAXIMIZE_MENU_DELAY_MS);
+ }
+ return true;
+ }
+ return false;
+ }
+
private void moveTaskToFront(RunningTaskInfo taskInfo) {
if (!taskInfo.isFocused) {
mDesktopTasksController.ifPresent(c -> c.moveTaskToFront(taskInfo));
@@ -726,7 +766,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mTransitionDragActive = false;
final int statusBarHeight = getStatusBarHeight(
relevantDecor.mTaskInfo.displayId);
- if (ev.getY() > 2 * statusBarHeight) {
+ if (ev.getRawY() > 2 * statusBarHeight) {
if (DesktopModeStatus.isEnabled()) {
animateToDesktop(relevantDecor, ev);
}
@@ -751,10 +791,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mDesktopTasksController.ifPresent(
c -> c.updateVisualIndicator(
relevantDecor.mTaskInfo,
- relevantDecor.mTaskSurface, ev.getX(), ev.getY()));
+ relevantDecor.mTaskSurface, ev.getRawX(), ev.getRawY()));
final int statusBarHeight = getStatusBarHeight(
relevantDecor.mTaskInfo.displayId);
- if (ev.getY() > statusBarHeight) {
+ if (ev.getRawY() > statusBarHeight) {
if (mMoveToDesktopAnimator == null) {
mMoveToDesktopAnimator = new MoveToDesktopAnimator(
mContext, mDragToDesktopAnimationStartBounds,
@@ -783,16 +823,16 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
* @param scale the amount to scale to relative to the Screen Bounds
*/
private Rect calculateFreeformBounds(int displayId, float scale) {
+ // TODO(b/319819547): Account for app constraints so apps do not become letterboxed
final DisplayLayout displayLayout = mDisplayController.getDisplayLayout(displayId);
final int screenWidth = displayLayout.width();
final int screenHeight = displayLayout.height();
final float adjustmentPercentage = (1f - scale) / 2;
- final Rect endBounds = new Rect((int) (screenWidth * adjustmentPercentage),
+ return new Rect((int) (screenWidth * adjustmentPercentage),
(int) (screenHeight * adjustmentPercentage),
(int) (screenWidth * (adjustmentPercentage + scale)),
(int) (screenHeight * (adjustmentPercentage + scale)));
- return endBounds;
}
/**
@@ -834,7 +874,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
c -> {
c.onDragPositioningEndThroughStatusBar(relevantDecor.mTaskInfo,
calculateFreeformBounds(ev.getDisplayId(),
- FINAL_FREEFORM_SCALE));
+ DesktopTasksController
+ .DESKTOP_MODE_INITIAL_BOUNDS_SCALE));
});
}
});
@@ -984,7 +1025,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
new DesktopModeTouchEventListener(taskInfo, dragPositioningCallback);
windowDecoration.setCaptionListeners(
- touchEventListener, touchEventListener, touchEventListener);
+ touchEventListener, touchEventListener, touchEventListener, touchEventListener);
windowDecoration.setExclusionRegionListener(mExclusionRegionListener);
windowDecoration.setDragPositioningCallback(dragPositioningCallback);
windowDecoration.setDragDetector(touchEventListener.mDragDetector);
@@ -1030,6 +1071,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
return;
}
decoration.showResizeVeil(t, bounds);
+ decoration.setAnimatingTaskResize(true);
}
@Override
@@ -1044,6 +1086,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskId);
if (decoration == null) return;
decoration.hideResizeVeil();
+ decoration.setAnimatingTaskResize(false);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 185365b2a501..74f460bf1226 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -60,6 +60,8 @@ import com.android.wm.shell.windowdecor.viewholder.DesktopModeAppControlsWindowD
import com.android.wm.shell.windowdecor.viewholder.DesktopModeFocusedWindowDecorationViewHolder;
import com.android.wm.shell.windowdecor.viewholder.DesktopModeWindowDecorationViewHolder;
+import kotlin.Unit;
+
import java.util.function.Supplier;
/**
@@ -79,6 +81,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private View.OnClickListener mOnCaptionButtonClickListener;
private View.OnTouchListener mOnCaptionTouchListener;
private View.OnLongClickListener mOnCaptionLongClickListener;
+ private View.OnGenericMotionListener mOnCaptionGenericMotionListener;
private DragPositioningCallback mDragPositioningCallback;
private DragResizeInputListener mDragResizeListener;
private DragDetector mDragDetector;
@@ -152,10 +155,12 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
void setCaptionListeners(
View.OnClickListener onCaptionButtonClickListener,
View.OnTouchListener onCaptionTouchListener,
- View.OnLongClickListener onLongClickListener) {
+ View.OnLongClickListener onLongClickListener,
+ View.OnGenericMotionListener onGenericMotionListener) {
mOnCaptionButtonClickListener = onCaptionButtonClickListener;
mOnCaptionTouchListener = onCaptionTouchListener;
mOnCaptionLongClickListener = onLongClickListener;
+ mOnCaptionGenericMotionListener = onGenericMotionListener;
}
void setExclusionRegionListener(ExclusionRegionListener exclusionRegionListener) {
@@ -225,9 +230,15 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mOnCaptionTouchListener,
mOnCaptionButtonClickListener,
mOnCaptionLongClickListener,
+ mOnCaptionGenericMotionListener,
mAppName,
- mAppIconBitmap
- );
+ mAppIconBitmap,
+ () -> {
+ if (!isMaximizeMenuActive()) {
+ createMaximizeMenu();
+ }
+ return Unit.INSTANCE;
+ });
} else {
throw new IllegalArgumentException("Unexpected layout resource id");
}
@@ -548,7 +559,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
*/
void createMaximizeMenu() {
mMaximizeMenu = new MaximizeMenu(mSyncQueue, mRootTaskDisplayAreaOrganizer,
- mDisplayController, mTaskInfo, mOnCaptionButtonClickListener, mContext,
+ mDisplayController, mTaskInfo, mOnCaptionButtonClickListener,
+ mOnCaptionGenericMotionListener, mOnCaptionTouchListener, mContext,
calculateMaximizeMenuPosition(), mSurfaceControlTransactionSupplier);
mMaximizeMenu.show();
}
@@ -776,6 +788,22 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
return R.id.desktop_mode_caption;
}
+ void setAnimatingTaskResize(boolean animatingTaskResize) {
+ if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_focused_window_decor) return;
+ ((DesktopModeAppControlsWindowDecorationViewHolder) mWindowDecorViewHolder)
+ .setAnimatingTaskResize(animatingTaskResize);
+ }
+
+ void onMaximizeWindowHoverExit() {
+ ((DesktopModeAppControlsWindowDecorationViewHolder) mWindowDecorViewHolder)
+ .onMaximizeWindowHoverExit();
+ }
+
+ void onMaximizeWindowHoverEnter() {
+ ((DesktopModeAppControlsWindowDecorationViewHolder) mWindowDecorViewHolder)
+ .onMaximizeWindowHoverEnter();
+ }
+
@Override
public String toString() {
return "{"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt
new file mode 100644
index 000000000000..b2f8cfdbfb7a
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2024 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.windowdecor
+
+import android.animation.AnimatorSet
+import android.animation.ObjectAnimator
+import android.animation.ValueAnimator
+import android.content.Context
+import android.content.res.ColorStateList
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.FrameLayout
+import android.widget.ImageButton
+import android.widget.ProgressBar
+import androidx.core.animation.doOnEnd
+import androidx.core.animation.doOnStart
+import androidx.core.content.ContextCompat
+import com.android.wm.shell.R
+
+private const val OPEN_MAXIMIZE_MENU_DELAY_ON_HOVER_MS = 350
+private const val MAX_DRAWABLE_ALPHA = 255
+
+class MaximizeButtonView(
+ context: Context,
+ attrs: AttributeSet
+) : FrameLayout(context, attrs) {
+ lateinit var onHoverAnimationFinishedListener: () -> Unit
+ private val hoverProgressAnimatorSet = AnimatorSet()
+ var hoverDisabled = false
+
+ private val progressBar: ProgressBar
+ private val maximizeWindow: ImageButton
+
+ init {
+ LayoutInflater.from(context).inflate(R.layout.maximize_menu_button, this, true)
+
+ progressBar = requireViewById(R.id.progress_bar)
+ maximizeWindow = requireViewById(R.id.maximize_window)
+ }
+
+ fun startHoverAnimation() {
+ if (hoverDisabled) return
+ if (hoverProgressAnimatorSet.isRunning) {
+ cancelHoverAnimation()
+ }
+
+ maximizeWindow.background.alpha = 0
+
+ hoverProgressAnimatorSet.playSequentially(
+ ValueAnimator.ofInt(0, MAX_DRAWABLE_ALPHA)
+ .setDuration(50)
+ .apply {
+ addUpdateListener {
+ maximizeWindow.background.alpha = animatedValue as Int
+ }
+ },
+ ObjectAnimator.ofInt(progressBar, "progress", 100)
+ .setDuration(OPEN_MAXIMIZE_MENU_DELAY_ON_HOVER_MS.toLong())
+ .apply {
+ doOnStart {
+ progressBar.setProgress(0, false)
+ progressBar.visibility = View.VISIBLE
+ }
+ doOnEnd {
+ progressBar.visibility = View.INVISIBLE
+ onHoverAnimationFinishedListener()
+ }
+ }
+ )
+ hoverProgressAnimatorSet.start()
+ }
+
+ fun cancelHoverAnimation() {
+ hoverProgressAnimatorSet.removeAllListeners()
+ hoverProgressAnimatorSet.cancel()
+ progressBar.visibility = View.INVISIBLE
+ }
+
+ fun setAnimationTints(darkMode: Boolean) {
+ if (darkMode) {
+ progressBar.progressTintList = ColorStateList.valueOf(
+ resources.getColor(R.color.desktop_mode_maximize_menu_progress_dark))
+ maximizeWindow.background?.setTintList(ContextCompat.getColorStateList(context,
+ R.color.desktop_mode_caption_button_color_selector_dark))
+ } else {
+ progressBar.progressTintList = ColorStateList.valueOf(
+ resources.getColor(R.color.desktop_mode_maximize_menu_progress_light))
+ maximizeWindow.background?.setTintList(ContextCompat.getColorStateList(context,
+ R.color.desktop_mode_caption_button_color_selector_light))
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
index 794b357c9f16..b82f7ca47ef3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.windowdecor
+import android.annotation.IdRes
import android.app.ActivityManager.RunningTaskInfo
import android.content.Context
import android.content.res.Resources
@@ -27,6 +28,8 @@ import android.view.SurfaceControl
import android.view.SurfaceControl.Transaction
import android.view.SurfaceControlViewHost
import android.view.View.OnClickListener
+import android.view.View.OnGenericMotionListener
+import android.view.View.OnTouchListener
import android.view.WindowManager
import android.view.WindowlessWindowManager
import android.widget.Button
@@ -49,6 +52,8 @@ class MaximizeMenu(
private val displayController: DisplayController,
private val taskInfo: RunningTaskInfo,
private val onClickListener: OnClickListener,
+ private val onGenericMotionListener: OnGenericMotionListener,
+ private val onTouchListener: OnTouchListener,
private val decorWindowContext: Context,
private val menuPosition: PointF,
private val transactionSupplier: Supplier<Transaction> = Supplier { Transaction() }
@@ -142,15 +147,26 @@ class MaximizeMenu(
private fun setupMaximizeMenu() {
val maximizeMenuView = maximizeMenu?.mWindowViewHost?.view ?: return
- maximizeMenuView.requireViewById<Button>(
+ maximizeMenuView.setOnGenericMotionListener(onGenericMotionListener)
+ maximizeMenuView.setOnTouchListener(onTouchListener)
+
+ val maximizeButton = maximizeMenuView.requireViewById<Button>(
R.id.maximize_menu_maximize_button
- ).setOnClickListener(onClickListener)
- maximizeMenuView.requireViewById<Button>(
+ )
+ maximizeButton.setOnClickListener(onClickListener)
+ maximizeButton.setOnGenericMotionListener(onGenericMotionListener)
+
+ val snapRightButton = maximizeMenuView.requireViewById<Button>(
R.id.maximize_menu_snap_right_button
- ).setOnClickListener(onClickListener)
- maximizeMenuView.requireViewById<Button>(
+ )
+ snapRightButton.setOnClickListener(onClickListener)
+ snapRightButton.setOnGenericMotionListener(onGenericMotionListener)
+
+ val snapLeftButton = maximizeMenuView.requireViewById<Button>(
R.id.maximize_menu_snap_left_button
- ).setOnClickListener(onClickListener)
+ )
+ snapLeftButton.setOnClickListener(onClickListener)
+ snapLeftButton.setOnGenericMotionListener(onGenericMotionListener)
}
/**
@@ -173,4 +189,12 @@ class MaximizeMenu(
private fun viewsLaidOut(): Boolean {
return maximizeMenu?.mWindowViewHost?.view?.isLaidOut ?: false
}
+
+ companion object {
+ fun isMaximizeMenuView(@IdRes viewId: Int): Boolean {
+ return viewId == R.id.maximize_menu || viewId == R.id.maximize_menu_maximize_button ||
+ viewId == R.id.maximize_menu_snap_left_button ||
+ viewId == R.id.maximize_menu_snap_right_button
+ }
+ }
}
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 7c6e69eb1ec9..5c69d5542227 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
@@ -61,7 +61,6 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback,
private int mCtrlType;
private boolean mIsResizingOrAnimatingResize;
@Surface.Rotation private int mRotation;
- private boolean mVeilIsVisible;
public VeiledResizeTaskPositioner(ShellTaskOrganizer taskOrganizer,
DesktopModeWindowDecoration windowDecoration,
@@ -119,10 +118,9 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback,
if (isResizing() && DragPositioningCallbackUtility.changeBounds(mCtrlType,
mRepositionTaskBounds, mTaskBoundsAtDragStart, mStableBounds, delta,
mDisplayController, mDesktopWindowDecoration)) {
- mIsResizingOrAnimatingResize = true;
- if (!mVeilIsVisible) {
+ if (!mIsResizingOrAnimatingResize) {
mDesktopWindowDecoration.showResizeVeil(mRepositionTaskBounds);
- mVeilIsVisible = true;
+ mIsResizingOrAnimatingResize = true;
} else {
mDesktopWindowDecoration.updateResizeVeil(mRepositionTaskBounds);
}
@@ -148,11 +146,10 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback,
final WindowContainerTransaction wct = new WindowContainerTransaction();
wct.setBounds(mDesktopWindowDecoration.mTaskInfo.token, mRepositionTaskBounds);
mTransitions.startTransition(TRANSIT_CHANGE, wct, this);
- } else if (mVeilIsVisible) {
+ } else {
// If bounds haven't changed, perform necessary veil reset here as startAnimation
// won't be called.
- mDesktopWindowDecoration.hideResizeVeil();
- mIsResizingOrAnimatingResize = false;
+ resetVeilIfVisible();
}
} else if (DragPositioningCallbackUtility.isBelowDisallowedArea(
mDisallowedAreaForEndBoundsHeight, mTaskBoundsAtDragStart, mRepositionStartPoint,
@@ -168,7 +165,6 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback,
mCtrlType = CTRL_TYPE_UNDEFINED;
mTaskBoundsAtDragStart.setEmpty();
mRepositionStartPoint.set(0, 0);
- mVeilIsVisible = false;
return new Rect(mRepositionTaskBounds);
}
@@ -177,6 +173,13 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback,
|| (mCtrlType & CTRL_TYPE_LEFT) != 0 || (mCtrlType & CTRL_TYPE_RIGHT) != 0;
}
+ private void resetVeilIfVisible() {
+ if (mIsResizingOrAnimatingResize) {
+ mDesktopWindowDecoration.hideResizeVeil();
+ mIsResizingOrAnimatingResize = false;
+ }
+ }
+
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@@ -192,7 +195,7 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback,
}
startTransaction.apply();
- mDesktopWindowDecoration.hideResizeVeil();
+ resetVeilIfVisible();
mCtrlType = CTRL_TYPE_UNDEFINED;
finishCallback.onTransitionFinished(null);
mIsResizingOrAnimatingResize = false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
index 2309c54b6591..7e5b9bd649f2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
@@ -21,6 +21,7 @@ import com.android.internal.R.attr.materialColorSurfaceContainerHigh
import com.android.internal.R.attr.materialColorSurfaceContainerLow
import com.android.internal.R.attr.materialColorSurfaceDim
import com.android.wm.shell.R
+import com.android.wm.shell.windowdecor.MaximizeButtonView
/**
* A desktop mode window decoration used when the window is floating (i.e. freeform). It hosts
@@ -32,8 +33,10 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder(
onCaptionTouchListener: View.OnTouchListener,
onCaptionButtonClickListener: View.OnClickListener,
onLongClickListener: OnLongClickListener,
+ onCaptionGenericMotionListener: View.OnGenericMotionListener,
appName: CharSequence,
- appIconBitmap: Bitmap
+ appIconBitmap: Bitmap,
+ onMaximizeHoverAnimationFinishedListener: () -> Unit
) : DesktopModeWindowDecorationViewHolder(rootView) {
private val captionView: View = rootView.requireViewById(R.id.desktop_mode_caption)
@@ -41,6 +44,8 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder(
private val openMenuButton: View = rootView.requireViewById(R.id.open_menu_button)
private val closeWindowButton: ImageButton = rootView.requireViewById(R.id.close_window)
private val expandMenuButton: ImageButton = rootView.requireViewById(R.id.expand_menu_button)
+ private val maximizeButtonView: MaximizeButtonView =
+ rootView.requireViewById(R.id.maximize_button_view)
private val maximizeWindowButton: ImageButton = rootView.requireViewById(R.id.maximize_window)
private val appNameTextView: TextView = rootView.requireViewById(R.id.application_name)
private val appIconImageView: ImageView = rootView.requireViewById(R.id.application_icon)
@@ -55,10 +60,13 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder(
closeWindowButton.setOnClickListener(onCaptionButtonClickListener)
maximizeWindowButton.setOnClickListener(onCaptionButtonClickListener)
maximizeWindowButton.setOnTouchListener(onCaptionTouchListener)
+ maximizeWindowButton.setOnGenericMotionListener(onCaptionGenericMotionListener)
maximizeWindowButton.onLongClickListener = onLongClickListener
closeWindowButton.setOnTouchListener(onCaptionTouchListener)
appNameTextView.text = appName
appIconImageView.setImageBitmap(appIconBitmap)
+ maximizeButtonView.onHoverAnimationFinishedListener =
+ onMaximizeHoverAnimationFinishedListener
}
override fun bindData(taskInfo: RunningTaskInfo) {
@@ -73,12 +81,30 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder(
maximizeWindowButton.imageAlpha = alpha
closeWindowButton.imageAlpha = alpha
expandMenuButton.imageAlpha = alpha
+
+ maximizeButtonView.setAnimationTints(isDarkMode())
}
override fun onHandleMenuOpened() {}
override fun onHandleMenuClosed() {}
+ fun setAnimatingTaskResize(animatingTaskResize: Boolean) {
+ // If animating a task resize, cancel any running hover animations
+ if (animatingTaskResize) {
+ maximizeButtonView.cancelHoverAnimation()
+ }
+ maximizeButtonView.hoverDisabled = animatingTaskResize
+ }
+
+ fun onMaximizeWindowHoverExit() {
+ maximizeButtonView.cancelHoverAnimation()
+ }
+
+ fun onMaximizeWindowHoverEnter() {
+ maximizeButtonView.startHoverAnimation()
+ }
+
@ColorInt
private fun getCaptionBackgroundColor(taskInfo: RunningTaskInfo): Int {
if (isTransparentBackgroundRequested(taskInfo)) {
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipWithSourceRectHintTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipWithSourceRectHintTest.kt
new file mode 100644
index 000000000000..8c0a524cda1e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipWithSourceRectHintTest.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2024 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.flicker.pip
+
+import android.platform.test.annotations.Presubmit
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test auto entering pip using a source rect hint.
+ *
+ * To run this test: `atest AutoEnterPipWithSourceRectHintTest`
+ *
+ * Actions:
+ * ```
+ * Launch an app in full screen
+ * Select "Auto-enter PiP" radio button
+ * Press "Set SourceRectHint" to create a temporary view that is used as the source rect hint
+ * Press Home button or swipe up to go Home and put [pipApp] in pip mode
+ * ```
+ *
+ * Notes:
+ * ```
+ * 1. All assertions are inherited from [AutoEnterPipOnGoToHomeTest]
+ * 2. Part of the test setup occurs automatically via
+ * [android.tools.device.flicker.legacy.runner.TransitionRunner],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
+ * ```
+ */
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class AutoEnterPipWithSourceRectHintTest(flicker: LegacyFlickerTest) :
+ AutoEnterPipOnGoToHomeTest(flicker) {
+ override val defaultEnterPip: FlickerBuilder.() -> Unit = {
+ setup {
+ pipApp.launchViaIntent(wmHelper)
+ pipApp.setSourceRectHint()
+ pipApp.enableAutoEnterForPipActivity()
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun pipOverlayNotShown() {
+ val overlay = ComponentNameMatcher.PIP_CONTENT_OVERLAY
+ flicker.assertLayers {
+ this.notContains(overlay)
+ }
+ }
+ @Presubmit
+ @Test
+ override fun pipOverlayLayerAppearThenDisappear() {
+ // we don't use overlay when entering with sourceRectHint
+ }
+
+ @Presubmit
+ @Test
+ override fun pipLayerOrOverlayRemainInsideVisibleBounds() {
+ // TODO (b/323511194): Looks like there is some bounciness with pip when using
+ // auto enter and sourceRectHint that causes the app to move outside of the display
+ // bounds during the transition.
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
index f3145c97a6f1..e6a2022166ea 100644
--- a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
@@ -113,7 +113,6 @@ class UnlockKeyguardToSplitScreen(override val flicker: LegacyFlickerTest) :
subject.name.contains(primaryApp.toLayerName()) && subject.isVisible
}
.mapNotNull { primaryApp -> primaryApp.layer.visibleRegion }
- .toTypedArray()
val primaryAppRegionArea = RegionSubject(primaryAppRegions, it.timestamp)
it.visibleRegion(secondaryApp).notOverlaps(primaryAppRegionArea.region)
diff --git a/libs/WindowManager/Shell/tests/unittest/AndroidManifest.xml b/libs/WindowManager/Shell/tests/unittest/AndroidManifest.xml
index 47a116be1b66..2ef425cf3d41 100644
--- a/libs/WindowManager/Shell/tests/unittest/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/unittest/AndroidManifest.xml
@@ -21,6 +21,7 @@
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
<uses-permission android:name="android.permission.VIBRATE"/>
+ <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS"/>
<application android:debuggable="true" android:largeHeap="true">
<uses-library android:name="android.test.mock" />
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiInstanceHelperTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiInstanceHelperTest.kt
new file mode 100644
index 000000000000..2f5fe11634a4
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiInstanceHelperTest.kt
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2024 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.common
+
+import android.app.ActivityTaskManager
+import android.content.ComponentName
+import android.content.pm.LauncherApps
+import android.content.pm.PackageManager
+import android.content.pm.ShortcutInfo
+import android.os.UserHandle
+import android.view.WindowManager.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.wm.shell.ShellTestCase
+import org.junit.Assert.assertEquals
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.doThrow
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+@RunWith(AndroidJUnit4::class)
+class MultiInstanceHelperTest : ShellTestCase() {
+
+ @Before
+ fun setup() {
+ assumeTrue(ActivityTaskManager.supportsSplitScreenMultiWindow(mContext))
+ }
+
+ @Test
+ fun getShortcutComponent_nullShortcuts() {
+ val launcherApps = mock<LauncherApps>()
+ whenever(launcherApps.getShortcuts(ArgumentMatchers.any(), ArgumentMatchers.any()))
+ .thenReturn(null)
+ assertEquals(null, MultiInstanceHelper.getShortcutComponent(TEST_PACKAGE,
+ TEST_SHORTCUT_ID, UserHandle.CURRENT, launcherApps))
+ }
+
+ @Test
+ fun getShortcutComponent_noShortcuts() {
+ val launcherApps = mock<LauncherApps>()
+ whenever(launcherApps.getShortcuts(ArgumentMatchers.any(), ArgumentMatchers.any()))
+ .thenReturn(ArrayList<ShortcutInfo>())
+ assertEquals(null, MultiInstanceHelper.getShortcutComponent(TEST_PACKAGE,
+ TEST_SHORTCUT_ID, UserHandle.CURRENT, launcherApps))
+ }
+
+ @Test
+ fun getShortcutComponent_validShortcut() {
+ val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
+ val shortcutInfo = ShortcutInfo.Builder(context, "id").setActivity(component).build()
+ val launcherApps = mock<LauncherApps>()
+ whenever(launcherApps.getShortcuts(ArgumentMatchers.any(), ArgumentMatchers.any()))
+ .thenReturn(arrayListOf(shortcutInfo))
+ assertEquals(component, MultiInstanceHelper.getShortcutComponent(TEST_PACKAGE,
+ TEST_SHORTCUT_ID, UserHandle.CURRENT, launcherApps))
+ }
+
+ @Test
+ fun supportsMultiInstanceSplit_inStaticAllowList() {
+ val allowList = arrayOf(TEST_PACKAGE)
+ val helper = MultiInstanceHelper(mContext, context.packageManager, allowList)
+ val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
+ assertEquals(true, helper.supportsMultiInstanceSplit(component))
+ }
+
+ @Test
+ fun supportsMultiInstanceSplit_notInStaticAllowList() {
+ val allowList = arrayOf(TEST_PACKAGE)
+ val helper = MultiInstanceHelper(mContext, context.packageManager, allowList)
+ val component = ComponentName(TEST_NOT_ALLOWED_PACKAGE, TEST_ACTIVITY)
+ assertEquals(false, helper.supportsMultiInstanceSplit(component))
+ }
+
+ @Test
+ @Throws(PackageManager.NameNotFoundException::class)
+ fun supportsMultiInstanceSplit_activityPropertyTrue() {
+ val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
+ val pm = mock<PackageManager>()
+ val activityProp = PackageManager.Property("", true, "", "")
+ whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+ eq(component)))
+ .thenReturn(activityProp)
+ val appProp = PackageManager.Property("", false, "", "")
+ whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+ eq(component.packageName)))
+ .thenReturn(appProp)
+
+ val helper = MultiInstanceHelper(mContext, pm, emptyArray())
+ // Expect activity property to override application property
+ assertEquals(true, helper.supportsMultiInstanceSplit(component))
+ }
+
+ @Test
+ @Throws(PackageManager.NameNotFoundException::class)
+ fun supportsMultiInstanceSplit_activityPropertyFalseApplicationPropertyTrue() {
+ val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
+ val pm = mock<PackageManager>()
+ val activityProp = PackageManager.Property("", false, "", "")
+ whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+ eq(component)))
+ .thenReturn(activityProp)
+ val appProp = PackageManager.Property("", true, "", "")
+ whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+ eq(component.packageName)))
+ .thenReturn(appProp)
+
+ val helper = MultiInstanceHelper(mContext, pm, emptyArray())
+ // Expect activity property to override application property
+ assertEquals(false, helper.supportsMultiInstanceSplit(component))
+ }
+
+ @Test
+ @Throws(PackageManager.NameNotFoundException::class)
+ fun supportsMultiInstanceSplit_noActivityPropertyApplicationPropertyTrue() {
+ val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
+ val pm = mock<PackageManager>()
+ whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+ eq(component)))
+ .thenThrow(PackageManager.NameNotFoundException())
+ val appProp = PackageManager.Property("", true, "", "")
+ whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+ eq(component.packageName)))
+ .thenReturn(appProp)
+
+ val helper = MultiInstanceHelper(mContext, pm, emptyArray())
+ // Expect fall through to app property
+ assertEquals(true, helper.supportsMultiInstanceSplit(component))
+ }
+
+ @Test
+ @Throws(PackageManager.NameNotFoundException::class)
+ fun supportsMultiInstanceSplit_noActivityOrAppProperty() {
+ val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
+ val pm = mock<PackageManager>()
+ whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+ eq(component)))
+ .thenThrow(PackageManager.NameNotFoundException())
+ whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+ eq(component.packageName)))
+ .thenThrow(PackageManager.NameNotFoundException())
+
+ val helper = MultiInstanceHelper(mContext, pm, emptyArray())
+ assertEquals(false, helper.supportsMultiInstanceSplit(component))
+ }
+
+ companion object {
+ val TEST_PACKAGE = "com.android.wm.shell.common"
+ val TEST_NOT_ALLOWED_PACKAGE = "com.android.wm.shell.common.fake";
+ val TEST_ACTIVITY = "TestActivity";
+ val TEST_SHORTCUT_ID = "test_shortcut_1"
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitScreenUtilsTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitScreenUtilsTests.kt
deleted file mode 100644
index 955660c396d0..000000000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitScreenUtilsTests.kt
+++ /dev/null
@@ -1,69 +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.wm.shell.common.split
-
-import android.content.ComponentName
-import android.content.pm.LauncherApps
-import android.content.pm.ShortcutInfo
-import android.os.UserHandle
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.wm.shell.ShellTestCase
-import org.junit.Assert.assertEquals
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.`when`
-
-@RunWith(AndroidJUnit4::class)
-class SplitScreenUtilsTests : ShellTestCase() {
-
- @Test
- fun getShortcutComponent_nullShortcuts() {
- val launcherApps = mock(LauncherApps::class.java).also {
- `when`(it.getShortcuts(any(), any())).thenReturn(null)
- }
- assertEquals(null, SplitScreenUtils.getShortcutComponent(TEST_PACKAGE,
- TEST_SHORTCUT_ID, UserHandle.CURRENT, launcherApps))
- }
-
- @Test
- fun getShortcutComponent_noShortcuts() {
- val launcherApps = mock(LauncherApps::class.java).also {
- `when`(it.getShortcuts(any(), any())).thenReturn(ArrayList<ShortcutInfo>())
- }
- assertEquals(null, SplitScreenUtils.getShortcutComponent(TEST_PACKAGE,
- TEST_SHORTCUT_ID, UserHandle.CURRENT, launcherApps))
- }
-
- @Test
- fun getShortcutComponent_validShortcut() {
- val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
- val shortcutInfo = ShortcutInfo.Builder(context, "id").setActivity(component).build()
- val launcherApps = mock(LauncherApps::class.java).also {
- `when`(it.getShortcuts(any(), any())).thenReturn(arrayListOf(shortcutInfo))
- }
- assertEquals(component, SplitScreenUtils.getShortcutComponent(TEST_PACKAGE,
- TEST_SHORTCUT_ID, UserHandle.CURRENT, launcherApps))
- }
-
- companion object {
- val TEST_PACKAGE = "com.android.wm.shell.common.split"
- val TEST_ACTIVITY = "TestActivity";
- val TEST_SHORTCUT_ID = "test_shortcut_1"
- }
-} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
index 9fe2cb11e804..81ba4b37d13b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
@@ -113,8 +113,22 @@ public class UserAspectRatioSettingsWindowManagerTest extends ShellTestCase {
mExecutor = new TestShellExecutor();
mTaskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */
false, /* topActivityBoundsLetterboxed */ true, ACTION_MAIN, CATEGORY_LAUNCHER);
+ final DisplayInfo displayInfo = new DisplayInfo();
+ final int displayWidth = 1000;
+ final int displayHeight = 1200;
+ displayInfo.logicalWidth = displayWidth;
+ displayInfo.logicalHeight = displayHeight;
+ final DisplayLayout displayLayout = new DisplayLayout(displayInfo,
+ mContext.getResources(), /* hasNavigationBar= */ true, /* hasStatusBar= */ false);
+ InsetsState insetsState = new InsetsState();
+ insetsState.setDisplayFrame(new Rect(0, 0, displayWidth, displayHeight));
+ InsetsSource insetsSource = new InsetsSource(
+ InsetsSource.createId(null, 0, navigationBars()), navigationBars());
+ insetsSource.setFrame(0, displayHeight - 200, displayWidth, displayHeight);
+ insetsState.addSource(insetsSource);
+ displayLayout.setInsets(mContext.getResources(), insetsState);
mWindowManager = new UserAspectRatioSettingsWindowManager(mContext, mTaskInfo,
- mSyncTransactionQueue, mTaskListener, new DisplayLayout(), new CompatUIHintsState(),
+ mSyncTransactionQueue, mTaskListener, displayLayout, new CompatUIHintsState(),
mOnUserAspectRatioSettingsButtonClicked, mExecutor, flags -> 0,
mUserAspectRatioButtonShownChecker, s -> {});
spyOn(mWindowManager);
@@ -253,6 +267,31 @@ public class UserAspectRatioSettingsWindowManagerTest extends ShellTestCase {
}
@Test
+ public void testEligibleButtonHiddenIfLetterboxBoundsEqualToStableBounds() {
+ TaskInfo taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */
+ true, /* topActivityBoundsLetterboxed */ true, ACTION_MAIN, CATEGORY_LAUNCHER);
+
+ final Rect stableBounds = mWindowManager.getTaskStableBounds();
+ final int stableHeight = stableBounds.height();
+
+ // Letterboxed activity bounds equal to stable bounds, layout shouldn't be inflated
+ taskInfo.appCompatTaskInfo.topActivityLetterboxHeight = stableHeight;
+ taskInfo.appCompatTaskInfo.topActivityLetterboxWidth = stableBounds.width();
+
+ mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true);
+
+ verify(mWindowManager, never()).inflateLayout();
+
+ // Letterboxed activity bounds smaller than stable bounds, layout should be inflated
+ taskInfo.appCompatTaskInfo.topActivityLetterboxHeight = stableHeight - 100;
+
+ clearInvocations(mWindowManager);
+ mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true);
+
+ verify(mWindowManager).inflateLayout();
+ }
+
+ @Test
public void testUpdateDisplayLayout() {
final DisplayInfo displayInfo = new DisplayInfo();
displayInfo.logicalWidth = 1000;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
new file mode 100644
index 000000000000..f8ce4ee8e1ce
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2024 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.desktopmode
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
+import android.graphics.Rect
+import android.graphics.Region
+import android.testing.AndroidTestingRunner
+import android.view.SurfaceControl
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.R
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.common.SyncTransactionQueue
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DesktopModeVisualIndicatorTest : ShellTestCase() {
+ @Mock private lateinit var taskInfo: RunningTaskInfo
+ @Mock private lateinit var syncQueue: SyncTransactionQueue
+ @Mock private lateinit var displayController: DisplayController
+ @Mock private lateinit var taskSurface: SurfaceControl
+ @Mock private lateinit var taskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+ @Mock private lateinit var displayLayout: DisplayLayout
+
+ private lateinit var visualIndicator: DesktopModeVisualIndicator
+
+ @Before
+ fun setUp() {
+ visualIndicator = DesktopModeVisualIndicator(syncQueue, taskInfo, displayController,
+ context, taskSurface, taskDisplayAreaOrganizer)
+ whenever(displayLayout.width()).thenReturn(DISPLAY_BOUNDS.width())
+ whenever(displayLayout.height()).thenReturn(DISPLAY_BOUNDS.height())
+ }
+
+ @Test
+ fun testFullscreenRegionCalculation() {
+ val transitionHeight = context.resources.getDimensionPixelSize(
+ R.dimen.desktop_mode_transition_area_height)
+ val fromFreeformWidth = mContext.resources.getDimensionPixelSize(
+ R.dimen.desktop_mode_fullscreen_from_desktop_width
+ )
+ val fromFreeformHeight = mContext.resources.getDimensionPixelSize(
+ R.dimen.desktop_mode_fullscreen_from_desktop_height
+ )
+ var testRegion = visualIndicator.calculateFullscreenRegion(displayLayout,
+ WINDOWING_MODE_FULLSCREEN, CAPTION_HEIGHT)
+ assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 2400, transitionHeight))
+ testRegion = visualIndicator.calculateFullscreenRegion(displayLayout,
+ WINDOWING_MODE_FREEFORM, CAPTION_HEIGHT)
+ assertThat(testRegion.bounds).isEqualTo(Rect(
+ DISPLAY_BOUNDS.width() / 2 - fromFreeformWidth / 2,
+ -50,
+ DISPLAY_BOUNDS.width() / 2 + fromFreeformWidth / 2,
+ fromFreeformHeight))
+ testRegion = visualIndicator.calculateFullscreenRegion(displayLayout,
+ WINDOWING_MODE_MULTI_WINDOW, CAPTION_HEIGHT)
+ assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 2400, transitionHeight))
+ }
+
+ @Test
+ fun testSplitLeftRegionCalculation() {
+ val transitionHeight = context.resources.getDimensionPixelSize(
+ R.dimen.desktop_mode_split_from_desktop_height)
+ var testRegion = visualIndicator.calculateSplitLeftRegion(displayLayout,
+ WINDOWING_MODE_FULLSCREEN, TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+ assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 32, 1600))
+ testRegion = visualIndicator.calculateSplitLeftRegion(displayLayout,
+ WINDOWING_MODE_FREEFORM, TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+ assertThat(testRegion.bounds).isEqualTo(Rect(0, transitionHeight, 32, 1600))
+ testRegion = visualIndicator.calculateSplitLeftRegion(displayLayout,
+ WINDOWING_MODE_MULTI_WINDOW, TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+ assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 32, 1600))
+ }
+
+ @Test
+ fun testSplitRightRegionCalculation() {
+ val transitionHeight = context.resources.getDimensionPixelSize(
+ R.dimen.desktop_mode_split_from_desktop_height)
+ var testRegion = visualIndicator.calculateSplitRightRegion(displayLayout,
+ WINDOWING_MODE_FULLSCREEN, TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+ assertThat(testRegion.bounds).isEqualTo(Rect(2368, -50, 2400, 1600))
+ testRegion = visualIndicator.calculateSplitRightRegion(displayLayout,
+ WINDOWING_MODE_FREEFORM, TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+ assertThat(testRegion.bounds).isEqualTo(Rect(2368, transitionHeight, 2400, 1600))
+ testRegion = visualIndicator.calculateSplitRightRegion(displayLayout,
+ WINDOWING_MODE_MULTI_WINDOW, TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+ assertThat(testRegion.bounds).isEqualTo(Rect(2368, -50, 2400, 1600))
+ }
+
+ @Test
+ fun testToDesktopRegionCalculation() {
+ val fullscreenRegion = visualIndicator.calculateFullscreenRegion(displayLayout,
+ WINDOWING_MODE_FULLSCREEN, CAPTION_HEIGHT)
+ val splitLeftRegion = visualIndicator.calculateSplitLeftRegion(displayLayout,
+ WINDOWING_MODE_FULLSCREEN, TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+ val splitRightRegion = visualIndicator.calculateSplitRightRegion(displayLayout,
+ WINDOWING_MODE_FULLSCREEN, TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+ val desktopRegion = visualIndicator.calculateToDesktopRegion(displayLayout,
+ WINDOWING_MODE_FULLSCREEN, splitLeftRegion, splitRightRegion, fullscreenRegion)
+ var testRegion = Region()
+ testRegion.union(DISPLAY_BOUNDS)
+ testRegion.op(splitLeftRegion, Region.Op.DIFFERENCE)
+ testRegion.op(splitRightRegion, Region.Op.DIFFERENCE)
+ testRegion.op(fullscreenRegion, Region.Op.DIFFERENCE)
+ assertThat(desktopRegion).isEqualTo(testRegion)
+ }
+
+ companion object {
+ private const val TRANSITION_AREA_WIDTH = 32
+ private const val CAPTION_HEIGHT = 50
+ private val DISPLAY_BOUNDS = Rect(0, 0, 2400, 1600)
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 79634e6040c4..383621beca22 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -48,12 +48,14 @@ import com.android.wm.shell.TestRunningTaskInfoBuilder
import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.LaunchAdjacentController
+import com.android.wm.shell.common.MultiInstanceHelper
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createHomeTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createSplitScreenTask
+import com.android.wm.shell.draganddrop.DragAndDropController
import com.android.wm.shell.recents.RecentsTransitionHandler
import com.android.wm.shell.recents.RecentsTransitionStateListener
import com.android.wm.shell.splitscreen.SplitScreenController
@@ -106,6 +108,8 @@ class DesktopTasksControllerTest : ShellTestCase() {
@Mock lateinit var desktopModeWindowDecoration: DesktopModeWindowDecoration
@Mock lateinit var splitScreenController: SplitScreenController
@Mock lateinit var recentsTransitionHandler: RecentsTransitionHandler
+ @Mock lateinit var dragAndDropController: DragAndDropController
+ @Mock lateinit var multiInstanceHelper: MultiInstanceHelper
private lateinit var mockitoSession: StaticMockitoSession
private lateinit var controller: DesktopTasksController
@@ -148,6 +152,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
shellTaskOrganizer,
syncQueue,
rootTaskDisplayAreaOrganizer,
+ dragAndDropController,
transitions,
enterDesktopTransitionHandler,
exitDesktopTransitionHandler,
@@ -156,6 +161,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
desktopModeTaskRepository,
launchAdjacentController,
recentsTransitionHandler,
+ multiInstanceHelper,
shellExecutor
)
}
@@ -373,7 +379,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
.isEqualTo(WINDOWING_MODE_FREEFORM)
verify(splitScreenController).prepareExitSplitScreen(any(), anyInt(),
- eq(SplitScreenController.EXIT_REASON_ENTER_DESKTOP)
+ eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE)
)
}
@@ -385,7 +391,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
.isEqualTo(WINDOWING_MODE_FREEFORM)
verify(splitScreenController, never()).prepareExitSplitScreen(any(), anyInt(),
- eq(SplitScreenController.EXIT_REASON_ENTER_DESKTOP)
+ eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE)
)
}
@@ -734,6 +740,46 @@ class DesktopTasksControllerTest : ShellTestCase() {
shellExecutor.flushAll()
verify(launchAdjacentController).launchAdjacentEnabled = true
}
+ @Test
+ fun enterDesktop_fullscreenTaskIsMovedToDesktop() {
+ val task1 = setUpFullscreenTask()
+ val task2 = setUpFullscreenTask()
+ val task3 = setUpFullscreenTask()
+
+ task1.isFocused = true
+ task2.isFocused = false
+ task3.isFocused = false
+
+ controller.enterDesktop(DEFAULT_DISPLAY)
+
+ val wct = getLatestMoveToDesktopWct()
+ assertThat(wct.changes[task1.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ }
+
+ @Test
+ fun enterDesktop_splitScreenTaskIsMovedToDesktop() {
+ val task1 = setUpSplitScreenTask()
+ val task2 = setUpFullscreenTask()
+ val task3 = setUpFullscreenTask()
+ val task4 = setUpSplitScreenTask()
+
+ task1.isFocused = true
+ task2.isFocused = false
+ task3.isFocused = false
+ task4.isFocused = true
+
+ task4.parentTaskId = task1.taskId
+
+ controller.enterDesktop(DEFAULT_DISPLAY)
+
+ val wct = getLatestMoveToDesktopWct()
+ assertThat(wct.changes[task4.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ verify(splitScreenController).prepareExitSplitScreen(any(), anyInt(),
+ eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE)
+ )
+ }
private fun setUpFreeformTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
val task = createFreeformTask(displayId)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java
index 54f36f61859d..a64ebd301c00 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java
@@ -45,12 +45,14 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
import com.android.launcher3.icons.IconProvider;
+import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.Transitions;
import org.junit.Before;
import org.junit.Test;
@@ -84,7 +86,9 @@ public class DragAndDropControllerTest extends ShellTestCase {
@Mock
private ShellExecutor mMainExecutor;
@Mock
- private WindowManager mWindowManager;
+ private Transitions mTransitions;
+ @Mock
+ private GlobalDragListener mGlobalDragListener;
private DragAndDropController mController;
@@ -93,7 +97,7 @@ public class DragAndDropControllerTest extends ShellTestCase {
MockitoAnnotations.initMocks(this);
mController = new DragAndDropController(mContext, mShellInit, mShellController,
mShellCommandHandler, mDisplayController, mUiEventLogger, mIconProvider,
- mMainExecutor);
+ mGlobalDragListener, mTransitions, mMainExecutor);
mController.onInit();
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/UnhandledDragControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/GlobalDragListenerTest.kt
index 522f05233f3a..e731b06c0947 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/UnhandledDragControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/GlobalDragListenerTest.kt
@@ -15,6 +15,7 @@
*/
package com.android.wm.shell.draganddrop
+import android.app.ActivityManager.RunningTaskInfo
import android.os.RemoteException
import android.view.DragEvent
import android.view.DragEvent.ACTION_DROP
@@ -25,19 +26,17 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.ShellExecutor
-import com.android.wm.shell.draganddrop.UnhandledDragController.UnhandledDragAndDropCallback
+import com.android.wm.shell.draganddrop.GlobalDragListener.GlobalDragListenerCallback
import java.util.function.Consumer
import junit.framework.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers
-import org.mockito.Mock
import org.mockito.Mockito
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.reset
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.reset
+import org.mockito.kotlin.verify
/**
* Tests for the unhandled drag controller.
@@ -45,35 +44,31 @@ import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidJUnit4::class)
class UnhandledDragControllerTest : ShellTestCase() {
- @Mock
- private lateinit var mIWindowManager: IWindowManager
+ private val mIWindowManager = mock<IWindowManager>()
+ private val mMainExecutor = mock<ShellExecutor>()
- @Mock
- private lateinit var mMainExecutor: ShellExecutor
-
- private lateinit var mController: UnhandledDragController
+ private lateinit var mController: GlobalDragListener
@Before
@Throws(RemoteException::class)
fun setUp() {
- MockitoAnnotations.initMocks(this)
- mController = UnhandledDragController(mIWindowManager, mMainExecutor)
+ mController = GlobalDragListener(mIWindowManager, mMainExecutor)
}
@Test
fun setListener_registersUnregistersWithWM() {
- mController.setListener(object : UnhandledDragAndDropCallback {})
- mController.setListener(object : UnhandledDragAndDropCallback {})
- mController.setListener(object : UnhandledDragAndDropCallback {})
+ mController.setListener(object : GlobalDragListenerCallback {})
+ mController.setListener(object : GlobalDragListenerCallback {})
+ mController.setListener(object : GlobalDragListenerCallback {})
verify(mIWindowManager, Mockito.times(1))
- .setUnhandledDragListener(ArgumentMatchers.any())
+ .setGlobalDragListener(ArgumentMatchers.any())
reset(mIWindowManager)
mController.setListener(null)
mController.setListener(null)
mController.setListener(null)
verify(mIWindowManager, Mockito.times(1))
- .setUnhandledDragListener(ArgumentMatchers.isNull())
+ .setGlobalDragListener(ArgumentMatchers.isNull())
}
@Test
@@ -81,7 +76,7 @@ class UnhandledDragControllerTest : ShellTestCase() {
// Simulate an unhandled drop
val dropEvent = DragEvent.obtain(ACTION_DROP, 0f, 0f, 0f, 0f, null, null, null,
null, null, false)
- val wmCallback = mock(IUnhandledDragCallback::class.java)
+ val wmCallback = mock<IUnhandledDragCallback>()
mController.onUnhandledDrop(dropEvent, wmCallback)
verify(wmCallback).notifyUnhandledDropComplete(ArgumentMatchers.eq(false))
@@ -92,7 +87,7 @@ class UnhandledDragControllerTest : ShellTestCase() {
val lastDragEvent = arrayOfNulls<DragEvent>(1)
// Set a listener to listen for unhandled drops
- mController.setListener(object : UnhandledDragAndDropCallback {
+ mController.setListener(object : GlobalDragListenerCallback {
override fun onUnhandledDrop(dragEvent: DragEvent,
onFinishedCallback: Consumer<Boolean>) {
lastDragEvent[0] = dragEvent
@@ -102,14 +97,31 @@ class UnhandledDragControllerTest : ShellTestCase() {
})
// Simulate an unhandled drop
- val dragSurface = mock(SurfaceControl::class.java)
+ val dragSurface = mock<SurfaceControl>()
val dropEvent = DragEvent.obtain(ACTION_DROP, 0f, 0f, 0f, 0f, null, null, null,
dragSurface, null, false)
- val wmCallback = mock(IUnhandledDragCallback::class.java)
+ val wmCallback = mock<IUnhandledDragCallback>()
mController.onUnhandledDrop(dropEvent, wmCallback)
verify(wmCallback).notifyUnhandledDropComplete(ArgumentMatchers.eq(true))
verify(dragSurface).release()
assertEquals(lastDragEvent.get(0), dropEvent)
}
+
+ @Test
+ fun onCrossWindowDrop() {
+ val lastTaskInfo = arrayOfNulls<RunningTaskInfo>(1)
+
+ // Set a listener to listen for unhandled drops
+ mController.setListener(object : GlobalDragListenerCallback {
+ override fun onCrossWindowDrop(taskInfo: RunningTaskInfo) {
+ lastTaskInfo[0] = taskInfo
+ }
+ })
+
+ // Simulate a cross-window drop
+ val taskInfo = mock<RunningTaskInfo>()
+ mController.onCrossWindowDrop(taskInfo)
+ assertEquals(lastTaskInfo.get(0), taskInfo)
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
index 46259a8b177f..080b0ae006ea 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
@@ -354,14 +354,17 @@ public class PipBoundsAlgorithmTest extends ShellTestCase {
}
@Test
- public void getEntryDestinationBounds_reentryStateExists_restoreLastSize() {
+ public void getEntryDestinationBounds_reentryStateExists_restoreProportionalSize() {
mPipBoundsState.setAspectRatio(DEFAULT_ASPECT_RATIO);
+ final Size maxSize = mSizeSpecSource.getMaxSize(DEFAULT_ASPECT_RATIO);
+ mPipBoundsState.setMaxSize(maxSize.getWidth(), maxSize.getHeight());
final Rect reentryBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
reentryBounds.scale(1.25f);
+ mPipBoundsState.setBounds(reentryBounds); // this updates the bounds scale used in reentry
+
final float reentrySnapFraction = mPipBoundsAlgorithm.getSnapFraction(reentryBounds);
- mPipBoundsState.saveReentryState(
- new Size(reentryBounds.width(), reentryBounds.height()), reentrySnapFraction);
+ mPipBoundsState.saveReentryState(reentrySnapFraction);
final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
assertEquals(reentryBounds.width(), destinationBounds.width());
@@ -375,8 +378,7 @@ public class PipBoundsAlgorithmTest extends ShellTestCase {
reentryBounds.offset(0, -100);
final float reentrySnapFraction = mPipBoundsAlgorithm.getSnapFraction(reentryBounds);
- mPipBoundsState.saveReentryState(
- new Size(reentryBounds.width(), reentryBounds.height()), reentrySnapFraction);
+ mPipBoundsState.saveReentryState(reentrySnapFraction);
final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
index db98abbbcbf1..304da75f870c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
@@ -114,22 +114,19 @@ public class PipBoundsStateTest extends ShellTestCase {
@Test
public void testSetReentryState() {
- final Size size = new Size(100, 100);
final float snapFraction = 0.5f;
- mPipBoundsState.saveReentryState(size, snapFraction);
+ mPipBoundsState.saveReentryState(snapFraction);
final PipBoundsState.PipReentryState state = mPipBoundsState.getReentryState();
- assertEquals(size, state.getSize());
assertEquals(snapFraction, state.getSnapFraction(), 0.01);
}
@Test
public void testClearReentryState() {
- final Size size = new Size(100, 100);
final float snapFraction = 0.5f;
- mPipBoundsState.saveReentryState(size, snapFraction);
+ mPipBoundsState.saveReentryState(snapFraction);
mPipBoundsState.clearReentryState();
assertNull(mPipBoundsState.getReentryState());
@@ -138,20 +135,19 @@ public class PipBoundsStateTest extends ShellTestCase {
@Test
public void testSetLastPipComponentName_notChanged_doesNotClearReentryState() {
mPipBoundsState.setLastPipComponentName(mTestComponentName1);
- mPipBoundsState.saveReentryState(DEFAULT_SIZE, DEFAULT_SNAP_FRACTION);
+ mPipBoundsState.saveReentryState(DEFAULT_SNAP_FRACTION);
mPipBoundsState.setLastPipComponentName(mTestComponentName1);
final PipBoundsState.PipReentryState state = mPipBoundsState.getReentryState();
assertNotNull(state);
- assertEquals(DEFAULT_SIZE, state.getSize());
assertEquals(DEFAULT_SNAP_FRACTION, state.getSnapFraction(), 0.01);
}
@Test
public void testSetLastPipComponentName_changed_clearReentryState() {
mPipBoundsState.setLastPipComponentName(mTestComponentName1);
- mPipBoundsState.saveReentryState(DEFAULT_SIZE, DEFAULT_SNAP_FRACTION);
+ mPipBoundsState.saveReentryState(DEFAULT_SNAP_FRACTION);
mPipBoundsState.setLastPipComponentName(mTestComponentName2);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 800f9e4e5371..d4e9ac96d221 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -118,7 +118,8 @@ public class PipTaskOrganizerTest extends ShellTestCase {
mPipTransitionState, mPipBoundsState, mPipDisplayLayoutState,
mPipBoundsAlgorithm, mMockPhonePipMenuController, mMockPipAnimationController,
mMockPipSurfaceTransactionHelper, mMockPipTransitionController,
- mMockPipParamsChangedForwarder, mMockOptionalSplitScreen, mMockDisplayController,
+ mMockPipParamsChangedForwarder, mMockOptionalSplitScreen,
+ Optional.empty() /* pipPerfHintControllerOptional */, mMockDisplayController,
mMockPipUiEventLogger, mMockShellTaskOrganizer, mMainExecutor);
mMainExecutor.flushAll();
preparePipTaskOrg();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index 4eb519334e12..5d968d3360ab 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -45,7 +45,6 @@ import android.os.RemoteException;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.util.Size;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.WindowManagerShellWrapper;
@@ -256,40 +255,13 @@ public class PipControllerTest extends ShellTestCase {
}
@Test
- public void saveReentryState_noUserResize_doesNotSaveSize() {
+ public void saveReentryState_savesPipBoundsState() {
final Rect bounds = new Rect(0, 0, 10, 10);
when(mMockPipBoundsAlgorithm.getSnapFraction(bounds)).thenReturn(1.0f);
- when(mMockPipBoundsState.hasUserResizedPip()).thenReturn(false);
mPipController.saveReentryState(bounds);
- verify(mMockPipBoundsState).saveReentryState(null, 1.0f);
- }
-
- @Test
- public void saveReentryState_nonEmptyUserResizeBounds_savesSize() {
- final Rect bounds = new Rect(0, 0, 10, 10);
- final Rect resizedBounds = new Rect(0, 0, 30, 30);
- when(mMockPipBoundsAlgorithm.getSnapFraction(bounds)).thenReturn(1.0f);
- when(mMockPipTouchHandler.getUserResizeBounds()).thenReturn(resizedBounds);
- when(mMockPipBoundsState.hasUserResizedPip()).thenReturn(true);
-
- mPipController.saveReentryState(bounds);
-
- verify(mMockPipBoundsState).saveReentryState(new Size(30, 30), 1.0f);
- }
-
- @Test
- public void saveReentryState_emptyUserResizeBounds_savesSize() {
- final Rect bounds = new Rect(0, 0, 10, 10);
- final Rect resizedBounds = new Rect(0, 0, 0, 0);
- when(mMockPipBoundsAlgorithm.getSnapFraction(bounds)).thenReturn(1.0f);
- when(mMockPipTouchHandler.getUserResizeBounds()).thenReturn(resizedBounds);
- when(mMockPipBoundsState.hasUserResizedPip()).thenReturn(true);
-
- mPipController.saveReentryState(bounds);
-
- verify(mMockPipBoundsState).saveReentryState(new Size(10, 10), 1.0f);
+ verify(mMockPipBoundsState).saveReentryState(1.0f);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
index cc726cb0adcf..ace09a82d71c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
@@ -55,6 +55,8 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Optional;
+
/**
* Unit tests against {@link PipResizeGestureHandler}
*/
@@ -114,7 +116,8 @@ public class PipResizeGestureHandlerTest extends ShellTestCase {
mSizeSpecSource);
final PipMotionHelper motionHelper = new PipMotionHelper(mContext, mPipBoundsState,
mPipTaskOrganizer, mPhonePipMenuController, pipSnapAlgorithm,
- mMockPipTransitionController, mFloatingContentCoordinator);
+ mMockPipTransitionController, mFloatingContentCoordinator,
+ Optional.empty() /* pipPerfHintControllerOptional */);
mPipTouchState = new PipTouchState(ViewConfiguration.get(mContext),
() -> {}, () -> {}, mMainExecutor);
@@ -122,7 +125,7 @@ public class PipResizeGestureHandlerTest extends ShellTestCase {
mPipBoundsState, motionHelper, mPipTouchState, mPipTaskOrganizer,
mPipDismissTargetHandler,
() -> {}, mPipUiEventLogger, mPhonePipMenuController,
- mMainExecutor) {
+ mMainExecutor, null /* pipPerfHintController */) {
@Override
public void pilferPointers() {
// Overridden just to avoid calling into InputMonitor.
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
index 9aaabd130527..92762fa68550 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
@@ -51,6 +51,8 @@ import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.util.Optional;
+
/**
* Unit tests against {@link PipTouchHandler}, including but not limited to:
* - Update movement bounds based on new bounds
@@ -116,10 +118,12 @@ public class PipTouchHandlerTest extends ShellTestCase {
new PipKeepClearAlgorithmInterface() {}, mPipDisplayLayoutState, mSizeSpecSource);
PipMotionHelper pipMotionHelper = new PipMotionHelper(mContext, mPipBoundsState,
mPipTaskOrganizer, mPhonePipMenuController, mPipSnapAlgorithm,
- mMockPipTransitionController, mFloatingContentCoordinator);
+ mMockPipTransitionController, mFloatingContentCoordinator,
+ Optional.empty() /* pipPerfHintControllerOptional */);
mPipTouchHandler = new PipTouchHandler(mContext, mShellInit, mPhonePipMenuController,
mPipBoundsAlgorithm, mPipBoundsState, mSizeSpecSource, mPipTaskOrganizer,
- pipMotionHelper, mFloatingContentCoordinator, mPipUiEventLogger, mMainExecutor);
+ pipMotionHelper, mFloatingContentCoordinator, mPipUiEventLogger, mMainExecutor,
+ Optional.empty() /* pipPerfHintControllerOptional */);
// We aren't actually using ShellInit, so just call init directly
mPipTouchHandler.onInit();
mMotionHelper = Mockito.spy(mPipTouchHandler.getMotionHelper());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
index 7f3bfbb0e81d..315d97ed333b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
@@ -22,7 +22,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
-import static android.view.WindowManager.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
@@ -37,8 +36,6 @@ import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -49,10 +46,8 @@ import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.PendingIntent;
import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
import android.os.Bundle;
import androidx.test.annotation.UiThreadTest;
@@ -60,7 +55,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.launcher3.icons.IconProvider;
-import com.android.wm.shell.R;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
@@ -69,6 +63,7 @@ import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.LaunchAdjacentController;
+import com.android.wm.shell.common.MultiInstanceHelper;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
@@ -90,6 +85,8 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Optional;
+
/**
* Tests for {@link SplitScreenController}
*/
@@ -97,10 +94,6 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class SplitScreenControllerTests extends ShellTestCase {
- private static final String TEST_PACKAGE = "com.android.wm.shell.splitscreen";
- private static final String TEST_NOT_ALLOWED_PACKAGE = "com.android.wm.shell.splitscreen.fake";
- private static final String TEST_ACTIVITY = "TestActivity";
-
@Mock ShellInit mShellInit;
@Mock ShellCommandHandler mShellCommandHandler;
@Mock ShellTaskOrganizer mTaskOrganizer;
@@ -119,6 +112,7 @@ public class SplitScreenControllerTests extends ShellTestCase {
@Mock LaunchAdjacentController mLaunchAdjacentController;
@Mock WindowDecorViewModel mWindowDecorViewModel;
@Mock DesktopTasksController mDesktopTasksController;
+ @Mock MultiInstanceHelper mMultiInstanceHelper;
@Captor ArgumentCaptor<Intent> mIntentCaptor;
private ShellController mShellController;
@@ -128,17 +122,15 @@ public class SplitScreenControllerTests extends ShellTestCase {
public void setup() {
assumeTrue(ActivityTaskManager.supportsSplitScreenMultiWindow(mContext));
MockitoAnnotations.initMocks(this);
- String[] appsSupportingMultiInstance = mContext.getResources()
- .getStringArray(R.array.config_appsSupportMultiInstancesSplit);
mShellController = spy(new ShellController(mContext, mShellInit, mShellCommandHandler,
mMainExecutor));
mSplitScreenController = spy(new SplitScreenController(mContext, mShellInit,
mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
mRootTDAOrganizer, mDisplayController, mDisplayImeController,
mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
- mIconProvider, mRecentTasks, mLaunchAdjacentController, mWindowDecorViewModel,
- mDesktopTasksController, mMainExecutor, mStageCoordinator,
- appsSupportingMultiInstance));
+ mIconProvider, Optional.of(mRecentTasks), mLaunchAdjacentController,
+ Optional.of(mWindowDecorViewModel), Optional.of(mDesktopTasksController),
+ mStageCoordinator, mMultiInstanceHelper, mMainExecutor));
}
@Test
@@ -213,7 +205,7 @@ public class SplitScreenControllerTests extends ShellTestCase {
@Test
public void startIntent_multiInstancesSupported_appendsMultipleTaskFag() {
- doReturn(true).when(mSplitScreenController).supportsMultiInstanceSplit(any());
+ doReturn(true).when(mMultiInstanceHelper).supportsMultiInstanceSplit(any());
Intent startIntent = createStartIntent("startActivity");
PendingIntent pendingIntent =
PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
@@ -250,13 +242,13 @@ public class SplitScreenControllerTests extends ShellTestCase {
verify(mStageCoordinator).startTask(anyInt(), eq(SPLIT_POSITION_TOP_OR_LEFT),
isNull());
- verify(mSplitScreenController, never()).supportsMultiInstanceSplit(any());
+ verify(mMultiInstanceHelper, never()).supportsMultiInstanceSplit(any());
verify(mStageCoordinator, never()).switchSplitPosition(any());
}
@Test
public void startIntent_multiInstancesSupported_startTaskInBackgroundAfterSplitActivated() {
- doReturn(true).when(mSplitScreenController).supportsMultiInstanceSplit(any());
+ doReturn(true).when(mMultiInstanceHelper).supportsMultiInstanceSplit(any());
doNothing().when(mSplitScreenController).startTask(anyInt(), anyInt(), any());
Intent startIntent = createStartIntent("startActivity");
PendingIntent pendingIntent =
@@ -273,14 +265,14 @@ public class SplitScreenControllerTests extends ShellTestCase {
mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null,
SPLIT_POSITION_TOP_OR_LEFT, null);
- verify(mSplitScreenController, never()).supportsMultiInstanceSplit(any());
+ verify(mMultiInstanceHelper, never()).supportsMultiInstanceSplit(any());
verify(mStageCoordinator).startTask(anyInt(), eq(SPLIT_POSITION_TOP_OR_LEFT),
isNull());
}
@Test
public void startIntent_multiInstancesNotSupported_switchesPositionAfterSplitActivated() {
- doReturn(false).when(mSplitScreenController).supportsMultiInstanceSplit(any());
+ doReturn(false).when(mMultiInstanceHelper).supportsMultiInstanceSplit(any());
Intent startIntent = createStartIntent("startActivity");
PendingIntent pendingIntent =
PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
@@ -298,130 +290,6 @@ public class SplitScreenControllerTests extends ShellTestCase {
}
@Test
- public void supportsMultiInstanceSplit_inStaticAllowList() {
- String[] allowList = { TEST_PACKAGE };
- SplitScreenController controller = new SplitScreenController(mContext, mShellInit,
- mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
- mRootTDAOrganizer, mDisplayController, mDisplayImeController,
- mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
- mIconProvider, mRecentTasks, mLaunchAdjacentController, mWindowDecorViewModel,
- mDesktopTasksController, mMainExecutor, mStageCoordinator,
- allowList);
- ComponentName component = new ComponentName(TEST_PACKAGE, TEST_ACTIVITY);
- assertEquals(true, controller.supportsMultiInstanceSplit(component));
- }
-
- @Test
- public void supportsMultiInstanceSplit_notInStaticAllowList() {
- String[] allowList = { TEST_PACKAGE };
- SplitScreenController controller = new SplitScreenController(mContext, mShellInit,
- mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
- mRootTDAOrganizer, mDisplayController, mDisplayImeController,
- mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
- mIconProvider, mRecentTasks, mLaunchAdjacentController, mWindowDecorViewModel,
- mDesktopTasksController, mMainExecutor, mStageCoordinator,
- allowList);
- ComponentName component = new ComponentName(TEST_NOT_ALLOWED_PACKAGE, TEST_ACTIVITY);
- assertEquals(false, controller.supportsMultiInstanceSplit(component));
- }
-
- @Test
- public void supportsMultiInstanceSplit_activityPropertyTrue()
- throws PackageManager.NameNotFoundException {
- Context context = spy(mContext);
- ComponentName component = new ComponentName(TEST_PACKAGE, TEST_ACTIVITY);
- PackageManager pm = mock(PackageManager.class);
- doReturn(pm).when(context).getPackageManager();
- PackageManager.Property activityProp = new PackageManager.Property("", true, "", "");
- doReturn(activityProp).when(pm).getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
- eq(component));
- PackageManager.Property appProp = new PackageManager.Property("", false, "", "");
- doReturn(appProp).when(pm).getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
- eq(component.getPackageName()));
-
- SplitScreenController controller = new SplitScreenController(context, mShellInit,
- mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
- mRootTDAOrganizer, mDisplayController, mDisplayImeController,
- mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
- mIconProvider, mRecentTasks, mLaunchAdjacentController, mWindowDecorViewModel,
- mDesktopTasksController, mMainExecutor, mStageCoordinator,
- new String[0]);
- // Expect activity property to override application property
- assertEquals(true, controller.supportsMultiInstanceSplit(component));
- }
-
- @Test
- public void supportsMultiInstanceSplit_activityPropertyFalseApplicationPropertyTrue()
- throws PackageManager.NameNotFoundException {
- Context context = spy(mContext);
- ComponentName component = new ComponentName(TEST_PACKAGE, TEST_ACTIVITY);
- PackageManager pm = mock(PackageManager.class);
- doReturn(pm).when(context).getPackageManager();
- PackageManager.Property activityProp = new PackageManager.Property("", false, "", "");
- doReturn(activityProp).when(pm).getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
- eq(component));
- PackageManager.Property appProp = new PackageManager.Property("", true, "", "");
- doReturn(appProp).when(pm).getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
- eq(component.getPackageName()));
-
- SplitScreenController controller = new SplitScreenController(context, mShellInit,
- mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
- mRootTDAOrganizer, mDisplayController, mDisplayImeController,
- mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
- mIconProvider, mRecentTasks, mLaunchAdjacentController, mWindowDecorViewModel,
- mDesktopTasksController, mMainExecutor, mStageCoordinator,
- new String[0]);
- // Expect activity property to override application property
- assertEquals(false, controller.supportsMultiInstanceSplit(component));
- }
-
- @Test
- public void supportsMultiInstanceSplit_noActivityPropertyApplicationPropertyTrue()
- throws PackageManager.NameNotFoundException {
- Context context = spy(mContext);
- ComponentName component = new ComponentName(TEST_PACKAGE, TEST_ACTIVITY);
- PackageManager pm = mock(PackageManager.class);
- doReturn(pm).when(context).getPackageManager();
- doThrow(PackageManager.NameNotFoundException.class).when(pm).getProperty(
- eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI), eq(component));
- PackageManager.Property appProp = new PackageManager.Property("", true, "", "");
- doReturn(appProp).when(pm).getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
- eq(component.getPackageName()));
-
- SplitScreenController controller = new SplitScreenController(context, mShellInit,
- mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
- mRootTDAOrganizer, mDisplayController, mDisplayImeController,
- mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
- mIconProvider, mRecentTasks, mLaunchAdjacentController, mWindowDecorViewModel,
- mDesktopTasksController, mMainExecutor, mStageCoordinator,
- new String[0]);
- // Expect fall through to app property
- assertEquals(true, controller.supportsMultiInstanceSplit(component));
- }
-
- @Test
- public void supportsMultiInstanceSplit_noActivityOrAppProperty()
- throws PackageManager.NameNotFoundException {
- Context context = spy(mContext);
- ComponentName component = new ComponentName(TEST_PACKAGE, TEST_ACTIVITY);
- PackageManager pm = mock(PackageManager.class);
- doReturn(pm).when(context).getPackageManager();
- doThrow(PackageManager.NameNotFoundException.class).when(pm).getProperty(
- eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI), eq(component));
- doThrow(PackageManager.NameNotFoundException.class).when(pm).getProperty(
- eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI), eq(component.getPackageName()));
-
- SplitScreenController controller = new SplitScreenController(context, mShellInit,
- mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
- mRootTDAOrganizer, mDisplayController, mDisplayImeController,
- mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
- mIconProvider, mRecentTasks, mLaunchAdjacentController, mWindowDecorViewModel,
- mDesktopTasksController, mMainExecutor, mStageCoordinator,
- new String[0]);
- assertEquals(false, controller.supportsMultiInstanceSplit(component));
- }
-
- @Test
public void testSwitchSplitPosition_checksIsSplitScreenVisible() {
final String reason = "test";
when(mSplitScreenController.isSplitScreenVisible()).thenReturn(true, false);
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 8748dab581bb..46f636e2ae7f 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -117,6 +117,10 @@ bool AssetManager2::SetApkAssets(ApkAssetsList apk_assets, bool invalidate_cache
return true;
}
+void AssetManager2::PresetApkAssets(ApkAssetsList apk_assets) {
+ BuildDynamicRefTable(apk_assets);
+}
+
bool AssetManager2::SetApkAssets(std::initializer_list<ApkAssetsPtr> apk_assets,
bool invalidate_caches) {
return SetApkAssets(ApkAssetsList(apk_assets.begin(), apk_assets.size()), invalidate_caches);
@@ -432,13 +436,18 @@ bool AssetManager2::ContainsAllocatedTable() const {
return false;
}
-void AssetManager2::SetConfigurations(std::vector<ResTable_config> configurations) {
+void AssetManager2::SetConfigurations(std::vector<ResTable_config> configurations,
+ bool force_refresh) {
int diff = 0;
- if (configurations_.size() != configurations.size()) {
+ if (force_refresh) {
diff = -1;
} else {
- for (int i = 0; i < configurations_.size(); i++) {
- diff |= configurations_[i].diff(configurations[i]);
+ if (configurations_.size() != configurations.size()) {
+ diff = -1;
+ } else {
+ for (int i = 0; i < configurations_.size(); i++) {
+ diff |= configurations_[i].diff(configurations[i]);
+ }
}
}
configurations_ = std::move(configurations);
@@ -775,8 +784,7 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry(
bool has_locale = false;
if (result->config.locale == 0) {
if (default_locale_ != 0) {
- ResTable_config conf;
- conf.locale = default_locale_;
+ ResTable_config conf = {.locale = default_locale_};
// Since we know conf has a locale and only a locale, match will tell us if that locale
// matches
has_locale = conf.match(config);
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index d9ff35b49e0a..17a8ba6c03bd 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -124,6 +124,9 @@ class AssetManager2 {
// new resource IDs.
bool SetApkAssets(ApkAssetsList apk_assets, bool invalidate_caches = true);
bool SetApkAssets(std::initializer_list<ApkAssetsPtr> apk_assets, bool invalidate_caches = true);
+ // This one is an optimization - it skips all calculations for applying the currently set
+ // configuration, expecting a configuration update later with a forced refresh.
+ void PresetApkAssets(ApkAssetsList apk_assets);
const ApkAssetsPtr& GetApkAssets(ApkAssetsCookie cookie) const;
int GetApkAssetsCount() const {
@@ -156,7 +159,7 @@ class AssetManager2 {
// Sets/resets the configuration for this AssetManager. This will cause all
// caches that are related to the configuration change to be invalidated.
- void SetConfigurations(std::vector<ResTable_config> configurations);
+ void SetConfigurations(std::vector<ResTable_config> configurations, bool force_refresh = false);
inline const std::vector<ResTable_config>& GetConfigurations() const {
return configurations_;
diff --git a/libs/hostgraphics/OWNERS b/libs/hostgraphics/OWNERS
new file mode 100644
index 000000000000..41977f693a04
--- /dev/null
+++ b/libs/hostgraphics/OWNERS
@@ -0,0 +1,4 @@
+include /libs/hwui/OWNERS
+
+diegoperez@google.com
+jgaillard@google.com
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 4e330da417be..40239b8c2719 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -155,6 +155,7 @@ cc_defaults {
host: {
static_libs: [
"libandroidfw",
+ "libhostgraphics",
"libutils",
],
},
@@ -502,6 +503,17 @@ cc_library_headers {
],
header_libs: ["android_graphics_jni_headers"],
export_header_lib_headers: ["android_graphics_jni_headers"],
+ target: {
+ android: {
+ export_include_dirs: ["platform/android"],
+ },
+ host: {
+ export_include_dirs: ["platform/host"],
+ },
+ windows: {
+ enabled: true,
+ },
+ },
}
cc_defaults {
@@ -539,6 +551,7 @@ cc_defaults {
"utils/Blur.cpp",
"utils/Color.cpp",
"utils/LinearAllocator.cpp",
+ "utils/StringUtils.cpp",
"utils/TypefaceUtils.cpp",
"utils/VectorDrawableUtils.cpp",
"AnimationContext.cpp",
@@ -553,6 +566,7 @@ cc_defaults {
"Mesh.cpp",
"MemoryPolicy.cpp",
"PathParser.cpp",
+ "ProfileData.cpp",
"Properties.cpp",
"PropertyValuesAnimatorSet.cpp",
"PropertyValuesHolder.cpp",
@@ -570,12 +584,13 @@ cc_defaults {
export_proto_headers: true,
},
+ header_libs: ["libandroid_headers_private"],
+
target: {
android: {
- header_libs: [
- "libandroid_headers_private",
- "libtonemap_headers",
- ],
+ header_libs: ["libtonemap_headers"],
+
+ local_include_dirs: ["platform/android"],
srcs: [
"hwui/AnimatedImageThread.cpp",
@@ -606,7 +621,6 @@ cc_defaults {
"thread/CommonPool.cpp",
"utils/GLUtils.cpp",
"utils/NdkUtils.cpp",
- "utils/StringUtils.cpp",
"AutoBackendTextureRelease.cpp",
"DeferredLayerUpdater.cpp",
"DeviceInfo.cpp",
@@ -618,7 +632,6 @@ cc_defaults {
"FrameMetricsReporter.cpp",
"Layer.cpp",
"LayerUpdateQueue.cpp",
- "ProfileData.cpp",
"ProfileDataContainer.cpp",
"Readback.cpp",
"TreeInfo.cpp",
@@ -629,6 +642,21 @@ cc_defaults {
// Allow implicit fallthroughs in HardwareBitmapUploader.cpp until they are fixed.
cflags: ["-Wno-implicit-fallthrough"],
},
+ host: {
+ header_libs: ["libnativebase_headers"],
+
+ local_include_dirs: ["platform/host"],
+
+ srcs: [
+ "platform/host/renderthread/CacheManager.cpp",
+ "platform/host/renderthread/RenderThread.cpp",
+ "platform/host/ProfileDataContainer.cpp",
+ "platform/host/Readback.cpp",
+ "platform/host/WebViewFunctorManager.cpp",
+ ],
+
+ cflags: ["-Wno-unused-private-field"],
+ },
},
}
@@ -664,6 +692,7 @@ cc_defaults {
header_libs: ["libandroid_headers_private"],
target: {
android: {
+ local_include_dirs: ["platform/android"],
shared_libs: [
"libgui",
"libui",
diff --git a/libs/hwui/CanvasTransform.cpp b/libs/hwui/CanvasTransform.cpp
index b667daf9c850..30e7a628f1f6 100644
--- a/libs/hwui/CanvasTransform.cpp
+++ b/libs/hwui/CanvasTransform.cpp
@@ -137,9 +137,10 @@ static BitmapPalette filterPalette(const SkPaint* paint, BitmapPalette palette)
return palette;
}
- SkColor color = palette == BitmapPalette::Light ? SK_ColorWHITE : SK_ColorBLACK;
- color = paint->getColorFilter()->filterColor(color);
- return paletteForColorHSV(color);
+ SkColor4f color = palette == BitmapPalette::Light ? SkColors::kWhite : SkColors::kBlack;
+ sk_sp<SkColorSpace> srgb = SkColorSpace::MakeSRGB();
+ color = paint->getColorFilter()->filterColor4f(color, srgb.get(), srgb.get());
+ return paletteForColorHSV(color.toSkColor());
}
bool transformPaint(ColorTransform transform, SkPaint* paint) {
diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp
index fd276419f5e5..28d85bd860df 100644
--- a/libs/hwui/DamageAccumulator.cpp
+++ b/libs/hwui/DamageAccumulator.cpp
@@ -218,7 +218,7 @@ void DamageAccumulator::applyRenderNodeTransform(DirtyStack* frame) {
}
// Perform clipping
- if (props.getClipDamageToBounds() && !frame->pendingDirty.isEmpty()) {
+ if (props.getClipDamageToBounds()) {
if (!frame->pendingDirty.intersect(SkRect::MakeIWH(props.getWidth(), props.getHeight()))) {
frame->pendingDirty.setEmpty();
}
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index 71f7926930fc..27ea15075682 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -378,10 +378,17 @@ static FormatInfo determineFormat(const SkBitmap& skBitmap, bool usingGL) {
break;
case kAlpha_8_SkColorType:
formatInfo.isSupported = HardwareBitmapUploader::hasAlpha8Support();
- formatInfo.bufferFormat = AHARDWAREBUFFER_FORMAT_R8_UNORM;
- formatInfo.format = GL_RED;
- formatInfo.type = GL_UNSIGNED_BYTE;
- formatInfo.vkFormat = VK_FORMAT_R8_UNORM;
+ if (formatInfo.isSupported) {
+ formatInfo.bufferFormat = AHARDWAREBUFFER_FORMAT_R8_UNORM;
+ formatInfo.format = GL_RED;
+ formatInfo.type = GL_UNSIGNED_BYTE;
+ formatInfo.vkFormat = VK_FORMAT_R8_UNORM;
+ } else {
+ formatInfo.type = GL_UNSIGNED_BYTE;
+ formatInfo.bufferFormat = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
+ formatInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
+ formatInfo.format = GL_RGBA;
+ }
break;
default:
ALOGW("unable to create hardware bitmap of colortype: %d", skBitmap.info().colorType());
diff --git a/libs/hwui/ProfileData.cpp b/libs/hwui/ProfileData.cpp
index 3d0ca0a10851..7be9541f6b99 100644
--- a/libs/hwui/ProfileData.cpp
+++ b/libs/hwui/ProfileData.cpp
@@ -110,6 +110,7 @@ void ProfileData::mergeWith(const ProfileData& other) {
}
void ProfileData::dump(int fd) const {
+#ifdef __ANDROID__
dprintf(fd, "\nStats since: %" PRIu64 "ns", mStatStartTime);
dprintf(fd, "\nTotal frames rendered: %u", mTotalFrameCount);
dprintf(fd, "\nJanky frames: %u (%.2f%%)", mJankFrameCount,
@@ -138,6 +139,7 @@ void ProfileData::dump(int fd) const {
dprintf(fd, " %ums=%u", entry.renderTimeMs, entry.frameCount);
});
dprintf(fd, "\n");
+#endif
}
uint32_t ProfileData::findPercentile(int percentile) const {
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 14b8d8d0aa12..0b739c361d64 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -70,6 +70,8 @@ public:
: mType(Type::RRect), mOp(op), mMatrix(m), mRRect(rrect) {}
Clip(const SkPath& path, SkClipOp op, const SkMatrix& m)
: mType(Type::Path), mOp(op), mMatrix(m), mPath(std::in_place, path) {}
+ Clip(const sk_sp<SkShader> shader, SkClipOp op, const SkMatrix& m)
+ : mType(Type::Shader), mOp(op), mMatrix(m), mShader(shader) {}
void apply(SkCanvas* canvas) const {
canvas->setMatrix(mMatrix);
@@ -86,6 +88,8 @@ public:
// Ensure path clips are anti-aliased
canvas->clipPath(mPath.value(), mOp, true);
break;
+ case Type::Shader:
+ canvas->clipShader(mShader, mOp);
}
}
@@ -94,6 +98,7 @@ private:
Rect,
RRect,
Path,
+ Shader,
};
Type mType;
@@ -103,6 +108,7 @@ private:
// These are logically a union (tracked separately due to non-POD path).
std::optional<SkPath> mPath;
SkRRect mRRect;
+ sk_sp<SkShader> mShader;
};
Canvas* Canvas::create_canvas(const SkBitmap& bitmap) {
@@ -413,6 +419,11 @@ bool SkiaCanvas::clipPath(const SkPath* path, SkClipOp op) {
return !mCanvas->isClipEmpty();
}
+void SkiaCanvas::clipShader(sk_sp<SkShader> shader, SkClipOp op) {
+ this->recordClip(shader, op);
+ mCanvas->clipShader(shader, op);
+}
+
bool SkiaCanvas::replaceClipRect_deprecated(float left, float top, float right, float bottom) {
SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 5e3553bbbbb4..4a012bc601c9 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -97,6 +97,7 @@ public:
virtual bool quickRejectPath(const SkPath& path) const override;
virtual bool clipRect(float left, float top, float right, float bottom, SkClipOp op) override;
virtual bool clipPath(const SkPath* path, SkClipOp op) override;
+ virtual void clipShader(sk_sp<SkShader> shader, SkClipOp op) override;
virtual bool replaceClipRect_deprecated(float left, float top, float right,
float bottom) override;
virtual bool replaceClipPath_deprecated(const SkPath* path) override;
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 8344a86923ee..185436160349 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -147,11 +147,7 @@ sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(size_t size, const SkImageInfo& info,
}
sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(const SkBitmap& bitmap) {
-#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
- if (bitmap.colorType() == kAlpha_8_SkColorType &&
- !uirenderer::HardwareBitmapUploader::hasAlpha8Support()) {
- return nullptr;
- }
+#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
return uirenderer::HardwareBitmapUploader::allocateHardwareBitmap(bitmap);
#else
return Bitmap::allocateHeapBitmap(bitmap.info());
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 20e3ad2c8202..14b4f584f0f3 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -188,6 +188,7 @@ public:
virtual bool clipRect(float left, float top, float right, float bottom, SkClipOp op) = 0;
virtual bool clipPath(const SkPath* path, SkClipOp op) = 0;
+ virtual void clipShader(sk_sp<SkShader> shader, SkClipOp op) = 0;
// Resets clip to wide open, used to emulate the now-removed SkClipOp::kReplace on
// apps with compatibility < P. Canvases for version P and later are restricted to
// intersect and difference at the Java level, matching SkClipOp's definition.
diff --git a/libs/hwui/hwui/DrawTextFunctor.h b/libs/hwui/hwui/DrawTextFunctor.h
index 02bf0d8d5e95..1fcb6920db14 100644
--- a/libs/hwui/hwui/DrawTextFunctor.h
+++ b/libs/hwui/hwui/DrawTextFunctor.h
@@ -92,6 +92,7 @@ public:
// high contrast draw path
int color = paint.getColor();
bool darken;
+ // This equation should match the one in core/java/android/text/Layout.java
if (flags::high_contrast_text_luminance()) {
uirenderer::Lab lab = uirenderer::sRGBToLab(color);
darken = lab.L <= 50;
diff --git a/libs/hwui/jni/android_graphics_Canvas.cpp b/libs/hwui/jni/android_graphics_Canvas.cpp
index e5bdeeea75be..9b63a46822ac 100644
--- a/libs/hwui/jni/android_graphics_Canvas.cpp
+++ b/libs/hwui/jni/android_graphics_Canvas.cpp
@@ -88,6 +88,10 @@ static void setBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHand
get_canvas(canvasHandle)->setBitmap(bitmap);
}
+static jboolean isHighContrastText(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) {
+ return get_canvas(canvasHandle)->isHighContrastText() ? JNI_TRUE : JNI_FALSE;
+}
+
static jboolean isOpaque(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) {
return get_canvas(canvasHandle)->isOpaque() ? JNI_TRUE : JNI_FALSE;
}
@@ -261,6 +265,23 @@ static jboolean clipPath(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong pat
return nonEmptyClip ? JNI_TRUE : JNI_FALSE;
}
+static void clipShader(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong shaderHandle,
+ jint opHandle) {
+ SkRegion::Op rgnOp = static_cast<SkRegion::Op>(opHandle);
+ sk_sp<SkShader> shader = sk_ref_sp(reinterpret_cast<SkShader*>(shaderHandle));
+ switch (rgnOp) {
+ case SkRegion::Op::kIntersect_Op:
+ case SkRegion::Op::kDifference_Op:
+ get_canvas(canvasHandle)->clipShader(shader, static_cast<SkClipOp>(rgnOp));
+ break;
+ default:
+ ALOGW("Ignoring unsupported clip operation %d", opHandle);
+ SkRect clipBounds; // ignored
+ get_canvas(canvasHandle)->getClipBounds(&clipBounds);
+ break;
+ }
+}
+
static void drawColor(JNIEnv* env, jobject, jlong canvasHandle, jint color, jint modeHandle) {
SkBlendMode mode = static_cast<SkBlendMode>(modeHandle);
get_canvas(canvasHandle)->drawColor(color, mode);
@@ -775,6 +796,7 @@ static const JNINativeMethod gMethods[] = {
// ------------ @CriticalNative ----------------
{"nIsOpaque", "(J)Z", (void*)CanvasJNI::isOpaque},
+ {"nIsHighContrastText", "(J)Z", (void*)CanvasJNI::isHighContrastText},
{"nGetWidth", "(J)I", (void*)CanvasJNI::getWidth},
{"nGetHeight", "(J)I", (void*)CanvasJNI::getHeight},
{"nSave", "(JI)I", (void*)CanvasJNI::save},
@@ -797,6 +819,7 @@ static const JNINativeMethod gMethods[] = {
{"nQuickReject", "(JFFFF)Z", (void*)CanvasJNI::quickRejectRect},
{"nClipRect", "(JFFFFI)Z", (void*)CanvasJNI::clipRect},
{"nClipPath", "(JJI)Z", (void*)CanvasJNI::clipPath},
+ {"nClipShader", "(JJI)V", (void*)CanvasJNI::clipShader},
{"nSetDrawFilter", "(JJ)V", (void*)CanvasJNI::setPaintFilter},
};
diff --git a/libs/hwui/thread/ThreadBase.h b/libs/hwui/platform/android/thread/ThreadBase.h
index 0289d3fd2ef7..2f3581f8b355 100644
--- a/libs/hwui/thread/ThreadBase.h
+++ b/libs/hwui/platform/android/thread/ThreadBase.h
@@ -17,14 +17,14 @@
#ifndef HWUI_THREADBASE_H
#define HWUI_THREADBASE_H
-#include "WorkQueue.h"
-#include "utils/Macros.h"
-
#include <utils/Looper.h>
#include <utils/Thread.h>
#include <algorithm>
+#include "thread/WorkQueue.h"
+#include "utils/Macros.h"
+
namespace android::uirenderer {
class ThreadBase : public Thread {
diff --git a/libs/hwui/platform/host/ProfileDataContainer.cpp b/libs/hwui/platform/host/ProfileDataContainer.cpp
new file mode 100644
index 000000000000..9ed1b02a8082
--- /dev/null
+++ b/libs/hwui/platform/host/ProfileDataContainer.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ProfileDataContainer.h"
+
+#include <log/log.h>
+
+namespace android {
+namespace uirenderer {
+
+void ProfileDataContainer::freeData() REQUIRES(mJankDataMutex) {
+ delete mData;
+ mIsMapped = false;
+ mData = nullptr;
+}
+
+void ProfileDataContainer::rotateStorage() {
+ std::lock_guard lock(mJankDataMutex);
+ mData->reset();
+}
+
+void ProfileDataContainer::switchStorageToAshmem(int ashmemfd) {
+ ALOGE("Ashmem is not supported for non-Android configurations");
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/platform/host/Readback.cpp b/libs/hwui/platform/host/Readback.cpp
new file mode 100644
index 000000000000..b024ec02efc3
--- /dev/null
+++ b/libs/hwui/platform/host/Readback.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Readback.h"
+
+using namespace android::uirenderer::renderthread;
+
+namespace android {
+namespace uirenderer {
+
+void Readback::copySurfaceInto(ANativeWindow* window, const std::shared_ptr<CopyRequest>& request) {
+}
+
+CopyResult Readback::copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap) {
+ return CopyResult::UnknownError;
+}
+
+CopyResult Readback::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBitmap* bitmap) {
+ return CopyResult::UnknownError;
+}
+
+CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, SkBitmap* bitmap) {
+ return CopyResult::UnknownError;
+}
+
+CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, const Rect& srcRect,
+ SkBitmap* bitmap) {
+ return CopyResult::UnknownError;
+}
+
+bool Readback::copyLayerInto(Layer* layer, const SkRect* srcRect, const SkRect* dstRect,
+ SkBitmap* bitmap) {
+ return false;
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/platform/host/WebViewFunctorManager.cpp b/libs/hwui/platform/host/WebViewFunctorManager.cpp
new file mode 100644
index 000000000000..1d16655bf73c
--- /dev/null
+++ b/libs/hwui/platform/host/WebViewFunctorManager.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "WebViewFunctorManager.h"
+
+namespace android::uirenderer {
+
+WebViewFunctor::WebViewFunctor(void* data, const WebViewFunctorCallbacks& callbacks,
+ RenderMode functorMode)
+ : mData(data) {}
+
+WebViewFunctor::~WebViewFunctor() {}
+
+void WebViewFunctor::sync(const WebViewSyncData& syncData) const {}
+
+void WebViewFunctor::onRemovedFromTree() {}
+
+bool WebViewFunctor::prepareRootSurfaceControl() {
+ return true;
+}
+
+void WebViewFunctor::drawGl(const DrawGlInfo& drawInfo) {}
+
+void WebViewFunctor::initVk(const VkFunctorInitParams& params) {}
+
+void WebViewFunctor::drawVk(const VkFunctorDrawParams& params) {}
+
+void WebViewFunctor::postDrawVk() {}
+
+void WebViewFunctor::destroyContext() {}
+
+void WebViewFunctor::removeOverlays() {}
+
+ASurfaceControl* WebViewFunctor::getSurfaceControl() {
+ return mSurfaceControl;
+}
+
+void WebViewFunctor::mergeTransaction(ASurfaceTransaction* transaction) {}
+
+void WebViewFunctor::reparentSurfaceControl(ASurfaceControl* parent) {}
+
+WebViewFunctorManager& WebViewFunctorManager::instance() {
+ static WebViewFunctorManager sInstance;
+ return sInstance;
+}
+
+int WebViewFunctorManager::createFunctor(void* data, const WebViewFunctorCallbacks& callbacks,
+ RenderMode functorMode) {
+ return 0;
+}
+
+void WebViewFunctorManager::releaseFunctor(int functor) {}
+
+void WebViewFunctorManager::onContextDestroyed() {}
+
+void WebViewFunctorManager::destroyFunctor(int functor) {}
+
+sp<WebViewFunctor::Handle> WebViewFunctorManager::handleFor(int functor) {
+ return nullptr;
+}
+
+} // namespace android::uirenderer
diff --git a/libs/hwui/platform/host/renderthread/CacheManager.cpp b/libs/hwui/platform/host/renderthread/CacheManager.cpp
new file mode 100644
index 000000000000..b03f400cd7d6
--- /dev/null
+++ b/libs/hwui/platform/host/renderthread/CacheManager.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "renderthread/CacheManager.h"
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+CacheManager::CacheManager(RenderThread& thread)
+ : mRenderThread(thread), mMemoryPolicy(loadMemoryPolicy()) {}
+
+void CacheManager::setupCacheLimits() {}
+
+void CacheManager::destroy() {}
+
+void CacheManager::trimMemory(TrimLevel mode) {}
+
+void CacheManager::trimCaches(CacheTrimLevel mode) {}
+
+void CacheManager::trimStaleResources() {}
+
+void CacheManager::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) {}
+
+void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) {}
+
+void CacheManager::onFrameCompleted() {}
+
+void CacheManager::onThreadIdle() {}
+
+void CacheManager::scheduleDestroyContext() {}
+
+void CacheManager::cancelDestroyContext() {}
+
+bool CacheManager::areAllContextsStopped() {
+ return false;
+}
+
+void CacheManager::checkUiHidden() {}
+
+void CacheManager::registerCanvasContext(CanvasContext* context) {}
+
+void CacheManager::unregisterCanvasContext(CanvasContext* context) {}
+
+void CacheManager::onContextStopped(CanvasContext* context) {}
+
+void CacheManager::notifyNextFrameSize(int width, int height) {}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/platform/host/renderthread/RenderThread.cpp b/libs/hwui/platform/host/renderthread/RenderThread.cpp
new file mode 100644
index 000000000000..6f08b5979772
--- /dev/null
+++ b/libs/hwui/platform/host/renderthread/RenderThread.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "renderthread/RenderThread.h"
+
+#include "Readback.h"
+#include "renderthread/VulkanManager.h"
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+static bool gHasRenderThreadInstance = false;
+static JVMAttachHook gOnStartHook = nullptr;
+
+ASurfaceControlFunctions::ASurfaceControlFunctions() {}
+
+bool RenderThread::hasInstance() {
+ return gHasRenderThreadInstance;
+}
+
+void RenderThread::setOnStartHook(JVMAttachHook onStartHook) {
+ LOG_ALWAYS_FATAL_IF(hasInstance(), "can't set an onStartHook after we've started...");
+ gOnStartHook = onStartHook;
+}
+
+JVMAttachHook RenderThread::getOnStartHook() {
+ return gOnStartHook;
+}
+
+RenderThread& RenderThread::getInstance() {
+ [[clang::no_destroy]] static sp<RenderThread> sInstance = []() {
+ sp<RenderThread> thread = sp<RenderThread>::make();
+ thread->start("RenderThread");
+ return thread;
+ }();
+ gHasRenderThreadInstance = true;
+ return *sInstance;
+}
+
+RenderThread::RenderThread()
+ : ThreadBase()
+ , mVsyncSource(nullptr)
+ , mVsyncRequested(false)
+ , mFrameCallbackTaskPending(false)
+ , mRenderState(nullptr)
+ , mEglManager(nullptr)
+ , mFunctorManager(WebViewFunctorManager::instance())
+ , mGlobalProfileData(mJankDataMutex) {
+ Properties::load();
+}
+
+RenderThread::~RenderThread() {}
+
+void RenderThread::initThreadLocals() {
+ mCacheManager = new CacheManager(*this);
+}
+
+void RenderThread::requireGlContext() {}
+
+void RenderThread::requireVkContext() {}
+
+void RenderThread::initGrContextOptions(GrContextOptions& options) {}
+
+void RenderThread::destroyRenderingContext() {}
+
+VulkanManager& RenderThread::vulkanManager() {
+ return *mVkManager;
+}
+
+void RenderThread::dumpGraphicsMemory(int fd, bool includeProfileData) {}
+
+void RenderThread::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) {}
+
+Readback& RenderThread::readback() {
+ if (!mReadback) {
+ mReadback = new Readback(*this);
+ }
+
+ return *mReadback;
+}
+
+void RenderThread::setGrContext(sk_sp<GrDirectContext> context) {}
+
+sk_sp<GrDirectContext> RenderThread::requireGrContext() {
+ return mGrContext;
+}
+
+bool RenderThread::threadLoop() {
+ if (gOnStartHook) {
+ gOnStartHook("RenderThread");
+ }
+ initThreadLocals();
+
+ while (true) {
+ waitForWork();
+ processQueue();
+ mCacheManager->onThreadIdle();
+ }
+
+ return false;
+}
+
+void RenderThread::postFrameCallback(IFrameCallback* callback) {}
+
+bool RenderThread::removeFrameCallback(IFrameCallback* callback) {
+ return false;
+}
+
+void RenderThread::pushBackFrameCallback(IFrameCallback* callback) {}
+
+sk_sp<Bitmap> RenderThread::allocateHardwareBitmap(SkBitmap& skBitmap) {
+ return nullptr;
+}
+
+bool RenderThread::isCurrent() {
+ return true;
+}
+
+void RenderThread::preload() {}
+
+void RenderThread::trimMemory(TrimLevel level) {}
+
+void RenderThread::trimCaches(CacheTrimLevel level) {}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/platform/host/thread/ThreadBase.h b/libs/hwui/platform/host/thread/ThreadBase.h
new file mode 100644
index 000000000000..d709430cc9b6
--- /dev/null
+++ b/libs/hwui/platform/host/thread/ThreadBase.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HWUI_THREADBASE_H
+#define HWUI_THREADBASE_H
+
+#include <utils/Thread.h>
+
+#include <algorithm>
+
+#include "thread/WorkQueue.h"
+#include "utils/Macros.h"
+
+namespace android::uirenderer {
+
+class ThreadBase : public Thread {
+ PREVENT_COPY_AND_ASSIGN(ThreadBase);
+
+public:
+ ThreadBase() : Thread(false), mQueue([this]() { mCondition.notify_all(); }, mLock) {}
+
+ WorkQueue& queue() { return mQueue; }
+
+ void requestExit() { Thread::requestExit(); }
+
+ void start(const char* name = "ThreadBase") { Thread::run(name); }
+
+ void join() { Thread::join(); }
+
+ bool isRunning() const { return Thread::isRunning(); }
+
+protected:
+ void waitForWork() {
+ std::unique_lock lock{mLock};
+ nsecs_t nextWakeup = mQueue.nextWakeup(lock);
+ std::chrono::nanoseconds duration = std::chrono::nanoseconds::max();
+ if (nextWakeup < std::numeric_limits<nsecs_t>::max()) {
+ int timeout = nextWakeup - WorkQueue::clock::now();
+ if (timeout < 0) timeout = 0;
+ duration = std::chrono::nanoseconds(timeout);
+ }
+ mCondition.wait_for(lock, duration);
+ }
+
+ void processQueue() { mQueue.process(); }
+
+ virtual bool threadLoop() override {
+ while (!exitPending()) {
+ waitForWork();
+ processQueue();
+ }
+ return false;
+ }
+
+private:
+ WorkQueue mQueue;
+ std::mutex mLock;
+ std::condition_variable mCondition;
+};
+
+} // namespace android::uirenderer
+
+#endif // HWUI_THREADBASE_H
diff --git a/libs/hwui/private/hwui/WebViewFunctor.h b/libs/hwui/private/hwui/WebViewFunctor.h
index 22ae59e5137b..493c943079ab 100644
--- a/libs/hwui/private/hwui/WebViewFunctor.h
+++ b/libs/hwui/private/hwui/WebViewFunctor.h
@@ -17,15 +17,7 @@
#ifndef FRAMEWORKS_BASE_WEBVIEWFUNCTOR_H
#define FRAMEWORKS_BASE_WEBVIEWFUNCTOR_H
-#ifdef __ANDROID__ // Layoutlib does not support surface control
#include <android/surface_control.h>
-#else
-// To avoid ifdefs around overlay implementation all over the place we typedef these to void *. They
-// won't be used.
-typedef void* ASurfaceControl;
-typedef void* ASurfaceTransaction;
-#endif
-
#include <cutils/compiler.h>
#include <private/hwui/DrawGlInfo.h>
#include <private/hwui/DrawVkInfo.h>
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 9c7f7cc24266..1d0330185b1c 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -1067,6 +1067,7 @@ SkRect CanvasContext::computeDirtyRect(const Frame& frame, SkRect* dirty) {
if (dirty->isEmpty()) {
dirty->setIWH(frame.width(), frame.height());
+ return *dirty;
}
// At this point dirty is the area of the window to update. However,
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 623ee4e6c27e..a024aeb285f9 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -17,11 +17,12 @@
#include "RenderThread.h"
#include <GrContextOptions.h>
-#include <include/gpu/ganesh/gl/GrGLDirectContext.h>
#include <android-base/properties.h>
#include <dlfcn.h>
#include <gl/GrGLInterface.h>
#include <gui/TraceUtils.h>
+#include <include/gpu/ganesh/gl/GrGLDirectContext.h>
+#include <private/android/choreographer.h>
#include <sys/resource.h>
#include <ui/FatVector.h>
#include <utils/Condition.h>
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 79e57de9d66f..045d26f1d329 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -20,10 +20,7 @@
#include <GrDirectContext.h>
#include <SkBitmap.h>
#include <cutils/compiler.h>
-#include <private/android/choreographer.h>
#include <surface_control_private.h>
-#include <thread/ThreadBase.h>
-#include <utils/Looper.h>
#include <utils/Thread.h>
#include <memory>
diff --git a/location/api/current.txt b/location/api/current.txt
index 5ed8c3c8eb4b..85e9f65a0718 100644
--- a/location/api/current.txt
+++ b/location/api/current.txt
@@ -88,12 +88,12 @@ package android.location {
public final class Geocoder {
ctor public Geocoder(@NonNull android.content.Context);
ctor public Geocoder(@NonNull android.content.Context, @NonNull java.util.Locale);
- method @Deprecated @Nullable public java.util.List<android.location.Address> getFromLocation(@FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @IntRange int) throws java.io.IOException;
- method public void getFromLocation(@FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @IntRange int, @NonNull android.location.Geocoder.GeocodeListener);
- method @Deprecated @Nullable public java.util.List<android.location.Address> getFromLocationName(@NonNull String, @IntRange int) throws java.io.IOException;
- method public void getFromLocationName(@NonNull String, @IntRange int, @NonNull android.location.Geocoder.GeocodeListener);
- method @Deprecated @Nullable public java.util.List<android.location.Address> getFromLocationName(@NonNull String, @IntRange int, @FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double) throws java.io.IOException;
- method public void getFromLocationName(@NonNull String, @IntRange int, @FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @NonNull android.location.Geocoder.GeocodeListener);
+ method @Deprecated @Nullable public java.util.List<android.location.Address> getFromLocation(@FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @IntRange(from=1) int) throws java.io.IOException;
+ method public void getFromLocation(@FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @IntRange(from=1) int, @NonNull android.location.Geocoder.GeocodeListener);
+ method @Deprecated @Nullable public java.util.List<android.location.Address> getFromLocationName(@NonNull String, @IntRange(from=1) int) throws java.io.IOException;
+ method public void getFromLocationName(@NonNull String, @IntRange(from=1) int, @NonNull android.location.Geocoder.GeocodeListener);
+ method @Deprecated @Nullable public java.util.List<android.location.Address> getFromLocationName(@NonNull String, @IntRange(from=1) int, @FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double) throws java.io.IOException;
+ method public void getFromLocationName(@NonNull String, @IntRange(from=1) int, @FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @NonNull android.location.Geocoder.GeocodeListener);
method public static boolean isPresent();
}
diff --git a/location/api/system-current.txt b/location/api/system-current.txt
index b1cf96d41497..2e7a541ecb60 100644
--- a/location/api/system-current.txt
+++ b/location/api/system-current.txt
@@ -591,6 +591,36 @@ package android.location {
package android.location.provider {
+ @FlaggedApi(Flags.FLAG_NEW_GEOCODER) public final class ForwardGeocodeRequest implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public String getCallingAttributionTag();
+ method @NonNull public String getCallingPackage();
+ method public int getCallingUid();
+ method @NonNull public java.util.Locale getLocale();
+ method @NonNull public String getLocationName();
+ method @FloatRange(from=-90.0, to=90.0) public double getLowerLeftLatitude();
+ method @FloatRange(from=-180.0, to=180.0) public double getLowerLeftLongitude();
+ method @IntRange(from=1) public int getMaxResults();
+ method @FloatRange(from=-90.0, to=90.0) public double getUpperRightLatitude();
+ method @FloatRange(from=-180.0, to=180.0) public double getUpperRightLongitude();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.provider.ForwardGeocodeRequest> CREATOR;
+ }
+
+ public static final class ForwardGeocodeRequest.Builder {
+ ctor public ForwardGeocodeRequest.Builder(@NonNull String, @FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @IntRange(from=1) int, @NonNull java.util.Locale, int, @NonNull String);
+ method @NonNull public android.location.provider.ForwardGeocodeRequest build();
+ method @NonNull public android.location.provider.ForwardGeocodeRequest.Builder setCallingAttributionTag(@NonNull String);
+ }
+
+ @FlaggedApi(Flags.FLAG_NEW_GEOCODER) public abstract class GeocodeProviderBase {
+ ctor public GeocodeProviderBase(@NonNull android.content.Context, @NonNull String);
+ method @NonNull public final android.os.IBinder getBinder();
+ method public abstract void onForwardGeocode(@NonNull android.location.provider.ForwardGeocodeRequest, @NonNull android.os.OutcomeReceiver<java.util.List<android.location.Address>,java.lang.Exception>);
+ method public abstract void onReverseGeocode(@NonNull android.location.provider.ReverseGeocodeRequest, @NonNull android.os.OutcomeReceiver<java.util.List<android.location.Address>,java.lang.Exception>);
+ field public static final String ACTION_GEOCODE_PROVIDER = "com.android.location.service.GeocodeProvider";
+ }
+
public abstract class LocationProviderBase {
ctor public LocationProviderBase(@NonNull android.content.Context, @NonNull String, @NonNull android.location.provider.ProviderProperties);
method @Nullable public final android.os.IBinder getBinder();
@@ -642,5 +672,24 @@ package android.location.provider {
method public void onProviderRequestChanged(@NonNull String, @NonNull android.location.provider.ProviderRequest);
}
+ @FlaggedApi(Flags.FLAG_NEW_GEOCODER) public final class ReverseGeocodeRequest implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public String getCallingAttributionTag();
+ method @NonNull public String getCallingPackage();
+ method public int getCallingUid();
+ method @FloatRange(from=-90.0, to=90.0) public double getLatitude();
+ method @NonNull public java.util.Locale getLocale();
+ method @FloatRange(from=-180.0, to=180.0) public double getLongitude();
+ method @IntRange(from=1) public int getMaxResults();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.location.provider.ReverseGeocodeRequest> CREATOR;
+ }
+
+ public static final class ReverseGeocodeRequest.Builder {
+ ctor public ReverseGeocodeRequest.Builder(@FloatRange(from=-90.0, to=90.0) double, @FloatRange(from=-180.0, to=180.0) double, @IntRange(from=0) int, @NonNull java.util.Locale, int, @NonNull String);
+ method @NonNull public android.location.provider.ReverseGeocodeRequest build();
+ method @NonNull public android.location.provider.ReverseGeocodeRequest.Builder setCallingAttributionTag(@NonNull String);
+ }
+
}
diff --git a/location/java/android/location/Geocoder.java b/location/java/android/location/Geocoder.java
index a15834407315..cdde7c66e268 100644
--- a/location/java/android/location/Geocoder.java
+++ b/location/java/android/location/Geocoder.java
@@ -21,11 +21,13 @@ import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.location.provider.ForwardGeocodeRequest;
+import android.location.provider.IGeocodeCallback;
+import android.location.provider.ReverseGeocodeRequest;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
-import com.android.internal.util.Preconditions;
-
import java.io.IOException;
import java.util.Collections;
import java.util.List;
@@ -33,6 +35,7 @@ import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
/**
* A class for handling geocoding and reverse geocoding. Geocoding is the process of transforming a
@@ -42,9 +45,12 @@ import java.util.concurrent.TimeUnit;
* example one might contain the full street address of the closest building, while another might
* contain only a city name and postal code.
*
- * The Geocoder class requires a backend service that is not included in the core android framework.
- * The Geocoder query methods will return an empty list if there no backend service in the platform.
- * Use the isPresent() method to determine whether a Geocoder implementation exists.
+ * <p>Use the isPresent() method to determine whether a Geocoder implementation exists on the
+ * current device. If no implementation is present, any attempt to geocode will result in an error.
+ *
+ * <p>Geocoder implementations are only required to make a best effort to return results in the
+ * chosen locale. Note that geocoder implementations may return results in other locales if they
+ * have no information available for the chosen locale.
*
* <p class="note"><strong>Warning:</strong> Geocoding services may provide no guarantees on
* availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful or
@@ -52,45 +58,53 @@ import java.util.concurrent.TimeUnit;
*/
public final class Geocoder {
- /** A listener for asynchronous geocoding results. */
+ /**
+ * A listener for asynchronous geocoding results. Only one of the methods will ever be invoked
+ * per geocoding attempt. There are no guarantees on how long it will take for a method to be
+ * invoked, nor any guarantees on the format or availability of error information.
+ */
public interface GeocodeListener {
/** Invoked when geocoding completes successfully. May return an empty list. */
void onGeocode(@NonNull List<Address> addresses);
- /** Invoked when geocoding fails, with a brief error message. */
+
+ /** Invoked when geocoding fails, with an optional error message. */
default void onError(@Nullable String errorMessage) {}
}
- private static final long TIMEOUT_MS = 60000;
+ private static final long TIMEOUT_MS = 15000;
- private final GeocoderParams mParams;
+ private final Context mContext;
+ private final Locale mLocale;
private final ILocationManager mService;
/**
- * Returns true if there is a geocoder implementation present that may return results. If true,
- * there is still no guarantee that any individual geocoding attempt will succeed.
+ * Returns true if there is a geocoder implementation present on the device that may return
+ * results. If true, there is still no guarantee that any individual geocoding attempt will
+ * succeed.
*/
public static boolean isPresent() {
ILocationManager lm = Objects.requireNonNull(ILocationManager.Stub.asInterface(
ServiceManager.getService(Context.LOCATION_SERVICE)));
try {
- return lm.geocoderIsPresent();
+ return lm.isGeocodeAvailable();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
- /**
- * Constructs a Geocoder localized for the default locale.
- */
+ /** Constructs a Geocoder localized for {@link Locale#getDefault()}. */
public Geocoder(@NonNull Context context) {
this(context, Locale.getDefault());
}
/**
- * Constructs a Geocoder localized for the given locale.
+ * Constructs a Geocoder localized for the given locale. Note that geocoder implementations will
+ * only make a best effort to return results in the given locale, and there is no guarantee that
+ * returned results will be in the specific locale.
*/
public Geocoder(@NonNull Context context, @NonNull Locale locale) {
- mParams = new GeocoderParams(context, locale);
+ mContext = Objects.requireNonNull(context);
+ mLocale = Objects.requireNonNull(locale);
mService = ILocationManager.Stub.asInterface(
ServiceManager.getService(Context.LOCATION_SERVICE));
}
@@ -103,31 +117,28 @@ public final class Geocoder {
* <p class="warning"><strong>Warning:</strong> Geocoding services may provide no guarantees on
* availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful
* or correct. Do <b>NOT</b> use this API for any safety-critical or regulatory compliance
- * purposes.</p>
+ * purposes.
*
* <p class="warning"><strong>Warning:</strong> This API may hit the network, and may block for
- * excessive amounts of time, up to 60 seconds or more. It's strongly encouraged to use the
- * asynchronous version of this API. If that is not possible, this should be run on a background
- * thread to avoid blocking other operations.</p>
+ * excessive amounts of time. It's strongly encouraged to use the asynchronous version of this
+ * API. If that is not possible, this should be run on a background thread to avoid blocking
+ * other operations.
*
* @param latitude the latitude a point for the search
* @param longitude the longitude a point for the search
* @param maxResults max number of addresses to return. Smaller numbers (1 to 5) are recommended
- *
- * @return a list of Address objects. Returns null or empty list if no matches were
- * found or there is no backend service available.
- *
+ * @return a list of Address objects. Returns null or empty list if no matches were found or
+ * there is no backend service available.
* @throws IllegalArgumentException if latitude or longitude is invalid
* @throws IOException if there is a failure
- *
* @deprecated Use {@link #getFromLocation(double, double, int, GeocodeListener)} instead to
- * avoid blocking a thread waiting for results.
+ * avoid blocking a thread waiting for results.
*/
@Deprecated
public @Nullable List<Address> getFromLocation(
@FloatRange(from = -90D, to = 90D) double latitude,
- @FloatRange(from = -180D, to = 180D)double longitude,
- @IntRange int maxResults)
+ @FloatRange(from = -180D, to = 180D) double longitude,
+ @IntRange(from = 1) int maxResults)
throws IOException {
SynchronousGeocoder listener = new SynchronousGeocoder();
getFromLocation(latitude, longitude, maxResults, listener);
@@ -142,26 +153,32 @@ public final class Geocoder {
* <p class="warning"><strong>Warning:</strong> Geocoding services may provide no guarantees on
* availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful
* or correct. Do <b>NOT</b> use this API for any safety-critical or regulatory compliance
- * purposes.</p>
+ * purposes.
*
* @param latitude the latitude a point for the search
* @param longitude the longitude a point for the search
* @param maxResults max number of addresses to return. Smaller numbers (1 to 5) are recommended
* @param listener a listener for receiving results
- *
* @throws IllegalArgumentException if latitude or longitude is invalid
*/
public void getFromLocation(
@FloatRange(from = -90D, to = 90D) double latitude,
@FloatRange(from = -180D, to = 180D) double longitude,
- @IntRange int maxResults,
+ @IntRange(from = 1) int maxResults,
@NonNull GeocodeListener listener) {
- Preconditions.checkArgumentInRange(latitude, -90.0, 90.0, "latitude");
- Preconditions.checkArgumentInRange(longitude, -180.0, 180.0, "longitude");
-
+ ReverseGeocodeRequest.Builder b =
+ new ReverseGeocodeRequest.Builder(
+ latitude,
+ longitude,
+ maxResults,
+ mLocale,
+ Process.myUid(),
+ mContext.getPackageName());
+ if (mContext.getAttributionTag() != null) {
+ b.setCallingAttributionTag(mContext.getAttributionTag());
+ }
try {
- mService.getFromLocation(latitude, longitude, maxResults, mParams,
- new GeocoderImpl(listener));
+ mService.reverseGeocode(b.build(), new GeocodeCallbackImpl(listener));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -176,29 +193,25 @@ public final class Geocoder {
* <p class="note"><strong>Warning:</strong> Geocoding services may provide no guarantees on
* availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful
* or correct. Do <b>NOT</b> use this API for any safety-critical or regulatory compliance
- * purposes.</p>
+ * purposes.
*
* <p class="warning"><strong>Warning:</strong> This API may hit the network, and may block for
- * excessive amounts of time, up to 60 seconds or more. It's strongly encouraged to use the
- * asynchronous version of this API. If that is not possible, this should be run on a background
- * thread to avoid blocking other operations.</p>
+ * excessive amounts of time. It's strongly encouraged to use the asynchronous version of this
+ * API. If that is not possible, this should be run on a background thread to avoid blocking
+ * other operations.
*
* @param locationName a user-supplied description of a location
* @param maxResults max number of results to return. Smaller numbers (1 to 5) are recommended
- *
- * @return a list of Address objects. Returns null or empty list if no matches were
- * found or there is no backend service available.
- *
+ * @return a list of Address objects. Returns null or empty list if no matches were found or
+ * there is no backend service available.
* @throws IllegalArgumentException if locationName is null
* @throws IOException if there is a failure
- *
* @deprecated Use {@link #getFromLocationName(String, int, GeocodeListener)} instead to avoid
- * blocking a thread waiting for results.
+ * blocking a thread waiting for results.
*/
@Deprecated
public @Nullable List<Address> getFromLocationName(
- @NonNull String locationName,
- @IntRange int maxResults) throws IOException {
+ @NonNull String locationName, @IntRange(from = 1) int maxResults) throws IOException {
return getFromLocationName(locationName, maxResults, 0, 0, 0, 0);
}
@@ -211,17 +224,16 @@ public final class Geocoder {
* <p class="note"><strong>Warning:</strong> Geocoding services may provide no guarantees on
* availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful
* or correct. Do <b>NOT</b> use this API for any safety-critical or regulatory compliance
- * purposes.</p>
+ * purposes.
*
* @param locationName a user-supplied description of a location
* @param maxResults max number of results to return. Smaller numbers (1 to 5) are recommended
* @param listener a listener for receiving results
- *
* @throws IllegalArgumentException if locationName is null
*/
public void getFromLocationName(
@NonNull String locationName,
- @IntRange int maxResults,
+ @IntRange(from = 1) int maxResults,
@NonNull GeocodeListener listener) {
getFromLocationName(locationName, maxResults, 0, 0, 0, 0, listener);
}
@@ -232,45 +244,42 @@ public final class Geocoder {
* View, CA", an airport code such as "SFO", and so forth. The returned addresses should be
* localized for the locale provided to this class's constructor.
*
- * <p> You may specify a bounding box for the search results by including the latitude and
+ * <p>You may specify a bounding box for the search results by including the latitude and
* longitude of the lower left point and upper right point of the box.
*
* <p class="note"><strong>Warning:</strong> Geocoding services may provide no guarantees on
* availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful
* or correct. Do <b>NOT</b> use this API for any safety-critical or regulatory compliance
- * purposes.</p>
+ * purposes.
*
* <p class="warning"><strong>Warning:</strong> This API may hit the network, and may block for
- * excessive amounts of time, up to 60 seconds or more. It's strongly encouraged to use the
- * asynchronous version of this API. If that is not possible, this should be run on a background
- * thread to avoid blocking other operations.</p>
+ * excessive amounts of time. It's strongly encouraged to use the asynchronous version of this
+ * API. If that is not possible, this should be run on a background thread to avoid blocking
+ * other operations.
*
- * @param locationName a user-supplied description of a location
- * @param maxResults max number of addresses to return. Smaller numbers (1 to 5) are
- * recommended
- * @param lowerLeftLatitude the latitude of the lower left corner of the bounding box
- * @param lowerLeftLongitude the longitude of the lower left corner of the bounding box
- * @param upperRightLatitude the latitude of the upper right corner of the bounding box
+ * @param locationName a user-supplied description of a location
+ * @param maxResults max number of addresses to return. Smaller numbers (1 to 5) are recommended
+ * @param lowerLeftLatitude the latitude of the lower left corner of the bounding box
+ * @param lowerLeftLongitude the longitude of the lower left corner of the bounding box
+ * @param upperRightLatitude the latitude of the upper right corner of the bounding box
* @param upperRightLongitude the longitude of the upper right corner of the bounding box
- *
- * @return a list of Address objects. Returns null or empty list if no matches were
- * found or there is no backend service available.
- *
+ * @return a list of Address objects. Returns null or empty list if no matches were found or
+ * there is no backend service available.
* @throws IllegalArgumentException if locationName is null
* @throws IllegalArgumentException if any latitude or longitude is invalid
- * @throws IOException if there is a failure
- *
+ * @throws IOException if there is a failure
* @deprecated Use {@link #getFromLocationName(String, int, double, double, double, double,
- * GeocodeListener)} instead to avoid blocking a thread waiting for results.
+ * GeocodeListener)} instead to avoid blocking a thread waiting for results.
*/
@Deprecated
public @Nullable List<Address> getFromLocationName(
@NonNull String locationName,
- @IntRange int maxResults,
+ @IntRange(from = 1) int maxResults,
@FloatRange(from = -90D, to = 90D) double lowerLeftLatitude,
@FloatRange(from = -180D, to = 180D) double lowerLeftLongitude,
@FloatRange(from = -90D, to = 90D) double upperRightLatitude,
- @FloatRange(from = -180D, to = 180D) double upperRightLongitude) throws IOException {
+ @FloatRange(from = -180D, to = 180D) double upperRightLongitude)
+ throws IOException {
SynchronousGeocoder listener = new SynchronousGeocoder();
getFromLocationName(locationName, maxResults, lowerLeftLatitude, lowerLeftLongitude,
upperRightLatitude, upperRightLongitude, listener);
@@ -283,75 +292,79 @@ public final class Geocoder {
* View, CA", an airport code such as "SFO", and so forth. The returned addresses should be
* localized for the locale provided to this class's constructor.
*
- * <p> You may specify a bounding box for the search results by including the latitude and
+ * <p>You may specify a bounding box for the search results by including the latitude and
* longitude of the lower left point and upper right point of the box.
*
* <p class="note"><strong>Warning:</strong> Geocoding services may provide no guarantees on
* availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful
* or correct. Do <b>NOT</b> use this API for any safety-critical or regulatory compliance
- * purposes.</p>
+ * purposes.
*
- * @param locationName a user-supplied description of a location
- * @param maxResults max number of addresses to return. Smaller numbers (1 to 5) are
- * recommended
- * @param lowerLeftLatitude the latitude of the lower left corner of the bounding box
- * @param lowerLeftLongitude the longitude of the lower left corner of the bounding box
- * @param upperRightLatitude the latitude of the upper right corner of the bounding box
+ * @param locationName a user-supplied description of a location
+ * @param maxResults max number of addresses to return. Smaller numbers (1 to 5) are recommended
+ * @param lowerLeftLatitude the latitude of the lower left corner of the bounding box
+ * @param lowerLeftLongitude the longitude of the lower left corner of the bounding box
+ * @param upperRightLatitude the latitude of the upper right corner of the bounding box
* @param upperRightLongitude the longitude of the upper right corner of the bounding box
- * @param listener a listener for receiving results
- *
+ * @param listener a listener for receiving results
* @throws IllegalArgumentException if locationName is null
* @throws IllegalArgumentException if any latitude or longitude is invalid
*/
public void getFromLocationName(
@NonNull String locationName,
- @IntRange int maxResults,
+ @IntRange(from = 1) int maxResults,
@FloatRange(from = -90D, to = 90D) double lowerLeftLatitude,
@FloatRange(from = -180D, to = 180D) double lowerLeftLongitude,
@FloatRange(from = -90D, to = 90D) double upperRightLatitude,
@FloatRange(from = -180D, to = 180D) double upperRightLongitude,
@NonNull GeocodeListener listener) {
- Preconditions.checkArgument(locationName != null);
- Preconditions.checkArgumentInRange(lowerLeftLatitude, -90.0, 90.0, "lowerLeftLatitude");
- Preconditions.checkArgumentInRange(lowerLeftLongitude, -180.0, 180.0, "lowerLeftLongitude");
- Preconditions.checkArgumentInRange(upperRightLatitude, -90.0, 90.0, "upperRightLatitude");
- Preconditions.checkArgumentInRange(upperRightLongitude, -180.0, 180.0,
- "upperRightLongitude");
-
+ ForwardGeocodeRequest.Builder b =
+ new ForwardGeocodeRequest.Builder(
+ locationName,
+ lowerLeftLatitude,
+ lowerLeftLongitude,
+ upperRightLatitude,
+ upperRightLongitude,
+ maxResults,
+ mLocale,
+ Process.myUid(),
+ mContext.getPackageName());
+ if (mContext.getAttributionTag() != null) {
+ b.setCallingAttributionTag(mContext.getAttributionTag());
+ }
try {
- mService.getFromLocationName(locationName, lowerLeftLatitude, lowerLeftLongitude,
- upperRightLatitude, upperRightLongitude, maxResults, mParams,
- new GeocoderImpl(listener));
+ mService.forwardGeocode(b.build(), new GeocodeCallbackImpl(listener));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
- private static class GeocoderImpl extends IGeocodeListener.Stub {
+ private static class GeocodeCallbackImpl extends IGeocodeCallback.Stub {
- private GeocodeListener mListener;
+ @Nullable private GeocodeListener mListener;
- GeocoderImpl(GeocodeListener listener) {
+ GeocodeCallbackImpl(GeocodeListener listener) {
mListener = Objects.requireNonNull(listener);
}
@Override
- public void onResults(String error, List<Address> addresses) throws RemoteException {
+ public void onError(@Nullable String error) {
if (mListener == null) {
return;
}
- GeocodeListener listener = mListener;
+ mListener.onError(error);
mListener = null;
+ }
- if (error != null) {
- listener.onError(error);
- } else {
- if (addresses == null) {
- addresses = Collections.emptyList();
- }
- listener.onGeocode(addresses);
+ @Override
+ public void onResults(List<Address> addresses) {
+ if (mListener == null) {
+ return;
}
+
+ mListener.onGeocode(addresses);
+ mListener = null;
}
}
@@ -364,21 +377,21 @@ public final class Geocoder {
SynchronousGeocoder() {}
@Override
- public void onGeocode(List<Address> addresses) {
+ public void onGeocode(@NonNull List<Address> addresses) {
mResults = addresses;
mLatch.countDown();
}
@Override
- public void onError(String errorMessage) {
- mError = errorMessage;
+ public void onError(@Nullable String error) {
+ mError = error;
mLatch.countDown();
}
public List<Address> getResults() throws IOException {
try {
if (!mLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
- mError = "Service not Available";
+ throw new IOException(new TimeoutException());
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
diff --git a/location/java/android/location/IGeocodeProvider.aidl b/location/java/android/location/IGeocodeProvider.aidl
deleted file mode 100644
index e661ca6cca2e..000000000000
--- a/location/java/android/location/IGeocodeProvider.aidl
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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.
- */
-
-package android.location;
-
-import android.location.Address;
-import android.location.IGeocodeListener;
-import android.location.GeocoderParams;
-
-/**
- * An interface for location providers implementing the Geocoder services.
- *
- * {@hide}
- */
-interface IGeocodeProvider {
-
- oneway void getFromLocation(double latitude, double longitude, int maxResults, in GeocoderParams params, in IGeocodeListener listener);
- oneway void getFromLocationName(String locationName, double lowerLeftLatitude, double lowerLeftLongitude, double upperRightLatitude,
- double upperRightLongitude, int maxResults, in GeocoderParams params, in IGeocodeListener listener);
-}
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 72761ef47569..c96c11898671 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -19,13 +19,11 @@ package android.location;
import android.app.PendingIntent;
import android.location.Address;
import android.location.Criteria;
-import android.location.GeocoderParams;
import android.location.Geofence;
import android.location.GnssAntennaInfo;
import android.location.GnssCapabilities;
import android.location.GnssMeasurementCorrections;
import android.location.GnssMeasurementRequest;
-import android.location.IGeocodeListener;
import android.location.IGnssAntennaInfoListener;
import android.location.IGnssMeasurementsListener;
import android.location.IGnssStatusListener;
@@ -37,8 +35,11 @@ import android.location.LastLocationRequest;
import android.location.Location;
import android.location.LocationRequest;
import android.location.LocationTime;
+import android.location.provider.ForwardGeocodeRequest;
+import android.location.provider.IGeocodeCallback;
import android.location.provider.IProviderRequestListener;
import android.location.provider.ProviderProperties;
+import android.location.provider.ReverseGeocodeRequest;
import android.os.Bundle;
import android.os.ICancellationSignal;
import android.os.PackageTagsList;
@@ -68,13 +69,9 @@ interface ILocationManager
void requestGeofence(in Geofence geofence, in PendingIntent intent, String packageName, String attributionTag);
void removeGeofence(in PendingIntent intent);
- boolean geocoderIsPresent();
- void getFromLocation(double latitude, double longitude, int maxResults,
- in GeocoderParams params, in IGeocodeListener listener);
- void getFromLocationName(String locationName,
- double lowerLeftLatitude, double lowerLeftLongitude,
- double upperRightLatitude, double upperRightLongitude, int maxResults,
- in GeocoderParams params, in IGeocodeListener listener);
+ boolean isGeocodeAvailable();
+ void reverseGeocode(in ReverseGeocodeRequest request, in IGeocodeCallback callback);
+ void forwardGeocode(in ForwardGeocodeRequest request, in IGeocodeCallback callback);
GnssCapabilities getGnssCapabilities();
int getGnssYearOfHardware();
diff --git a/location/java/android/location/flags/location.aconfig b/location/java/android/location/flags/location.aconfig
index 0fa641b926a9..156be389fe84 100644
--- a/location/java/android/location/flags/location.aconfig
+++ b/location/java/android/location/flags/location.aconfig
@@ -1,6 +1,13 @@
package: "android.location.flags"
flag {
+ name: "new_geocoder"
+ namespace: "location"
+ description: "Flag for new Geocoder APIs"
+ bug: "229872126"
+}
+
+flag {
name: "location_bypass"
namespace: "location"
description: "Enable location bypass appops behavior"
diff --git a/location/java/android/location/provider/ForwardGeocodeRequest.aidl b/location/java/android/location/provider/ForwardGeocodeRequest.aidl
new file mode 100644
index 000000000000..acd6190aeec8
--- /dev/null
+++ b/location/java/android/location/provider/ForwardGeocodeRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location.provider;
+
+parcelable ForwardGeocodeRequest;
diff --git a/location/java/android/location/provider/ForwardGeocodeRequest.java b/location/java/android/location/provider/ForwardGeocodeRequest.java
new file mode 100644
index 000000000000..8f227b1604b7
--- /dev/null
+++ b/location/java/android/location/provider/ForwardGeocodeRequest.java
@@ -0,0 +1,286 @@
+/*
+ * 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.location.provider;
+
+import static java.lang.Math.max;
+
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Locale;
+import java.util.Objects;
+
+/**
+ * Forward geocode (ie from address to lat/lng) provider request.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_NEW_GEOCODER)
+@SystemApi
+public final class ForwardGeocodeRequest implements Parcelable {
+
+ private final String mLocationName;
+ private final double mLowerLeftLatitude;
+ private final double mLowerLeftLongitude;
+ private final double mUpperRightLatitude;
+ private final double mUpperRightLongitude;
+ private final int mMaxResults;
+ private final Locale mLocale;
+ private final int mCallingUid;
+ private final String mCallingPackage;
+ @Nullable private final String mCallingAttributionTag;
+
+ private ForwardGeocodeRequest(
+ @NonNull String locationName,
+ double lowerLeftLatitude,
+ double lowerLeftLongitude,
+ double upperRightLatitude,
+ double upperRightLongitude,
+ int maxResults,
+ @NonNull Locale locale,
+ int callingUid,
+ @NonNull String callingPackage,
+ @Nullable String callingAttributionTag) {
+ Preconditions.checkArgument(locationName != null, "locationName must not be null");
+ Preconditions.checkArgumentInRange(lowerLeftLatitude, -90.0, 90.0, "lowerLeftLatitude");
+ Preconditions.checkArgumentInRange(lowerLeftLongitude, -180.0, 180.0, "lowerLeftLongitude");
+ Preconditions.checkArgumentInRange(upperRightLatitude, -90.0, 90.0, "upperRightLatitude");
+ Preconditions.checkArgumentInRange(
+ upperRightLongitude, -180.0, 180.0, "upperRightLongitude");
+
+ mLocationName = locationName;
+ mLowerLeftLatitude = lowerLeftLatitude;
+ mLowerLeftLongitude = lowerLeftLongitude;
+ mUpperRightLatitude = upperRightLatitude;
+ mUpperRightLongitude = upperRightLongitude;
+ mMaxResults = max(maxResults, 1);
+ mLocale = Objects.requireNonNull(locale);
+
+ mCallingUid = callingUid;
+ mCallingPackage = Objects.requireNonNull(callingPackage);
+ mCallingAttributionTag = callingAttributionTag;
+ }
+
+ /**
+ * The location name to be forward geocoded. An arbitrary user string that could have any value.
+ */
+ @NonNull
+ public String getLocationName() {
+ return mLocationName;
+ }
+
+ /** The lower left latitude of the bounding box that should constrain forward geocoding. */
+ @FloatRange(from = -90.0, to = 90.0)
+ public double getLowerLeftLatitude() {
+ return mLowerLeftLatitude;
+ }
+
+ /** The lower left longitude of the bounding box that should constrain forward geocoding. */
+ @FloatRange(from = -180.0, to = 180.0)
+ public double getLowerLeftLongitude() {
+ return mLowerLeftLongitude;
+ }
+
+ /** The upper right latitude of the bounding box that should constrain forward geocoding. */
+ @FloatRange(from = -90.0, to = 90.0)
+ public double getUpperRightLatitude() {
+ return mUpperRightLatitude;
+ }
+
+ /** The upper right longitude of the bounding box that should constrain forward geocoding. */
+ @FloatRange(from = -180.0, to = 180.0)
+ public double getUpperRightLongitude() {
+ return mUpperRightLongitude;
+ }
+
+ /** The maximum number of forward geocoding results that should be returned. */
+ @IntRange(from = 1)
+ public int getMaxResults() {
+ return mMaxResults;
+ }
+
+ /** The locale that results should be localized to (best effort). */
+ @NonNull
+ public Locale getLocale() {
+ return mLocale;
+ }
+
+ /** The UID of the caller this geocoding request is happening on behalf of. */
+ public int getCallingUid() {
+ return mCallingUid;
+ }
+
+ /** The package of the caller this geocoding request is happening on behalf of. */
+ @NonNull
+ public String getCallingPackage() {
+ return mCallingPackage;
+ }
+
+ /** The attribution tag of the caller this geocoding request is happening on behalf of. */
+ @Nullable
+ public String getCallingAttributionTag() {
+ return mCallingAttributionTag;
+ }
+
+ public static final @NonNull Creator<ForwardGeocodeRequest> CREATOR =
+ new Creator<>() {
+ @Override
+ public ForwardGeocodeRequest createFromParcel(Parcel in) {
+ return new ForwardGeocodeRequest(
+ Objects.requireNonNull(in.readString8()),
+ in.readDouble(),
+ in.readDouble(),
+ in.readDouble(),
+ in.readDouble(),
+ in.readInt(),
+ new Locale(in.readString8(), in.readString8(), in.readString8()),
+ in.readInt(),
+ Objects.requireNonNull(in.readString8()),
+ in.readString8());
+ }
+
+ @Override
+ public ForwardGeocodeRequest[] newArray(int size) {
+ return new ForwardGeocodeRequest[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeString8(mLocationName);
+ parcel.writeDouble(mLowerLeftLatitude);
+ parcel.writeDouble(mLowerLeftLongitude);
+ parcel.writeDouble(mUpperRightLatitude);
+ parcel.writeDouble(mUpperRightLongitude);
+ parcel.writeInt(mMaxResults);
+ parcel.writeString8(mLocale.getLanguage());
+ parcel.writeString8(mLocale.getCountry());
+ parcel.writeString8(mLocale.getVariant());
+ parcel.writeInt(mCallingUid);
+ parcel.writeString8(mCallingPackage);
+ parcel.writeString8(mCallingAttributionTag);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object object) {
+ if (object instanceof ForwardGeocodeRequest that) {
+ return mLowerLeftLatitude == that.mLowerLeftLatitude
+ && mLowerLeftLongitude == that.mLowerLeftLongitude
+ && mUpperRightLatitude == that.mUpperRightLatitude
+ && mUpperRightLongitude == that.mUpperRightLongitude
+ && mMaxResults == that.mMaxResults
+ && mCallingUid == that.mCallingUid
+ && mLocale.equals(that.mLocale)
+ && mCallingPackage.equals(that.mCallingPackage)
+ && mLocationName.equals(that.mLocationName)
+ && Objects.equals(mCallingAttributionTag, that.mCallingAttributionTag);
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ mLocationName,
+ mLowerLeftLatitude,
+ mLowerLeftLongitude,
+ mUpperRightLatitude,
+ mUpperRightLongitude,
+ mMaxResults,
+ mLocale,
+ mCallingUid,
+ mCallingPackage,
+ mCallingAttributionTag);
+ }
+
+ /** A Builder for {@link ReverseGeocodeRequest}s. */
+ public static final class Builder {
+
+ private final String mLocationName;
+ private final double mLowerLeftLatitude;
+ private final double mLowerLeftLongitude;
+ private final double mUpperRightLatitude;
+ private final double mUpperRightLongitude;
+ private final int mMaxResults;
+ private final Locale mLocale;
+
+ private final int mCallingUid;
+ private final String mCallingPackage;
+ @Nullable private String mCallingAttributionTag;
+
+ /** Creates a new Builder instance with the given parameters. */
+ public Builder(
+ @NonNull String locationName,
+ @FloatRange(from = -90.0, to = 90.0) double lowerLeftLatitude,
+ @FloatRange(from = -180.0, to = 180.0) double lowerLeftLongitude,
+ @FloatRange(from = -90.0, to = 90.0) double upperRightLatitude,
+ @FloatRange(from = -180.0, to = 180.0) double upperRightLongitude,
+ @IntRange(from = 1) int maxResults,
+ @NonNull Locale locale,
+ int callingUid,
+ @NonNull String callingPackage) {
+ mLocationName = locationName;
+ mLowerLeftLatitude = lowerLeftLatitude;
+ mLowerLeftLongitude = lowerLeftLongitude;
+ mUpperRightLatitude = upperRightLatitude;
+ mUpperRightLongitude = upperRightLongitude;
+ mMaxResults = maxResults;
+ mLocale = locale;
+ mCallingUid = callingUid;
+ mCallingPackage = callingPackage;
+ mCallingAttributionTag = null;
+ }
+
+ /** Sets the attribution tag. */
+ @NonNull
+ public Builder setCallingAttributionTag(@NonNull String attributionTag) {
+ mCallingAttributionTag = attributionTag;
+ return this;
+ }
+
+ /** Builds a {@link ForwardGeocodeRequest}. */
+ @NonNull
+ public ForwardGeocodeRequest build() {
+ return new ForwardGeocodeRequest(
+ mLocationName,
+ mLowerLeftLatitude,
+ mLowerLeftLongitude,
+ mUpperRightLatitude,
+ mUpperRightLongitude,
+ mMaxResults,
+ mLocale,
+ mCallingUid,
+ mCallingPackage,
+ mCallingAttributionTag);
+ }
+ }
+}
diff --git a/location/java/android/location/provider/GeocodeProviderBase.java b/location/java/android/location/provider/GeocodeProviderBase.java
new file mode 100644
index 000000000000..e2c48b9c3515
--- /dev/null
+++ b/location/java/android/location/provider/GeocodeProviderBase.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location.provider;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.content.Intent;
+import android.location.Address;
+import android.location.flags.Flags;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.OutcomeReceiver;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Base class for geocode providers outside the system server.
+ *
+ * <p>Geocode providers should be wrapped in a non-exported service which returns the result of
+ * {@link #getBinder()} from the service's {@link android.app.Service#onBind(Intent)} method. The
+ * service should not be exported so that components other than the system server cannot bind to it.
+ * Alternatively, the service may be guarded by a permission that only system server can obtain. The
+ * service may specify metadata on its capabilities:
+ *
+ * <ul>
+ * <li>"serviceVersion": An integer version code to help tie break if multiple services are
+ * capable of implementing the geocode provider. All else equal, the service with the highest
+ * version code will be chosen. Assumed to be 0 if not specified.
+ * <li>"serviceIsMultiuser": A boolean property, indicating if the service wishes to take
+ * responsibility for handling changes to the current user on the device. If true, the service
+ * will always be bound from the system user. If false, the service will always be bound from
+ * the current user. If the current user changes, the old binding will be released, and a new
+ * binding established under the new user. Assumed to be false if not specified.
+ * </ul>
+ *
+ * <p>The service should have an intent filter in place for the geocode provider as specified by the
+ * constant in this class.
+ *
+ * <p>Geocode providers are identified by their UID / package name / attribution tag. Based on this
+ * identity, geocode providers may be given some special privileges.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_NEW_GEOCODER)
+@SystemApi
+public abstract class GeocodeProviderBase {
+
+ /**
+ * The action the wrapping service should have in its intent filter to implement the geocode
+ * provider.
+ */
+ @SuppressLint("ActionValue")
+ public static final String ACTION_GEOCODE_PROVIDER =
+ "com.android.location.service.GeocodeProvider";
+
+ final String mTag;
+ @Nullable final String mAttributionTag;
+ final IBinder mBinder;
+
+ /**
+ * Subclasses should pass in a context and an arbitrary tag that may be used for logcat logging
+ * of errors, and thus should uniquely identify the class.
+ */
+ public GeocodeProviderBase(@NonNull Context context, @NonNull String tag) {
+ mTag = tag;
+ mAttributionTag = context.getAttributionTag();
+ mBinder = new Service();
+ }
+
+ /**
+ * Returns the IBinder instance that should be returned from the {@link
+ * android.app.Service#onBind(Intent)} method of the wrapping service.
+ */
+ @NonNull
+ public final IBinder getBinder() {
+ return mBinder;
+ }
+
+ /**
+ * Requests forward geocoding of the given arguments. The given callback must be invoked once.
+ */
+ public abstract void onForwardGeocode(
+ @NonNull ForwardGeocodeRequest request,
+ @NonNull OutcomeReceiver<List<Address>, Exception> callback);
+
+ /**
+ * Requests reverse geocoding of the given arguments. The given callback must be invoked once.
+ */
+ public abstract void onReverseGeocode(
+ @NonNull ReverseGeocodeRequest request,
+ @NonNull OutcomeReceiver<List<Address>, Exception> callback);
+
+ private class Service extends IGeocodeProvider.Stub {
+ @Override
+ public void forwardGeocode(ForwardGeocodeRequest request, IGeocodeCallback callback) {
+ try {
+ onForwardGeocode(request, new SingleUseCallback(callback));
+ } catch (RuntimeException e) {
+ // exceptions on one-way binder threads are dropped - move to a different thread
+ Log.w(mTag, e);
+ new Handler(Looper.getMainLooper())
+ .post(
+ () -> {
+ throw new AssertionError(e);
+ });
+ }
+ }
+
+ @Override
+ public void reverseGeocode(ReverseGeocodeRequest request, IGeocodeCallback callback) {
+ try {
+ onReverseGeocode(request, new SingleUseCallback(callback));
+ } catch (RuntimeException e) {
+ // exceptions on one-way binder threads are dropped - move to a different thread
+ Log.w(mTag, e);
+ new Handler(Looper.getMainLooper())
+ .post(
+ () -> {
+ throw new AssertionError(e);
+ });
+ }
+ }
+ }
+
+ private static class SingleUseCallback implements OutcomeReceiver<List<Address>, Exception> {
+
+ private final AtomicReference<IGeocodeCallback> mCallback;
+
+ SingleUseCallback(IGeocodeCallback callback) {
+ mCallback = new AtomicReference<>(callback);
+ }
+
+ @Override
+ public void onError(Exception e) {
+ try {
+ Objects.requireNonNull(mCallback.getAndSet(null)).onError(e.toString());
+ } catch (RemoteException r) {
+ throw r.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ public void onResult(List<Address> results) {
+ try {
+ Objects.requireNonNull(mCallback.getAndSet(null)).onResults(results);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+}
diff --git a/location/java/android/location/IGeocodeListener.aidl b/location/java/android/location/provider/IGeocodeCallback.aidl
index 8e104119a6f3..cf527130bcc3 100644
--- a/location/java/android/location/IGeocodeListener.aidl
+++ b/location/java/android/location/provider/IGeocodeCallback.aidl
@@ -14,16 +14,15 @@
* limitations under the License.
*/
-package android.location;
+package android.location.provider;
import android.location.Address;
/**
- * An interface for returning geocode results.
- *
- * {@hide}
+ * Binder interface for geocoding callbacks.
+ * @hide
*/
-interface IGeocodeListener {
-
- oneway void onResults(String error, in List<Address> results);
+oneway interface IGeocodeCallback {
+ void onError(String error);
+ void onResults(in List<Address> results);
}
diff --git a/location/java/android/location/provider/IGeocodeProvider.aidl b/location/java/android/location/provider/IGeocodeProvider.aidl
new file mode 100644
index 000000000000..92174387c578
--- /dev/null
+++ b/location/java/android/location/provider/IGeocodeProvider.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+package android.location.provider;
+
+import android.location.provider.IGeocodeCallback;
+import android.location.provider.ForwardGeocodeRequest;
+import android.location.provider.ReverseGeocodeRequest;
+
+/**
+ * Binder interface for services that implement geocode providers. Do not implement this directly,
+ * extend {@link GeocodeProviderBase} instead.
+ * @hide
+ */
+oneway interface IGeocodeProvider {
+ void forwardGeocode(in ForwardGeocodeRequest request, in IGeocodeCallback callback);
+ void reverseGeocode(in ReverseGeocodeRequest request, in IGeocodeCallback callback);
+}
diff --git a/core/java/android/service/voice/HotwordTrainingAudio.aidl b/location/java/android/location/provider/ReverseGeocodeRequest.aidl
index 4dd22897f831..015757ad5d2d 100644
--- a/core/java/android/service/voice/HotwordTrainingAudio.aidl
+++ b/location/java/android/location/provider/ReverseGeocodeRequest.aidl
@@ -1,6 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
- *
+ * Copyright (C) 2024
* 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
@@ -14,6 +13,6 @@
* limitations under the License.
*/
-package android.service.voice;
+package android.location.provider;
-parcelable HotwordTrainingAudio; \ No newline at end of file
+parcelable ReverseGeocodeRequest;
diff --git a/location/java/android/location/provider/ReverseGeocodeRequest.java b/location/java/android/location/provider/ReverseGeocodeRequest.java
new file mode 100644
index 000000000000..57c9047f07ca
--- /dev/null
+++ b/location/java/android/location/provider/ReverseGeocodeRequest.java
@@ -0,0 +1,230 @@
+/*
+ * 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.location.provider;
+
+import static java.lang.Math.max;
+
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.location.flags.Flags;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Locale;
+import java.util.Objects;
+
+/**
+ * Reverse geocode (ie from lat/lng to address) provider request.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_NEW_GEOCODER)
+@SystemApi
+public final class ReverseGeocodeRequest implements Parcelable {
+
+ private final double mLatitude;
+ private final double mLongitude;
+ private final int mMaxResults;
+ private final Locale mLocale;
+
+ private final int mCallingUid;
+ private final String mCallingPackage;
+ @Nullable private final String mCallingAttributionTag;
+
+ private ReverseGeocodeRequest(
+ double latitude,
+ double longitude,
+ int maxResults,
+ Locale locale,
+ int callingUid,
+ String callingPackage,
+ @Nullable String callingAttributionTag) {
+ Preconditions.checkArgumentInRange(latitude, -90.0, 90.0, "latitude");
+ Preconditions.checkArgumentInRange(longitude, -180.0, 180.0, "longitude");
+
+ mLatitude = latitude;
+ mLongitude = longitude;
+ mMaxResults = max(maxResults, 1);
+ mLocale = Objects.requireNonNull(locale);
+
+ mCallingUid = callingUid;
+ mCallingPackage = Objects.requireNonNull(callingPackage);
+ mCallingAttributionTag = callingAttributionTag;
+ }
+
+ /** The latitude of the point to be reverse geocoded. */
+ @FloatRange(from = -90.0, to = 90.0)
+ public double getLatitude() {
+ return mLatitude;
+ }
+
+ /** The longitude of the point to be reverse geocoded. */
+ @FloatRange(from = -180.0, to = 180.0)
+ public double getLongitude() {
+ return mLongitude;
+ }
+
+ /** The maximum number of reverse geocoding results that should be returned. */
+ @IntRange(from = 1)
+ public int getMaxResults() {
+ return mMaxResults;
+ }
+
+ /** The locale that results should be localized to (best effort). */
+ @NonNull
+ public Locale getLocale() {
+ return mLocale;
+ }
+
+ /** The UID of the caller this geocoding request is happening on behalf of. */
+ public int getCallingUid() {
+ return mCallingUid;
+ }
+
+ /** The package of the caller this geocoding request is happening on behalf of. */
+ @NonNull
+ public String getCallingPackage() {
+ return mCallingPackage;
+ }
+
+ /** The attribution tag of the caller this geocoding request is happening on behalf of. */
+ @Nullable
+ public String getCallingAttributionTag() {
+ return mCallingAttributionTag;
+ }
+
+ public static final @NonNull Creator<ReverseGeocodeRequest> CREATOR =
+ new Creator<>() {
+ @Override
+ public ReverseGeocodeRequest createFromParcel(Parcel in) {
+ return new ReverseGeocodeRequest(
+ in.readDouble(),
+ in.readDouble(),
+ in.readInt(),
+ new Locale(in.readString8(), in.readString8(), in.readString8()),
+ in.readInt(),
+ Objects.requireNonNull(in.readString8()),
+ in.readString8());
+ }
+
+ @Override
+ public ReverseGeocodeRequest[] newArray(int size) {
+ return new ReverseGeocodeRequest[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeDouble(mLatitude);
+ parcel.writeDouble(mLongitude);
+ parcel.writeInt(mMaxResults);
+ parcel.writeString8(mLocale.getLanguage());
+ parcel.writeString8(mLocale.getCountry());
+ parcel.writeString8(mLocale.getVariant());
+ parcel.writeInt(mCallingUid);
+ parcel.writeString8(mCallingPackage);
+ parcel.writeString8(mCallingAttributionTag);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object object) {
+ if (object instanceof ReverseGeocodeRequest that) {
+ return mLatitude == that.mLatitude
+ && mLongitude == that.mLongitude
+ && mMaxResults == that.mMaxResults
+ && mCallingUid == that.mCallingUid
+ && mLocale.equals(that.mLocale)
+ && mCallingPackage.equals(that.mCallingPackage)
+ && Objects.equals(mCallingAttributionTag, that.mCallingAttributionTag);
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ mLatitude,
+ mLongitude,
+ mMaxResults,
+ mLocale,
+ mCallingUid,
+ mCallingPackage,
+ mCallingAttributionTag);
+ }
+
+ /** A Builder for {@link ReverseGeocodeRequest}s. */
+ public static final class Builder {
+
+ private final double mLatitude;
+ private final double mLongitude;
+ private final int mMaxResults;
+ private final Locale mLocale;
+
+ private final int mCallingUid;
+ private final String mCallingPackage;
+ @Nullable private String mCallingAttributionTag;
+
+ /** Creates a new Builder instance with the given parameters. */
+ public Builder(
+ @FloatRange(from = -90.0, to = 90.0) double latitude,
+ @FloatRange(from = -180.0, to = 180.0) double longitude,
+ @IntRange(from = 0) int maxResults,
+ @NonNull Locale locale,
+ int callingUid,
+ @NonNull String callingPackage) {
+ mLatitude = latitude;
+ mLongitude = longitude;
+ mMaxResults = maxResults;
+ mLocale = locale;
+ mCallingUid = callingUid;
+ mCallingPackage = callingPackage;
+ mCallingAttributionTag = null;
+ }
+
+ /** Sets the attribution tag. */
+ @NonNull
+ public Builder setCallingAttributionTag(@NonNull String attributionTag) {
+ mCallingAttributionTag = attributionTag;
+ return this;
+ }
+
+ /** Builds a {@link ReverseGeocodeRequest}. */
+ @NonNull
+ public ReverseGeocodeRequest build() {
+ return new ReverseGeocodeRequest(
+ mLatitude,
+ mLongitude,
+ mMaxResults,
+ mLocale,
+ mCallingUid,
+ mCallingPackage,
+ mCallingAttributionTag);
+ }
+ }
+}
diff --git a/location/lib/Android.bp b/location/lib/Android.bp
index c9be5797fdbc..b10019a94209 100644
--- a/location/lib/Android.bp
+++ b/location/lib/Android.bp
@@ -30,6 +30,7 @@ java_sdk_library {
"androidx.annotation_annotation",
],
api_packages: [
+ "android.location",
"com.android.location.provider",
"com.android.location.timezone.provider",
],
diff --git a/location/lib/README.txt b/location/lib/README.txt
index 400a7dd3f6ba..ae5187245163 100644
--- a/location/lib/README.txt
+++ b/location/lib/README.txt
@@ -1,30 +1,12 @@
This library (com.android.location.provider.jar) is a shared java library
-containing classes required by unbundled location providers.
-
---- Rules of this library ---
-o This library is effectively a PUBLIC API for unbundled location providers
- that may be distributed outside the system image. So it MUST BE API STABLE.
- You can add but not remove. The rules are the same as for the
- public platform SDK API.
-o This library can see and instantiate internal platform classes (such as
- ProviderRequest.java), but it must not expose them in any public method
- (or by extending them via inheritance). This would break clients of the
- library because they cannot see the internal platform classes.
-
-This library is distributed in the system image, and loaded as
-a shared library. So you can change the implementation, but not
-the interface. In this way it is like framework.jar.
-
---- Why does this library exists? ---
-
-Unbundled location providers (such as the NetworkLocationProvider)
-can not use internal platform classes.
-
-So ideally all of these classes would be part of the public platform SDK API,
-but that doesn't seem like a great idea when only applications with a special
-signature can implement this API.
-
-The compromise is this library.
-
-It wraps internal platform classes (like ProviderRequest) with a stable
-API that does not leak the internal classes.
+containing classes required by unbundled providers. The library was created
+as a way of exposing API classes outside of the public API before SystemApi
+was possible. Now that SystemApi exists, no new classes should ever be added
+to this library, and all classes in this library should eventually be
+deprecated and new SystemApi replacements offered.
+
+Whether or not classes in this library can ever be removed must be answered on
+a case by case basis. Most of the classes are usually referenced by Google Play
+services (in which case references can be removed from that code base), but
+these classes may also be referenced by OEM code, which must be considered
+before any removal.
diff --git a/location/lib/api/system-current.txt b/location/lib/api/system-current.txt
index 7046abd8fd3b..75e6bb437ca4 100644
--- a/location/lib/api/system-current.txt
+++ b/location/lib/api/system-current.txt
@@ -1,4 +1,21 @@
// Signature format: 2.0
+package android.location {
+
+ @Deprecated public class GeocoderParams implements android.os.Parcelable {
+ ctor @Deprecated public GeocoderParams(android.content.Context);
+ ctor @Deprecated public GeocoderParams(android.content.Context, java.util.Locale);
+ ctor @Deprecated public GeocoderParams(int, String, @Nullable String, java.util.Locale);
+ method @Deprecated public int describeContents();
+ method @Deprecated @Nullable public String getClientAttributionTag();
+ method @Deprecated @NonNull public String getClientPackage();
+ method @Deprecated public int getClientUid();
+ method @Deprecated @NonNull public java.util.Locale getLocale();
+ method @Deprecated public void writeToParcel(android.os.Parcel, int);
+ field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.location.GeocoderParams> CREATOR;
+ }
+
+}
+
package com.android.location.provider {
@Deprecated public final class FusedLocationHardware {
@@ -25,6 +42,13 @@ package com.android.location.provider {
method @Deprecated public void onStatusChanged(int);
}
+ @Deprecated public abstract class GeocodeProvider {
+ ctor @Deprecated public GeocodeProvider();
+ method @Deprecated public android.os.IBinder getBinder();
+ method @Deprecated public abstract String onGetFromLocation(double, double, int, android.location.GeocoderParams, java.util.List<android.location.Address>);
+ method @Deprecated public abstract String onGetFromLocationName(String, double, double, double, double, int, android.location.GeocoderParams, java.util.List<android.location.Address>);
+ }
+
@Deprecated public class GmsFusedBatchOptions {
ctor @Deprecated public GmsFusedBatchOptions();
method @Deprecated public int getFlags();
diff --git a/core/java/android/location/GeocoderParams.java b/location/lib/java/android/location/GeocoderParams.java
index 3ea6364e07c5..780ccf4ca53c 100644
--- a/core/java/android/location/GeocoderParams.java
+++ b/location/lib/java/android/location/GeocoderParams.java
@@ -18,7 +18,7 @@ package android.location;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.compat.annotation.UnsupportedAppUsage;
+import android.annotation.SystemApi;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
@@ -28,15 +28,15 @@ import java.util.Locale;
import java.util.Objects;
/**
- * This class contains extra parameters to pass to an IGeocodeProvider
- * implementation from the Geocoder class. Currently this contains the
- * language, country and variant information from the Geocoder's locale
- * as well as the Geocoder client's package name for geocoder server
- * logging. This information is kept in a separate class to allow for
- * future expansion of the IGeocodeProvider interface.
+ * This class was originally shipped out-of-band from the normal API processes as a separate drop
+ * before SystemApi existed. Now that SystemApi does exist, this class has been retroactively
+ * published through SystemApi.
*
+ * @deprecated Do not use.
* @hide
*/
+@Deprecated
+@SystemApi
public class GeocoderParams implements Parcelable {
private final int mUid;
@@ -52,7 +52,8 @@ public class GeocoderParams implements Parcelable {
this(Process.myUid(), context.getPackageName(), context.getAttributionTag(), locale);
}
- private GeocoderParams(int uid, String packageName, String attributionTag, Locale locale) {
+ public GeocoderParams(
+ int uid, String packageName, @Nullable String attributionTag, Locale locale) {
mUid = uid;
mPackageName = Objects.requireNonNull(packageName);
mAttributionTag = attributionTag;
@@ -62,7 +63,6 @@ public class GeocoderParams implements Parcelable {
/**
* Returns the client UID.
*/
- @UnsupportedAppUsage
public int getClientUid() {
return mUid;
}
@@ -70,7 +70,6 @@ public class GeocoderParams implements Parcelable {
/**
* Returns the client package name.
*/
- @UnsupportedAppUsage
public @NonNull String getClientPackage() {
return mPackageName;
}
@@ -78,7 +77,6 @@ public class GeocoderParams implements Parcelable {
/**
* Returns the client attribution tag.
*/
- @UnsupportedAppUsage
public @Nullable String getClientAttributionTag() {
return mAttributionTag;
}
@@ -86,34 +84,38 @@ public class GeocoderParams implements Parcelable {
/**
* Returns the locale.
*/
- @UnsupportedAppUsage
public @NonNull Locale getLocale() {
return mLocale;
}
public static final @NonNull Parcelable.Creator<GeocoderParams> CREATOR =
- new Parcelable.Creator<GeocoderParams>() {
- public GeocoderParams createFromParcel(Parcel in) {
- int uid = in.readInt();
- String packageName = in.readString8();
- String attributionTag = in.readString8();
- String language = in.readString8();
- String country = in.readString8();
- String variant = in.readString8();
-
- return new GeocoderParams(uid, packageName, attributionTag,
- new Locale(language, country, variant));
- }
-
- public GeocoderParams[] newArray(int size) {
- return new GeocoderParams[size];
- }
- };
-
+ new Parcelable.Creator<>() {
+ public GeocoderParams createFromParcel(Parcel in) {
+ int uid = in.readInt();
+ String packageName = in.readString8();
+ String attributionTag = in.readString8();
+ String language = in.readString8();
+ String country = in.readString8();
+ String variant = in.readString8();
+
+ return new GeocoderParams(
+ uid,
+ packageName,
+ attributionTag,
+ new Locale(language, country, variant));
+ }
+
+ public GeocoderParams[] newArray(int size) {
+ return new GeocoderParams[size];
+ }
+ };
+
+ @Override
public int describeContents() {
return 0;
}
+ @Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(mUid);
parcel.writeString8(mPackageName);
diff --git a/location/lib/java/com/android/location/provider/GeocodeProvider.java b/location/lib/java/com/android/location/provider/GeocodeProvider.java
index 05d793542202..45077ba8adc5 100644
--- a/location/lib/java/com/android/location/provider/GeocodeProvider.java
+++ b/location/lib/java/com/android/location/provider/GeocodeProvider.java
@@ -16,10 +16,13 @@
package com.android.location.provider;
+import android.annotation.SystemApi;
import android.location.Address;
import android.location.GeocoderParams;
-import android.location.IGeocodeListener;
-import android.location.IGeocodeProvider;
+import android.location.provider.ForwardGeocodeRequest;
+import android.location.provider.IGeocodeCallback;
+import android.location.provider.IGeocodeProvider;
+import android.location.provider.ReverseGeocodeRequest;
import android.os.IBinder;
import android.os.RemoteException;
@@ -27,47 +30,74 @@ import java.util.ArrayList;
import java.util.List;
/**
- * Base class for geocode providers implemented as unbundled services.
+ * This class was originally shipped out-of-band from the normal API processes as a separate drop
+ * before SystemApi existed. Now that SystemApi does exist, this class has been retroactively
+ * published through SystemApi.
*
- * <p>Geocode providers can be implemented as services and return the result of
- * {@link GeocodeProvider#getBinder()} in its getBinder() method.
- *
- * <p>IMPORTANT: This class is effectively a public API for unbundled
- * applications, and must remain API stable. See README.txt in the root
- * of this package for more information.
+ * @deprecated Use {@link android.location.provider.GeocodeProviderBase} instead.
* @hide
*/
+@Deprecated
+@SystemApi
public abstract class GeocodeProvider {
- private IGeocodeProvider.Stub mProvider = new IGeocodeProvider.Stub() {
- @Override
- public void getFromLocation(double latitude, double longitude, int maxResults,
- GeocoderParams params, IGeocodeListener listener) {
- List<Address> results = new ArrayList<>();
- String error = onGetFromLocation(latitude, longitude, maxResults, params, results);
- try {
- listener.onResults(error, results);
- } catch (RemoteException e) {
- // ignore
- }
- }
+ private final IGeocodeProvider.Stub mProvider =
+ new IGeocodeProvider.Stub() {
+ @Override
+ public void reverseGeocode(
+ ReverseGeocodeRequest request, IGeocodeCallback callback) {
+ List<Address> results = new ArrayList<>();
+ String error =
+ onGetFromLocation(
+ request.getLatitude(),
+ request.getLongitude(),
+ request.getMaxResults(),
+ new GeocoderParams(
+ request.getCallingUid(),
+ request.getCallingPackage(),
+ request.getCallingAttributionTag(),
+ request.getLocale()),
+ results);
+ try {
+ if (error != null) {
+ callback.onError(error);
+ } else {
+ callback.onResults(results);
+ }
+ } catch (RemoteException e) {
+ // ignore
+ }
+ }
- @Override
- public void getFromLocationName(String locationName,
- double lowerLeftLatitude, double lowerLeftLongitude,
- double upperRightLatitude, double upperRightLongitude, int maxResults,
- GeocoderParams params, IGeocodeListener listener) {
- List<Address> results = new ArrayList<>();
- String error = onGetFromLocationName(locationName, lowerLeftLatitude,
- lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
- maxResults, params, results);
- try {
- listener.onResults(error, results);
- } catch (RemoteException e) {
- // ignore
- }
- }
- };
+ @Override
+ public void forwardGeocode(
+ ForwardGeocodeRequest request, IGeocodeCallback callback) {
+ List<Address> results = new ArrayList<>();
+ String error =
+ onGetFromLocationName(
+ request.getLocationName(),
+ request.getLowerLeftLatitude(),
+ request.getLowerLeftLongitude(),
+ request.getUpperRightLatitude(),
+ request.getUpperRightLongitude(),
+ request.getMaxResults(),
+ new GeocoderParams(
+ request.getCallingUid(),
+ request.getCallingPackage(),
+ request.getCallingAttributionTag(),
+ request.getLocale()),
+ results);
+ try {
+ if (error != null) {
+ callback.onError(error);
+ } else {
+ callback.onResults(results);
+ }
+ } catch (RemoteException e) {
+ // ignore
+ }
+ }
+ };
/**
* This method is overridden to implement the
diff --git a/media/java/android/media/FadeManagerConfiguration.java b/media/java/android/media/FadeManagerConfiguration.java
index 4f1a8ee5e728..e6ec2c330340 100644
--- a/media/java/android/media/FadeManagerConfiguration.java
+++ b/media/java/android/media/FadeManagerConfiguration.java
@@ -21,6 +21,7 @@ import static android.media.audiopolicy.Flags.FLAG_ENABLE_FADE_MANAGER_CONFIGURA
import android.annotation.DurationMillisLong;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -274,7 +275,7 @@ public final class FadeManagerConfiguration implements Parcelable {
* @throws IllegalArgumentException if the usage is invalid
* @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED}
*/
- @DurationMillisLong
+ @IntRange(from = 0) @DurationMillisLong
public long getFadeOutDurationForUsage(@AudioAttributes.AttributeUsage int usage) {
ensureFadingIsEnabled();
validateUsage(usage);
@@ -290,7 +291,7 @@ public final class FadeManagerConfiguration implements Parcelable {
* @throws IllegalArgumentException if the usage is invalid
* @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED}
*/
- @DurationMillisLong
+ @IntRange(from = 0) @DurationMillisLong
public long getFadeInDurationForUsage(@AudioAttributes.AttributeUsage int usage) {
ensureFadingIsEnabled();
validateUsage(usage);
@@ -345,7 +346,7 @@ public final class FadeManagerConfiguration implements Parcelable {
* @throws NullPointerException if the audio attributes is {@code null}
* @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED}
*/
- @DurationMillisLong
+ @IntRange(from = 0) @DurationMillisLong
public long getFadeOutDurationForAudioAttributes(@NonNull AudioAttributes audioAttributes) {
ensureFadingIsEnabled();
return getDurationForVolumeShaperConfig(getVolumeShaperConfigFromWrapper(
@@ -361,7 +362,7 @@ public final class FadeManagerConfiguration implements Parcelable {
* @throws NullPointerException if the audio attributes is {@code null}
* @throws IllegalStateException if the fade state is set to {@link #FADE_STATE_DISABLED}
*/
- @DurationMillisLong
+ @IntRange(from = 0) @DurationMillisLong
public long getFadeInDurationForAudioAttributes(@NonNull AudioAttributes audioAttributes) {
ensureFadingIsEnabled();
return getDurationForVolumeShaperConfig(getVolumeShaperConfigFromWrapper(
@@ -428,7 +429,7 @@ public final class FadeManagerConfiguration implements Parcelable {
*
* @return delay in milliseconds
*/
- @DurationMillisLong
+ @IntRange(from = 0) @DurationMillisLong
public long getFadeInDelayForOffenders() {
return mFadeInDelayForOffendersMillis;
}
@@ -517,14 +518,14 @@ public final class FadeManagerConfiguration implements Parcelable {
/**
* Returns the default fade out duration (in milliseconds)
*/
- public static @DurationMillisLong long getDefaultFadeOutDurationMillis() {
+ public static @IntRange(from = 1) @DurationMillisLong long getDefaultFadeOutDurationMillis() {
return DEFAULT_FADE_OUT_DURATION_MS;
}
/**
* Returns the default fade in duration (in milliseconds)
*/
- public static @DurationMillisLong long getDefaultFadeInDurationMillis() {
+ public static @IntRange(from = 1) @DurationMillisLong long getDefaultFadeInDurationMillis() {
return DEFAULT_FADE_IN_DURATION_MS;
}
@@ -820,8 +821,8 @@ public final class FadeManagerConfiguration implements Parcelable {
* @param fadeOutDurationMillis duration in milliseconds used for fading out
* @param fadeInDurationMills duration in milliseconds used for fading in
*/
- public Builder(@DurationMillisLong long fadeOutDurationMillis,
- @DurationMillisLong long fadeInDurationMills) {
+ public Builder(@IntRange(from = 1) @DurationMillisLong long fadeOutDurationMillis,
+ @IntRange(from = 1) @DurationMillisLong long fadeInDurationMills) {
mFadeOutDurationMillis = fadeOutDurationMillis;
mFadeInDurationMillis = fadeInDurationMills;
}
@@ -939,7 +940,7 @@ public final class FadeManagerConfiguration implements Parcelable {
*/
@NonNull
public Builder setFadeOutDurationForUsage(@AudioAttributes.AttributeUsage int usage,
- @DurationMillisLong long fadeOutDurationMillis) {
+ @IntRange(from = 0) @DurationMillisLong long fadeOutDurationMillis) {
validateUsage(usage);
VolumeShaper.Configuration fadeOutVShaperConfig =
createVolShaperConfigForDuration(fadeOutDurationMillis, /* isFadeIn= */ false);
@@ -970,7 +971,7 @@ public final class FadeManagerConfiguration implements Parcelable {
*/
@NonNull
public Builder setFadeInDurationForUsage(@AudioAttributes.AttributeUsage int usage,
- @DurationMillisLong long fadeInDurationMillis) {
+ @IntRange(from = 0) @DurationMillisLong long fadeInDurationMillis) {
validateUsage(usage);
VolumeShaper.Configuration fadeInVShaperConfig =
createVolShaperConfigForDuration(fadeInDurationMillis, /* isFadeIn= */ true);
@@ -1055,7 +1056,7 @@ public final class FadeManagerConfiguration implements Parcelable {
@NonNull
public Builder setFadeOutDurationForAudioAttributes(
@NonNull AudioAttributes audioAttributes,
- @DurationMillisLong long fadeOutDurationMillis) {
+ @IntRange(from = 0) @DurationMillisLong long fadeOutDurationMillis) {
Objects.requireNonNull(audioAttributes, "Audio attribute cannot be null");
VolumeShaper.Configuration fadeOutVShaperConfig =
createVolShaperConfigForDuration(fadeOutDurationMillis, /* isFadeIn= */ false);
@@ -1087,7 +1088,7 @@ public final class FadeManagerConfiguration implements Parcelable {
*/
@NonNull
public Builder setFadeInDurationForAudioAttributes(@NonNull AudioAttributes audioAttributes,
- @DurationMillisLong long fadeInDurationMillis) {
+ @IntRange(from = 0) @DurationMillisLong long fadeInDurationMillis) {
Objects.requireNonNull(audioAttributes, "Audio attribute cannot be null");
VolumeShaper.Configuration fadeInVShaperConfig =
createVolShaperConfigForDuration(fadeInDurationMillis, /* isFadeIn= */ true);
@@ -1336,7 +1337,8 @@ public final class FadeManagerConfiguration implements Parcelable {
* @see #getFadeInDelayForOffenders()
*/
@NonNull
- public Builder setFadeInDelayForOffenders(@DurationMillisLong long delayMillis) {
+ public Builder setFadeInDelayForOffenders(
+ @IntRange(from = 0) @DurationMillisLong long delayMillis) {
Preconditions.checkArgument(delayMillis >= 0, "Delay cannot be negative");
mFadeInDelayForOffendersMillis = delayMillis;
return this;
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 4ee25c4bcf60..9548525d68d1 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -16,6 +16,7 @@
package android.media;
+import static android.media.codec.Flags.FLAG_NULL_OUTPUT_SURFACE;
import static android.media.codec.Flags.FLAG_REGION_OF_INTEREST;
import static com.android.media.codec.flags.Flags.FLAG_LARGE_AUDIO_FRAME;
@@ -2213,6 +2214,18 @@ final public class MediaCodec {
*/
public static final int CONFIGURE_FLAG_USE_CRYPTO_ASYNC = 4;
+ /**
+ * Configure the codec with a detached output surface.
+ * <p>
+ * This flag is only defined for a video decoder. MediaCodec
+ * configured with this flag will be in Surface mode even though
+ * the surface parameter is null.
+ *
+ * @see detachOutputSurface
+ */
+ @FlaggedApi(FLAG_NULL_OUTPUT_SURFACE)
+ public static final int CONFIGURE_FLAG_DETACHED_SURFACE = 8;
+
/** @hide */
@IntDef(
flag = true,
@@ -2395,6 +2408,31 @@ final public class MediaCodec {
private native void native_setSurface(@NonNull Surface surface);
/**
+ * Detach the current output surface of a codec.
+ * <p>
+ * Detaches the currently associated output Surface from the
+ * MediaCodec decoder. This allows the SurfaceView or other
+ * component holding the Surface to be safely destroyed or
+ * modified without affecting the decoder's operation. After
+ * calling this method (and after it returns), the decoder will
+ * enter detached-Surface mode and will no longer render
+ * output.
+ *
+ * @throws IllegalStateException if the codec was not
+ * configured in surface mode.
+ * @see CONFIGURE_FLAG_DETACHED_SURFACE
+ */
+ @FlaggedApi(FLAG_NULL_OUTPUT_SURFACE)
+ public void detachOutputSurface() {
+ if (!mHasSurface) {
+ throw new IllegalStateException("codec was not configured for an output surface");
+ }
+ // note: we still have a surface in detached mode, so keep mHasSurface
+ // we also technically allow calling detachOutputSurface multiple times in a row
+ // native_detachSurface();
+ }
+
+ /**
* Create a persistent input surface that can be used with codecs that normally have an input
* surface, such as video encoders. A persistent input can be reused by subsequent
* {@link MediaCodec} or {@link MediaRecorder} instances, but can only be used by at
@@ -3212,6 +3250,51 @@ final public class MediaCodec {
}
}
+ /**
+ * Similar to {@link #queueInputBuffers queueInputBuffers} but submits multiple access units
+ * in a buffer that is potentially encrypted.
+ * <strong>Check out further notes at {@link #queueInputBuffers queueInputBuffers}.</strong>
+ *
+ * @param index The index of a client-owned input buffer previously returned
+ * in a call to {@link #dequeueInputBuffer}.
+ * @param bufferInfos ArrayDeque of {@link MediaCodec.BufferInfo} that describes the
+ * contents in the buffer. The ArrayDeque and the BufferInfo objects provided
+ * can be recycled by the caller for re-use.
+ * @param cryptoInfos ArrayDeque of {@link MediaCodec.CryptoInfo} objects to facilitate the
+ * decryption of the contents. The ArrayDeque and the CryptoInfo objects
+ * provided can be reused immediately after the call returns. These objects
+ * should correspond to bufferInfo objects to ensure correct decryption.
+ * @throws IllegalStateException if not in the Executing state or not in asynchronous mode.
+ * @throws MediaCodec.CodecException upon codec error.
+ * @throws IllegalArgumentException upon if bufferInfos is empty, contains null, or if the
+ * access units are not contiguous.
+ * @throws CryptoException if an error occurs while attempting to decrypt the buffer.
+ * An error code associated with the exception helps identify the
+ * reason for the failure.
+ */
+ @FlaggedApi(FLAG_LARGE_AUDIO_FRAME)
+ public final void queueSecureInputBuffers(
+ int index,
+ @NonNull ArrayDeque<BufferInfo> bufferInfos,
+ @NonNull ArrayDeque<CryptoInfo> cryptoInfos) {
+ synchronized(mBufferLock) {
+ if (mBufferMode == BUFFER_MODE_BLOCK) {
+ throw new IncompatibleWithBlockModelException("queueSecureInputBuffers() "
+ + "is not compatible with CONFIGURE_FLAG_USE_BLOCK_MODEL. "
+ + "Please use getQueueRequest() to queue buffers");
+ }
+ invalidateByteBufferLocked(mCachedInputBuffers, index, true /* input */);
+ mDequeuedInputBuffers.remove(index);
+ }
+ try {
+ native_queueSecureInputBuffers(
+ index, bufferInfos.toArray(), cryptoInfos.toArray());
+ } catch (CryptoException | IllegalStateException | IllegalArgumentException e) {
+ revalidateByteBuffer(mCachedInputBuffers, index, true /* input */);
+ throw e;
+ }
+ }
+
private native final void native_queueSecureInputBuffer(
int index,
int offset,
@@ -3219,6 +3302,11 @@ final public class MediaCodec {
long presentationTimeUs,
int flags) throws CryptoException;
+ private native final void native_queueSecureInputBuffers(
+ int index,
+ @NonNull Object[] bufferInfos,
+ @NonNull Object[] cryptoInfos) throws CryptoException, CodecException;
+
/**
* Returns the index of an input buffer to be filled with valid data
* or -1 if no such buffer is currently available.
@@ -3464,7 +3552,37 @@ final public class MediaCodec {
mLinearBlock = block;
mOffset = offset;
mSize = size;
- mCryptoInfo = null;
+ mCryptoInfos.clear();
+ return this;
+ }
+
+ /**
+ * Set a linear block that contain multiple non-encrypted access unit to this
+ * queue request. Exactly one buffer must be set for a queue request before
+ * calling {@link #queue}. Multiple access units if present must be laid out contiguously
+ * and without gaps and in order. An IllegalArgumentException will be thrown
+ * during {@link #queue} if access units are not laid out contiguously.
+ *
+ * @param block The linear block object
+ * @param infos Represents {@link MediaCodec.BufferInfo} objects to mark
+ * individual access-unit boundaries and the timestamps associated with it.
+ * @return this object
+ * @throws IllegalStateException if a buffer is already set
+ */
+ @FlaggedApi(FLAG_LARGE_AUDIO_FRAME)
+ public @NonNull QueueRequest setMultiFrameLinearBlock(
+ @NonNull LinearBlock block,
+ @NonNull ArrayDeque<BufferInfo> infos) {
+ if (!isAccessible()) {
+ throw new IllegalStateException("The request is stale");
+ }
+ if (mLinearBlock != null || mHardwareBuffer != null) {
+ throw new IllegalStateException("Cannot set block twice");
+ }
+ mLinearBlock = block;
+ mBufferInfos.clear();
+ mBufferInfos.addAll(infos);
+ mCryptoInfos.clear();
return this;
}
@@ -3498,7 +3616,44 @@ final public class MediaCodec {
mLinearBlock = block;
mOffset = offset;
mSize = size;
- mCryptoInfo = cryptoInfo;
+ mCryptoInfos.clear();
+ mCryptoInfos.add(cryptoInfo);
+ return this;
+ }
+
+ /**
+ * Set an encrypted linear block to this queue request. Exactly one buffer must be
+ * set for a queue request before calling {@link #queue}. The block can contain multiple
+ * access units and if present should be laid out contiguously and without gaps.
+ *
+ * @param block The linear block object
+ * @param bufferInfos ArrayDeque of {@link MediaCodec.BufferInfo} that describes the
+ * contents in the buffer. The ArrayDeque and the BufferInfo objects
+ * provided can be recycled by the caller for re-use.
+ * @param cryptoInfos ArrayDeque of {@link MediaCodec.CryptoInfo} that describes the
+ * structure of the encrypted input samples. The ArrayDeque and the
+ * BufferInfo objects provided can be recycled by the caller for re-use.
+ * @return this object
+ * @throws IllegalStateException if a buffer is already set
+ * @throws IllegalArgumentException upon if bufferInfos is empty, contains null, or if the
+ * access units are not contiguous.
+ */
+ @FlaggedApi(FLAG_LARGE_AUDIO_FRAME)
+ public @NonNull QueueRequest setMultiFrameEncryptedLinearBlock(
+ @NonNull LinearBlock block,
+ @NonNull ArrayDeque<MediaCodec.BufferInfo> bufferInfos,
+ @NonNull ArrayDeque<MediaCodec.CryptoInfo> cryptoInfos) {
+ if (!isAccessible()) {
+ throw new IllegalStateException("The request is stale");
+ }
+ if (mLinearBlock != null || mHardwareBuffer != null) {
+ throw new IllegalStateException("Cannot set block twice");
+ }
+ mLinearBlock = block;
+ mBufferInfos.clear();
+ mBufferInfos.addAll(bufferInfos);
+ mCryptoInfos.clear();
+ mCryptoInfos.addAll(cryptoInfos);
return this;
}
@@ -3566,26 +3721,6 @@ final public class MediaCodec {
}
/**
- * Sets MediaCodec.BufferInfo objects describing the access units
- * contained in this queue request. Access units must be laid out
- * contiguously without gaps and in order.
- *
- * @param infos Represents {@link MediaCodec.BufferInfo} objects to mark
- * individual access-unit boundaries and the timestamps associated with it.
- * The buffer is expected to contain the data in a continuous manner.
- * @return this object
- */
- @FlaggedApi(FLAG_LARGE_AUDIO_FRAME)
- public @NonNull QueueRequest setBufferInfos(@NonNull ArrayDeque<BufferInfo> infos) {
- if (!isAccessible()) {
- throw new IllegalStateException("The request is stale");
- }
- mBufferInfos.clear();
- mBufferInfos.addAll(infos);
- return this;
- }
-
- /**
* Add an integer parameter.
* See {@link MediaFormat} for an exhaustive list of supported keys with
* values of type int, that can also be set with {@link MediaFormat#setInteger}.
@@ -3710,8 +3845,10 @@ final public class MediaCodec {
mBufferInfos.add(info);
}
if (mLinearBlock != null) {
+
mCodec.native_queueLinearBlock(
- mIndex, mLinearBlock, mCryptoInfo,
+ mIndex, mLinearBlock,
+ mCryptoInfos.isEmpty() ? null : mCryptoInfos.toArray(),
mBufferInfos.toArray(),
mTuningKeys, mTuningValues);
} else if (mHardwareBuffer != null) {
@@ -3726,11 +3863,11 @@ final public class MediaCodec {
mLinearBlock = null;
mOffset = 0;
mSize = 0;
- mCryptoInfo = null;
mHardwareBuffer = null;
mPresentationTimeUs = 0;
mFlags = 0;
mBufferInfos.clear();
+ mCryptoInfos.clear();
mTuningKeys.clear();
mTuningValues.clear();
return this;
@@ -3750,11 +3887,11 @@ final public class MediaCodec {
private LinearBlock mLinearBlock = null;
private int mOffset = 0;
private int mSize = 0;
- private MediaCodec.CryptoInfo mCryptoInfo = null;
private HardwareBuffer mHardwareBuffer = null;
private long mPresentationTimeUs = 0;
private @BufferFlag int mFlags = 0;
private final ArrayDeque<BufferInfo> mBufferInfos = new ArrayDeque<>();
+ private final ArrayDeque<CryptoInfo> mCryptoInfos = new ArrayDeque<>();
private final ArrayList<String> mTuningKeys = new ArrayList<>();
private final ArrayList<Object> mTuningValues = new ArrayList<>();
@@ -3764,7 +3901,7 @@ final public class MediaCodec {
private native void native_queueLinearBlock(
int index,
@NonNull LinearBlock block,
- @Nullable CryptoInfo cryptoInfo,
+ @Nullable Object[] cryptoInfos,
@NonNull Object[] bufferInfos,
@NonNull ArrayList<String> keys,
@NonNull ArrayList<Object> values);
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 86f89ab89f63..1e7bc4764dd7 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -20,9 +20,12 @@ import static android.media.Utils.intersectSortedDistinctRanges;
import static android.media.Utils.sortDistinctRanges;
import static android.media.codec.Flags.FLAG_DYNAMIC_COLOR_ASPECTS;
import static android.media.codec.Flags.FLAG_HLG_EDITING;
+import static android.media.codec.Flags.FLAG_IN_PROCESS_SW_AUDIO_CODEC;
+import static android.media.codec.Flags.FLAG_NULL_OUTPUT_SURFACE;
import static android.media.codec.Flags.FLAG_REGION_OF_INTEREST;
import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -39,6 +42,8 @@ import android.util.Range;
import android.util.Rational;
import android.util.Size;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -778,6 +783,17 @@ public final class MediaCodecInfo {
public static final String FEATURE_Roi = "region-of-interest";
/**
+ * <b>video decoder only</b>: codec supports detaching the
+ * output surface when in Surface mode.
+ * <p> If true, the codec can be configured in Surface mode
+ * without an actual surface (in detached surface mode).
+ * @see MediaCodec#CONFIGURE_FLAG_DETACHED_SURFACE
+ */
+ @SuppressLint("AllUpper")
+ @FlaggedApi(FLAG_NULL_OUTPUT_SURFACE)
+ public static final String FEATURE_DetachedSurface = "detached-surface";
+
+ /**
* Query codec feature capabilities.
* <p>
* These features are supported to be used by the codec. These
@@ -814,6 +830,9 @@ public final class MediaCodecInfo {
if (android.media.codec.Flags.dynamicColorAspects()) {
features.add(new Feature(FEATURE_DynamicColorAspects, (1 << 8), true));
}
+ if (android.media.codec.Flags.nullOutputSurface()) {
+ features.add(new Feature(FEATURE_DetachedSurface, (1 << 9), true));
+ }
// feature to exclude codec from REGULAR codec list
features.add(new Feature(FEATURE_SpecialCodec, (1 << 30), false, true));
@@ -1793,6 +1812,55 @@ public final class MediaCodecInfo {
}
}
+ /** @hide */
+ @IntDef(prefix = {"SECURITY_MODEL_"}, value = {
+ SECURITY_MODEL_SANDBOXED,
+ SECURITY_MODEL_MEMORY_SAFE,
+ SECURITY_MODEL_TRUSTED_CONTENT_ONLY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SecurityModel {}
+
+ /**
+ * In this model the codec is running in a sandboxed process. Even if a
+ * malicious content was fed to the codecs in this model, the impact will
+ * be contained in the sandboxed process.
+ */
+ @FlaggedApi(FLAG_IN_PROCESS_SW_AUDIO_CODEC)
+ public static final int SECURITY_MODEL_SANDBOXED = 0;
+ /**
+ * In this model the codec is not running in a sandboxed process, but
+ * written in a memory-safe way. It typically means that the software
+ * implementation of the codec is written in a memory-safe language such
+ * as Rust.
+ */
+ @FlaggedApi(FLAG_IN_PROCESS_SW_AUDIO_CODEC)
+ public static final int SECURITY_MODEL_MEMORY_SAFE = 1;
+ /**
+ * In this model the codec is suitable only for trusted content where
+ * the input can be verified to be well-formed and no malicious actor
+ * can alter it. For example, codecs in this model are not suitable
+ * for arbitrary media downloaded from the internet or present in a user
+ * directory. On the other hand, they could be suitable for media encoded
+ * in the backend that the app developer wholly controls.
+ * <p>
+ * Codecs with this security model is not included in
+ * {@link MediaCodecList#REGULAR_CODECS}, but included in
+ * {@link MediaCodecList#ALL_CODECS}.
+ */
+ @FlaggedApi(FLAG_IN_PROCESS_SW_AUDIO_CODEC)
+ public static final int SECURITY_MODEL_TRUSTED_CONTENT_ONLY = 2;
+
+ /**
+ * Query the security model of the codec.
+ */
+ @FlaggedApi(FLAG_IN_PROCESS_SW_AUDIO_CODEC)
+ @SecurityModel
+ public int getSecurityModel() {
+ // TODO b/297922713 --- detect security model of out-of-sandbox codecs
+ return SECURITY_MODEL_SANDBOXED;
+ }
+
/**
* A class that supports querying the video capabilities of a codec.
*/
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 5e40eee26886..7b83842a9fb2 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -16,6 +16,8 @@
package android.media;
+import static android.media.codec.Flags.FLAG_IN_PROCESS_SW_AUDIO_CODEC;
+
import static com.android.media.codec.flags.Flags.FLAG_CODEC_IMPORTANCE;
import static com.android.media.codec.flags.Flags.FLAG_LARGE_AUDIO_FRAME;
@@ -1715,6 +1717,58 @@ public final class MediaFormat {
@FlaggedApi(FLAG_CODEC_IMPORTANCE)
public static final String KEY_IMPORTANCE = "importance";
+ /** @hide */
+ @IntDef(flag = true, prefix = {"FLAG_SECURITY_MODEL_"}, value = {
+ FLAG_SECURITY_MODEL_SANDBOXED,
+ FLAG_SECURITY_MODEL_MEMORY_SAFE,
+ FLAG_SECURITY_MODEL_TRUSTED_CONTENT_ONLY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SecurityModelFlag {}
+
+ /**
+ * Flag for {@link MediaCodecInfo#SECURITY_MODEL_SANDBOXED}.
+ */
+ @FlaggedApi(FLAG_IN_PROCESS_SW_AUDIO_CODEC)
+ public static final int FLAG_SECURITY_MODEL_SANDBOXED =
+ (1 << MediaCodecInfo.SECURITY_MODEL_SANDBOXED);
+ /**
+ * Flag for {@link MediaCodecInfo#SECURITY_MODEL_MEMORY_SAFE}.
+ */
+ @FlaggedApi(FLAG_IN_PROCESS_SW_AUDIO_CODEC)
+ public static final int FLAG_SECURITY_MODEL_MEMORY_SAFE =
+ (1 << MediaCodecInfo.SECURITY_MODEL_MEMORY_SAFE);
+ /**
+ * Flag for {@link MediaCodecInfo#SECURITY_MODEL_TRUSTED_CONTENT_ONLY}.
+ */
+ @FlaggedApi(FLAG_IN_PROCESS_SW_AUDIO_CODEC)
+ public static final int FLAG_SECURITY_MODEL_TRUSTED_CONTENT_ONLY =
+ (1 << MediaCodecInfo.SECURITY_MODEL_TRUSTED_CONTENT_ONLY);
+
+ /**
+ * A key describing the requested security model as flags.
+ * <p>
+ * The associated value is a flag of the following values:
+ * {@link FLAG_SECURITY_MODEL_SANDBOXED},
+ * {@link FLAG_SECURITY_MODEL_MEMORY_SAFE},
+ * {@link FLAG_SECURITY_MODEL_TRUSTED_CONTENT_ONLY}. The default value is
+ * {@link FLAG_SECURITY_MODEL_SANDBOXED}.
+ * <p>
+ * When passed to {@link MediaCodecList#findDecoderForFormat} or
+ * {@link MediaCodecList#findEncoderForFormat}, MediaCodecList filters
+ * the security model of the codecs according to this flag value.
+ * <p>
+ * When passed to {@link MediaCodec#configure}, MediaCodec verifies
+ * the security model matches the flag value passed, and throws
+ * {@link java.lang.IllegalArgumentException} if the model does not match.
+ * <p>
+ * @see MediaCodecInfo#getSecurityModel
+ * @see MediaCodecList#findDecoderForFormat
+ * @see MediaCodecList#findEncoderForFormat
+ */
+ @FlaggedApi(FLAG_IN_PROCESS_SW_AUDIO_CODEC)
+ public static final String KEY_SECURITY_MODEL = "security-model";
+
/* package private */ MediaFormat(@NonNull Map<String, Object> map) {
mMap = map;
}
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 144b01a438a2..4d5a33d99b64 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -20,6 +20,7 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainMessa
import static com.android.media.flags.Flags.FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES;
import static com.android.media.flags.Flags.FLAG_ENABLE_CROSS_USER_ROUTING_IN_MEDIA_ROUTER2;
import static com.android.media.flags.Flags.FLAG_ENABLE_GET_TRANSFERABLE_ROUTES;
+import static com.android.media.flags.Flags.FLAG_ENABLE_PRIVILEGED_ROUTING_FOR_MEDIA_ROUTING_CONTROL;
import static com.android.media.flags.Flags.FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2;
import static com.android.media.flags.Flags.FLAG_ENABLE_SCREEN_OFF_SCANNING;
@@ -989,17 +990,24 @@ public final class MediaRouter2 {
}
/**
- * Requests a volume change for the route asynchronously.
- * It may have no effect if the route is currently not selected.
+ * Sets the volume for a specific route.
*
- * <p>This will be no-op for non-system media routers.
+ * <p>The call may have no effect if the route is currently not selected.
+ *
+ * <p>This method is only supported by {@link #getInstance(Context, String) proxy MediaRouter2
+ * instances}. Use {@link RoutingController#setVolume(int) RoutingController#setVolume(int)}
+ * instead for {@link #getInstance(Context) local MediaRouter2 instances}.</p>
*
* @param volume The new volume value between 0 and {@link MediaRoute2Info#getVolumeMax}.
- * @see #getInstance(Context, String)
- * @hide
+ * @throws UnsupportedOperationException If called on a {@link #getInstance(Context) local
+ * router instance}.
*/
- @SystemApi
- @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
+ @FlaggedApi(FLAG_ENABLE_PRIVILEGED_ROUTING_FOR_MEDIA_ROUTING_CONTROL)
+ @RequiresPermission(
+ anyOf = {
+ Manifest.permission.MEDIA_CONTENT_CONTROL,
+ Manifest.permission.MEDIA_ROUTING_CONTROL
+ })
public void setRouteVolume(@NonNull MediaRoute2Info route, int volume) {
Objects.requireNonNull(route, "route must not be null");
@@ -3464,10 +3472,11 @@ public final class MediaRouter2 {
return result;
}
- /** No-op. Local routers cannot modify the volume of specific routes. */
+ /** Local routers cannot modify the volume of specific routes. */
@Override
public void setRouteVolume(MediaRoute2Info route, int volume) {
- // Do nothing.
+ throw new UnsupportedOperationException(
+ "setRouteVolume is only supported by proxy routers. See javadoc.");
// If this API needs to be public, use IMediaRouterService#setRouteVolumeWithRouter2()
}
diff --git a/media/java/android/media/audiofx/Visualizer.java b/media/java/android/media/audiofx/Visualizer.java
index 2795cfe4ba61..f05ea9c2684f 100644
--- a/media/java/android/media/audiofx/Visualizer.java
+++ b/media/java/android/media/audiofx/Visualizer.java
@@ -336,8 +336,9 @@ public class Visualizer {
* This method must not be called when the Visualizer is enabled.
* @param size requested capture size
* @return {@link #SUCCESS} in case of success,
- * {@link #ERROR_BAD_VALUE} in case of failure.
- * @throws IllegalStateException
+ * {@link #ERROR_INVALID_OPERATION} if Visualizer effect enginer not enabled.
+ * @throws IllegalStateException if the effect is not in proper state.
+ * @throws IllegalArgumentException if the size parameter is invalid (out of supported range).
*/
public int setCaptureSize(int size)
throws IllegalStateException {
@@ -345,7 +346,13 @@ public class Visualizer {
if (mState != STATE_INITIALIZED) {
throw(new IllegalStateException("setCaptureSize() called in wrong state: "+mState));
}
- return native_setCaptureSize(size);
+
+ int ret = native_setCaptureSize(size);
+ if (ret == ERROR_BAD_VALUE) {
+ throw(new IllegalArgumentException("setCaptureSize to " + size + " failed"));
+ }
+
+ return ret;
}
}
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index b662901176e6..022278298875 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -697,6 +697,19 @@ public final class MediaBrowser {
});
}
+ private void onDisconnectRequested(ServiceCallbacks callback) {
+ mHandler.post(
+ () -> {
+ Log.i(TAG, "onDisconnectRequest for " + mServiceComponent);
+
+ if (!isCurrent(callback, "onDisconnectRequest")) {
+ return;
+ }
+ forceCloseConnection();
+ mCallback.onDisconnected();
+ });
+ }
+
/**
* Return true if {@code callback} is the current ServiceCallbacks. Also logs if it's not.
*/
@@ -880,6 +893,19 @@ public final class MediaBrowser {
*/
public void onConnectionFailed() {
}
+
+ /**
+ * Invoked after disconnecting by request of the {@link MediaBrowserService}.
+ *
+ * <p>The default implementation of this method calls {@link #onConnectionFailed()}.
+ *
+ * @hide
+ */
+ // TODO: b/185136506 - Consider publishing this API in the next window for API changes, if
+ // the need arises.
+ public void onDisconnected() {
+ onConnectionFailed();
+ }
}
/**
@@ -1112,6 +1138,14 @@ public final class MediaBrowser {
mediaBrowser.onLoadChildren(this, parentId, list, options);
}
}
+
+ @Override
+ public void onDisconnect() {
+ MediaBrowser mediaBrowser = mMediaBrowser.get();
+ if (mediaBrowser != null) {
+ mediaBrowser.onDisconnectRequested(this);
+ }
+ }
}
private static class Subscription {
diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig
index 8dba04066ad9..6cf9c6fa7616 100644
--- a/media/java/android/media/flags/media_better_together.aconfig
+++ b/media/java/android/media/flags/media_better_together.aconfig
@@ -104,3 +104,10 @@ flag {
description: "Enable new MediaRouter2 API to enable watch companion apps to scan while the phone screen is off."
bug: "281072508"
}
+
+flag {
+ name: "enable_null_session_in_media_browser_service"
+ namespace: "media_solutions"
+ description: "Enables apps owning a MediaBrowserService to disconnect all connected browsers."
+ bug: "263520343"
+}
diff --git a/media/java/android/media/flags/projection.aconfig b/media/java/android/media/flags/projection.aconfig
index c4b38c725ea4..b16580927fa6 100644
--- a/media/java/android/media/flags/projection.aconfig
+++ b/media/java/android/media/flags/projection.aconfig
@@ -1,4 +1,4 @@
-package: "com.android.media.flags"
+package: "com.android.media.projection.flags"
# Project link: https://gantry.corp.google.com/projects/android_platform_window_surfaces/changes
diff --git a/media/java/android/media/metrics/EditingEndedEvent.java b/media/java/android/media/metrics/EditingEndedEvent.java
index 9b3477f4bd69..54496bff077f 100644
--- a/media/java/android/media/metrics/EditingEndedEvent.java
+++ b/media/java/android/media/metrics/EditingEndedEvent.java
@@ -199,7 +199,7 @@ public final class EditingEndedEvent extends Event implements Parcelable {
/** Input audio was edited. */
public static final long OPERATION_TYPE_AUDIO_EDIT = 1L << 3;
- /** Input video samples were writted (muxed) directly to the output file without transcoding. */
+ /** Input video samples were written (muxed) directly to the output file without transcoding. */
public static final long OPERATION_TYPE_VIDEO_TRANSMUX = 1L << 4;
/** Input audio samples were written (muxed) directly to the output file without transcoding. */
@@ -272,7 +272,8 @@ public final class EditingEndedEvent extends Event implements Parcelable {
}
/**
- * Returns the name of the library implementing the exporting operation, or {@code null} if
+ * Returns the name of the library implementing the exporting operation, for example, a Maven
+ * artifact ID like "androidx.media3.media3-transformer:1.3.0-beta01", or {@code null} if
* unknown.
*/
@Nullable
@@ -281,8 +282,8 @@ public final class EditingEndedEvent extends Event implements Parcelable {
}
/**
- * Returns the name of the library implementing the media muxing operation, or {@code null} if
- * unknown.
+ * Returns the name of the library implementing the media muxing operation, for example, a Maven
+ * artifact ID like "androidx.media3.media3-muxer:1.3.0-beta01", or {@code null} if unknown.
*/
@Nullable
public String getMuxerName() {
diff --git a/media/java/android/media/metrics/MediaItemInfo.java b/media/java/android/media/metrics/MediaItemInfo.java
index 63dd3ccd3b33..338697fef76b 100644
--- a/media/java/android/media/metrics/MediaItemInfo.java
+++ b/media/java/android/media/metrics/MediaItemInfo.java
@@ -92,7 +92,7 @@ public final class MediaItemInfo implements Parcelable {
DATA_TYPE_DEPTH,
DATA_TYPE_GAIN_MAP,
DATA_TYPE_HIGH_FRAME_RATE,
- DATA_TYPE_CUE_POINTS,
+ DATA_TYPE_SPEED_SETTING_CUE_POINTS,
DATA_TYPE_GAPLESS,
DATA_TYPE_SPATIAL_AUDIO,
DATA_TYPE_HIGH_DYNAMIC_RANGE_VIDEO,
@@ -109,7 +109,10 @@ public final class MediaItemInfo implements Parcelable {
/** The media item includes audio data. */
public static final long DATA_TYPE_AUDIO = 1L << 2;
- /** The media item includes metadata. */
+ /**
+ * The media item includes static media container metadata (for example, capture frame rate or
+ * location information).
+ */
public static final long DATA_TYPE_METADATA = 1L << 3;
/** The media item includes depth (z-distance) information. */
@@ -121,8 +124,11 @@ public final class MediaItemInfo implements Parcelable {
/** The media item includes high frame rate video data. */
public static final long DATA_TYPE_HIGH_FRAME_RATE = 1L << 6;
- /** The media item includes time-dependent speed setting metadata. */
- public static final long DATA_TYPE_CUE_POINTS = 1L << 7;
+ /**
+ * The media item includes time-dependent speed information (for example, slow motion cue
+ * points).
+ */
+ public static final long DATA_TYPE_SPEED_SETTING_CUE_POINTS = 1L << 7;
/** The media item includes gapless audio metadata. */
public static final long DATA_TYPE_GAPLESS = 1L << 8;
diff --git a/media/java/android/media/midi/package.html b/media/java/android/media/midi/package.html
index ae0c2ab0e811..45b4370d6cc9 100644
--- a/media/java/android/media/midi/package.html
+++ b/media/java/android/media/midi/package.html
@@ -478,7 +478,7 @@ MIDI 1.0 virtual devices, android.media.midi.MidiUmpDeviceService is used</p>
&lt;intent-filter>
&lt;action android:name="android.media.midi.MidiUmpDeviceService" />
&lt;/intent-filter>
- &lt;meta-data android:name="android.media.midi.MidiUmpDeviceService"
+ &lt;property android:name="android.media.midi.MidiUmpDeviceService"
android:resource="@xml/<strong>echo_device_info</strong>" />
&lt;/service>
</pre>
diff --git a/media/java/android/media/tv/SignalingDataResponse.java b/media/java/android/media/tv/SignalingDataResponse.java
index be172ec62773..51fa6a23bdf6 100644
--- a/media/java/android/media/tv/SignalingDataResponse.java
+++ b/media/java/android/media/tv/SignalingDataResponse.java
@@ -73,6 +73,10 @@ public final class SignalingDataResponse extends BroadcastInfoResponse implement
/**
* Gets a list of types of metadata that are contained in this response.
*
+ * <p> This list correlates to all the available types that can be found within
+ * {@link #getSignalingDataInfoList()}. This list is determined by the types specified in
+ * {@link SignalingDataRequest#getSignalingDataTypes()}.
+ *
* <p> A list of types available are defined in {@link SignalingDataRequest}.
* For more information about these types, see A/344:2023-5 9.2.10 - Query Signaling Data API.
*
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
index f332f8102013..84d08db1b9c8 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppService.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
@@ -964,7 +964,11 @@ public abstract class TvInteractiveAppService extends Service {
/**
* Called when the TV App sends the selected track info as a response to
- * {@link #requestSelectedTrackInfo()}
+ * {@link #requestSelectedTrackInfo()}.
+ *
+ * <p> When a selected track changes as a result of a new selection,
+ * {@link #onTrackSelected(int, String)} should be used instead to communicate the specific
+ * track selection.
*
* @param tracks A list of {@link TvTrackInfo} that are currently selected
*/
@@ -1383,6 +1387,8 @@ public abstract class TvInteractiveAppService extends Service {
* <p> Normally, track info cannot be synchronized until the channel has
* been changed. This is used when the session of the {@link TvInteractiveAppService}
* is newly created and the normal synchronization has not happened yet.
+ *
+ * <p> The track info will be returned in {@link #onSelectedTrackInfo(List)}
*/
@FlaggedApi(Flags.FLAG_TIAF_V_APIS)
@CallSuper
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppView.java b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
index 29a3b98073fe..635572d12cc5 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppView.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
@@ -585,7 +585,8 @@ public class TvInteractiveAppView extends ViewGroup {
}
/**
- * Sends the currently selected track info to the TV Interactive App.
+ * Sends the currently selected track info to the TV Interactive App in response to a
+ * {@link TvInteractiveAppCallback#onRequestSelectedTrackInfo(String)} request.
*
* @param tracks list of {@link TvTrackInfo} of the currently selected track(s)
*/
diff --git a/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl b/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
index a8772076af97..fbb7cfd5ded1 100644
--- a/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
+++ b/media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl
@@ -24,4 +24,11 @@ oneway interface IMediaBrowserServiceCallbacks {
@UnsupportedAppUsage
void onConnectFailed();
void onLoadChildren(String mediaId, in ParceledListSlice list, in Bundle options);
+ /**
+ * Invoked when the browser service cuts off the connection with the browser.
+ *
+ * <p>The browser must also clean up any state associated with this connection, as if the
+ * service had been destroyed.
+ */
+ void onDisconnect();
}
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index fa9afa872091..39ef528fb69b 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -38,10 +38,13 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
+import com.android.media.flags.Flags;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -51,6 +54,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
/**
* Base class for media browser services.
@@ -96,6 +100,7 @@ public abstract class MediaBrowserService extends Service {
private static final int RESULT_ERROR = -1;
private static final int RESULT_OK = 0;
+ private final ServiceBinder mBinder;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -105,13 +110,17 @@ public abstract class MediaBrowserService extends Service {
private final Handler mHandler = new Handler();
- private final ServiceState mServiceState = new ServiceState();
+ private final AtomicReference<ServiceState> mServiceState;
+
+ // Holds the connection record associated with the currently executing callback operation, if
+ // any. See getCurrentBrowserInfo for an example. Must only be accessed on mHandler.
+ @Nullable private ConnectionRecord mCurrentConnectionOnHandler;
/**
* All the info about a connection.
*/
private static class ConnectionRecord implements IBinder.DeathRecipient {
- public final MediaBrowserService service;
+ public final ServiceState serviceState;
public final String pkg;
public final int pid;
public final int uid;
@@ -121,9 +130,14 @@ public abstract class MediaBrowserService extends Service {
public final HashMap<String, List<Pair<IBinder, Bundle>>> subscriptions = new HashMap<>();
ConnectionRecord(
- MediaBrowserService service, String pkg, int pid, int uid, Bundle rootHints,
- IMediaBrowserServiceCallbacks callbacks, BrowserRoot root) {
- this.service = service;
+ ServiceState serviceState,
+ String pkg,
+ int pid,
+ int uid,
+ Bundle rootHints,
+ IMediaBrowserServiceCallbacks callbacks,
+ BrowserRoot root) {
+ this.serviceState = serviceState;
this.pkg = pkg;
this.pid = pid;
this.uid = uid;
@@ -134,12 +148,8 @@ public abstract class MediaBrowserService extends Service {
@Override
public void binderDied() {
- service.mHandler.post(new Runnable() {
- @Override
- public void run() {
- service.mServiceState.mConnections.remove(callbacks.asBinder());
- }
- });
+ serviceState.postOnHandler(
+ () -> serviceState.mConnections.remove(callbacks.asBinder()));
}
}
@@ -211,95 +221,45 @@ public abstract class MediaBrowserService extends Service {
}
private static class ServiceBinder extends IMediaBrowserService.Stub {
- private WeakReference<MediaBrowserService> mService;
+ private final AtomicReference<WeakReference<ServiceState>> mServiceState;
- private ServiceBinder(MediaBrowserService service) {
- mService = new WeakReference(service);
+ private ServiceBinder(ServiceState serviceState) {
+ mServiceState = new AtomicReference<>();
+ setServiceState(serviceState);
+ }
+
+ public void setServiceState(ServiceState serviceState) {
+ mServiceState.set(new WeakReference<>(serviceState));
}
@Override
public void connect(final String pkg, final Bundle rootHints,
final IMediaBrowserServiceCallbacks callbacks) {
- MediaBrowserService service = mService.get();
- if (service == null) {
+ ServiceState serviceState = mServiceState.get().get();
+ if (serviceState == null) {
return;
}
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
- if (!service.isValidPackage(pkg, uid)) {
+ if (!serviceState.isValidPackage(pkg, uid)) {
throw new IllegalArgumentException("Package/uid mismatch: uid=" + uid
+ " package=" + pkg);
}
- service.mHandler.post(new Runnable() {
- @Override
- public void run() {
- final IBinder b = callbacks.asBinder();
- // Clear out the old subscriptions. We are getting new ones.
- service.mServiceState.mConnections.remove(b);
-
- // Temporarily sets a placeholder ConnectionRecord to make
- // getCurrentBrowserInfo() work in onGetRoot().
- service.mServiceState.mCurConnection =
- new ConnectionRecord(
- service, pkg, pid, uid, rootHints, callbacks, null);
- BrowserRoot root = service.onGetRoot(pkg, uid, rootHints);
- service.mServiceState.mCurConnection = null;
-
- // If they didn't return something, don't allow this client.
- if (root == null) {
- Log.i(TAG, "No root for client " + pkg + " from service "
- + getClass().getName());
- try {
- callbacks.onConnectFailed();
- } catch (RemoteException ex) {
- Log.w(TAG, "Calling onConnectFailed() failed. Ignoring. "
- + "pkg=" + pkg);
- }
- } else {
- try {
- ConnectionRecord connection =
- new ConnectionRecord(
- service, pkg, pid, uid, rootHints, callbacks, root);
- service.mServiceState.mConnections.put(b, connection);
- b.linkToDeath(connection, 0);
- if (service.mServiceState.mSession != null) {
- callbacks.onConnect(
- connection.root.getRootId(),
- service.mServiceState.mSession,
- connection.root.getExtras());
- }
- } catch (RemoteException ex) {
- Log.w(TAG, "Calling onConnect() failed. Dropping client. "
- + "pkg=" + pkg);
- service.mServiceState.mConnections.remove(b);
- }
- }
- }
- });
+ serviceState.postOnHandler(
+ () -> serviceState.connectOnHandler(pkg, pid, uid, rootHints, callbacks));
}
@Override
public void disconnect(final IMediaBrowserServiceCallbacks callbacks) {
- MediaBrowserService service = mService.get();
- if (service == null) {
+ ServiceState serviceState = mServiceState.get().get();
+ if (serviceState == null) {
return;
}
- service.mHandler.post(new Runnable() {
- @Override
- public void run() {
- final IBinder b = callbacks.asBinder();
-
- // Clear out the old subscriptions. We are getting new ones.
- final ConnectionRecord old = service.mServiceState.mConnections.remove(b);
- if (old != null) {
- // TODO
- old.callbacks.asBinder().unlinkToDeath(old, 0);
- }
- }
- });
+ serviceState.postOnHandler(
+ () -> serviceState.removeConnectionRecordOnHandler(callbacks));
}
@Override
@@ -310,27 +270,13 @@ public abstract class MediaBrowserService extends Service {
@Override
public void addSubscription(final String id, final IBinder token, final Bundle options,
final IMediaBrowserServiceCallbacks callbacks) {
- MediaBrowserService service = mService.get();
- if (service == null) {
+ ServiceState serviceState = mServiceState.get().get();
+ if (serviceState == null) {
return;
}
- service.mHandler.post(new Runnable() {
- @Override
- public void run() {
- final IBinder b = callbacks.asBinder();
-
- // Get the record for the connection
- ConnectionRecord connection = service.mServiceState.mConnections.get(b);
- if (connection == null) {
- Log.w(TAG, "addSubscription for callback that isn't registered id="
- + id);
- return;
- }
-
- service.addSubscription(id, connection, token, options);
- }
- });
+ serviceState.postOnHandler(
+ () -> serviceState.addSubscriptionOnHandler(id, callbacks, token, options));
}
@Override
@@ -342,64 +288,49 @@ public abstract class MediaBrowserService extends Service {
@Override
public void removeSubscription(final String id, final IBinder token,
final IMediaBrowserServiceCallbacks callbacks) {
- MediaBrowserService service = mService.get();
- if (service == null) {
+ ServiceState serviceState = mServiceState.get().get();
+ if (serviceState == null) {
return;
}
- service.mHandler.post(new Runnable() {
- @Override
- public void run() {
- final IBinder b = callbacks.asBinder();
-
- ConnectionRecord connection = service.mServiceState.mConnections.get(b);
- if (connection == null) {
- Log.w(TAG, "removeSubscription for callback that isn't registered id="
- + id);
- return;
- }
- if (!service.removeSubscription(id, connection, token)) {
- Log.w(TAG, "removeSubscription called for " + id
- + " which is not subscribed");
- }
- }
- });
+ serviceState.postOnHandler(
+ () -> {
+ if (!serviceState.removeSubscriptionOnHandler(id, callbacks, token)) {
+ Log.w(TAG, "removeSubscription for id with no subscription: " + id);
+ }
+ });
}
@Override
public void getMediaItem(final String mediaId, final ResultReceiver receiver,
final IMediaBrowserServiceCallbacks callbacks) {
- MediaBrowserService service = mService.get();
- if (service == null) {
+ ServiceState serviceState = mServiceState.get().get();
+ if (serviceState == null) {
return;
}
- service.mHandler.post(new Runnable() {
- @Override
- public void run() {
- final IBinder b = callbacks.asBinder();
- ConnectionRecord connection = service.mServiceState.mConnections.get(b);
- if (connection == null) {
- Log.w(TAG, "getMediaItem for callback that isn't registered id=" + mediaId);
- return;
- }
- service.performLoadItem(mediaId, connection, receiver);
- }
- });
+ serviceState.postOnHandler(
+ () -> serviceState.performLoadItemOnHandler(mediaId, callbacks, receiver));
}
}
+ /** Default constructor. */
+ public MediaBrowserService() {
+ mServiceState = new AtomicReference<>(new ServiceState());
+ mBinder = new ServiceBinder(mServiceState.get());
+ }
+
@Override
public void onCreate() {
super.onCreate();
- mServiceState.mBinder = new ServiceBinder(this);
}
@Override
public IBinder onBind(Intent intent) {
if (SERVICE_INTERFACE.equals(intent.getAction())) {
- return mServiceState.mBinder;
+ return mBinder;
}
+
return null;
}
@@ -513,36 +444,33 @@ public abstract class MediaBrowserService extends Service {
/**
* Call to set the media session.
- * <p>
- * This should be called as soon as possible during the service's startup.
- * It may only be called once.
+ *
+ * <p>This should be called as soon as possible during the service's startup. It may only be
+ * called once.
*
* @param token The token for the service's {@link MediaSession}.
*/
+ // TODO: b/185136506 - Update the javadoc to reflect API changes when
+ // enableNullSessionInMediaBrowserService makes it to nextfood.
public void setSessionToken(final MediaSession.Token token) {
+ ServiceState serviceState = mServiceState.get();
if (token == null) {
- throw new IllegalArgumentException("Session token may not be null.");
- }
- if (mServiceState.mSession != null) {
+ if (!Flags.enableNullSessionInMediaBrowserService()) {
+ throw new IllegalArgumentException("Session token may not be null.");
+ } else if (serviceState.mSession != null) {
+ ServiceState newServiceState = new ServiceState();
+ mBinder.setServiceState(newServiceState);
+ mServiceState.set(newServiceState);
+ serviceState.release();
+ } else {
+ // Nothing to do. The session is already null.
+ }
+ } else if (serviceState.mSession != null) {
throw new IllegalStateException("The session token has already been set.");
+ } else {
+ serviceState.mSession = token;
+ mHandler.post(() -> serviceState.notifySessionTokenInitializedOnHandler(token));
}
- mServiceState.mSession = token;
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- Iterator<ConnectionRecord> iter = mServiceState.mConnections.values().iterator();
- while (iter.hasNext()) {
- ConnectionRecord connection = iter.next();
- try {
- connection.callbacks.onConnect(connection.root.getRootId(), token,
- connection.root.getExtras());
- } catch (RemoteException e) {
- Log.w(TAG, "Connection for " + connection.pkg + " is no longer valid.");
- iter.remove();
- }
- }
- }
- });
}
/**
@@ -550,7 +478,7 @@ public abstract class MediaBrowserService extends Service {
* or if it has been destroyed.
*/
public @Nullable MediaSession.Token getSessionToken() {
- return mServiceState.mSession;
+ return mServiceState.get().mSession;
}
/**
@@ -566,12 +494,12 @@ public abstract class MediaBrowserService extends Service {
* @see MediaBrowserService.BrowserRoot#EXTRA_SUGGESTED
*/
public final Bundle getBrowserRootHints() {
- ConnectionRecord curConnection = mServiceState.mCurConnection;
- if (curConnection == null) {
+ ConnectionRecord currentConnection = mCurrentConnectionOnHandler;
+ if (currentConnection == null) {
throw new IllegalStateException("This should be called inside of onGetRoot or"
+ " onLoadChildren or onLoadItem methods");
}
- return curConnection.rootHints == null ? null : new Bundle(curConnection.rootHints);
+ return currentConnection.rootHints == null ? null : new Bundle(currentConnection.rootHints);
}
/**
@@ -582,12 +510,13 @@ public abstract class MediaBrowserService extends Service {
* @see MediaSessionManager#isTrustedForMediaControl(RemoteUserInfo)
*/
public final RemoteUserInfo getCurrentBrowserInfo() {
- ConnectionRecord curConnection = mServiceState.mCurConnection;
- if (curConnection == null) {
+ ConnectionRecord currentConnection = mCurrentConnectionOnHandler;
+ if (currentConnection == null) {
throw new IllegalStateException("This should be called inside of onGetRoot or"
+ " onLoadChildren or onLoadItem methods");
}
- return new RemoteUserInfo(curConnection.pkg, curConnection.pid, curConnection.uid);
+ return new RemoteUserInfo(
+ currentConnection.pkg, currentConnection.pid, currentConnection.uid);
}
/**
@@ -599,7 +528,7 @@ public abstract class MediaBrowserService extends Service {
* children changed.
*/
public void notifyChildrenChanged(@NonNull String parentId) {
- notifyChildrenChangedInternal(parentId, null);
+ notifyChildrenChanged(parentId, Bundle.EMPTY);
}
/**
@@ -617,182 +546,10 @@ public abstract class MediaBrowserService extends Service {
if (options == null) {
throw new IllegalArgumentException("options cannot be null in notifyChildrenChanged");
}
- notifyChildrenChangedInternal(parentId, options);
- }
-
- private void notifyChildrenChangedInternal(final String parentId, final Bundle options) {
if (parentId == null) {
throw new IllegalArgumentException("parentId cannot be null in notifyChildrenChanged");
}
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- for (IBinder binder : mServiceState.mConnections.keySet()) {
- ConnectionRecord connection = mServiceState.mConnections.get(binder);
- List<Pair<IBinder, Bundle>> callbackList =
- connection.subscriptions.get(parentId);
- if (callbackList != null) {
- for (Pair<IBinder, Bundle> callback : callbackList) {
- if (MediaBrowserUtils.hasDuplicatedItems(options, callback.second)) {
- performLoadChildren(parentId, connection, callback.second);
- }
- }
- }
- }
- }
- });
- }
-
- /**
- * Return whether the given package is one of the ones that is owned by the uid.
- */
- private boolean isValidPackage(String pkg, int uid) {
- if (pkg == null) {
- return false;
- }
- final PackageManager pm = getPackageManager();
- final String[] packages = pm.getPackagesForUid(uid);
- final int N = packages.length;
- for (int i = 0; i < N; i++) {
- if (packages[i].equals(pkg)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Save the subscription and if it is a new subscription send the results.
- */
- private void addSubscription(String id, ConnectionRecord connection, IBinder token,
- Bundle options) {
- // Save the subscription
- List<Pair<IBinder, Bundle>> callbackList = connection.subscriptions.get(id);
- if (callbackList == null) {
- callbackList = new ArrayList<>();
- }
- for (Pair<IBinder, Bundle> callback : callbackList) {
- if (token == callback.first
- && MediaBrowserUtils.areSameOptions(options, callback.second)) {
- return;
- }
- }
- callbackList.add(new Pair<>(token, options));
- connection.subscriptions.put(id, callbackList);
- // send the results
- performLoadChildren(id, connection, options);
- }
-
- /**
- * Remove the subscription.
- */
- private boolean removeSubscription(String id, ConnectionRecord connection, IBinder token) {
- if (token == null) {
- return connection.subscriptions.remove(id) != null;
- }
- boolean removed = false;
- List<Pair<IBinder, Bundle>> callbackList = connection.subscriptions.get(id);
- if (callbackList != null) {
- Iterator<Pair<IBinder, Bundle>> iter = callbackList.iterator();
- while (iter.hasNext()) {
- if (token == iter.next().first) {
- removed = true;
- iter.remove();
- }
- }
- if (callbackList.size() == 0) {
- connection.subscriptions.remove(id);
- }
- }
- return removed;
- }
-
- /**
- * Call onLoadChildren and then send the results back to the connection.
- * <p>
- * Callers must make sure that this connection is still connected.
- */
- private void performLoadChildren(final String parentId, final ConnectionRecord connection,
- final Bundle options) {
- final Result<List<MediaBrowser.MediaItem>> result =
- new Result<List<MediaBrowser.MediaItem>>(parentId) {
- @Override
- void onResultSent(List<MediaBrowser.MediaItem> list, @ResultFlags int flag) {
- if (mServiceState.mConnections.get(connection.callbacks.asBinder()) != connection) {
- if (DBG) {
- Log.d(TAG, "Not sending onLoadChildren result for connection that has"
- + " been disconnected. pkg=" + connection.pkg + " id=" + parentId);
- }
- return;
- }
-
- List<MediaBrowser.MediaItem> filteredList =
- (flag & RESULT_FLAG_OPTION_NOT_HANDLED) != 0
- ? MediaBrowserUtils.applyPagingOptions(list, options) : list;
- final ParceledListSlice<MediaBrowser.MediaItem> pls;
- if (filteredList == null) {
- pls = null;
- } else {
- pls = new ParceledListSlice<>(filteredList);
- // Limit the size of initial Parcel to prevent binder buffer overflow
- // as onLoadChildren is an async binder call.
- pls.setInlineCountLimit(1);
- }
- try {
- connection.callbacks.onLoadChildren(parentId, pls, options);
- } catch (RemoteException ex) {
- // The other side is in the process of crashing.
- Log.w(TAG, "Calling onLoadChildren() failed for id=" + parentId
- + " package=" + connection.pkg);
- }
- }
- };
-
- mServiceState.mCurConnection = connection;
- if (options == null) {
- onLoadChildren(parentId, result);
- } else {
- onLoadChildren(parentId, result, options);
- }
- mServiceState.mCurConnection = null;
-
- if (!result.isDone()) {
- throw new IllegalStateException("onLoadChildren must call detach() or sendResult()"
- + " before returning for package=" + connection.pkg + " id=" + parentId);
- }
- }
-
- private void performLoadItem(String itemId, final ConnectionRecord connection,
- final ResultReceiver receiver) {
- final Result<MediaBrowser.MediaItem> result =
- new Result<MediaBrowser.MediaItem>(itemId) {
- @Override
- void onResultSent(MediaBrowser.MediaItem item, @ResultFlags int flag) {
- if (mServiceState.mConnections.get(connection.callbacks.asBinder()) != connection) {
- if (DBG) {
- Log.d(TAG, "Not sending onLoadItem result for connection that has"
- + " been disconnected. pkg=" + connection.pkg + " id=" + itemId);
- }
- return;
- }
- if ((flag & RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED) != 0) {
- receiver.send(RESULT_ERROR, null);
- return;
- }
- Bundle bundle = new Bundle();
- bundle.putParcelable(KEY_MEDIA_ITEM, item);
- receiver.send(RESULT_OK, bundle);
- }
- };
-
- mServiceState.mCurConnection = connection;
- onLoadItem(itemId, result);
- mServiceState.mCurConnection = null;
-
- if (!result.isDone()) {
- throw new IllegalStateException("onLoadItem must call detach() or sendResult()"
- + " before returning for id=" + itemId);
- }
+ mHandler.post(() -> mServiceState.get().notifyChildrenChangeOnHandler(parentId, options));
}
/**
@@ -890,14 +647,322 @@ public abstract class MediaBrowserService extends Service {
* service. This allows us to put the service in a valid state once the session is released
* (which is an irrecoverable invalid state). More details about this in b/185136506.
*/
- private static class ServiceState {
+ private class ServiceState {
// Fields accessed from any caller thread.
@Nullable private MediaSession.Token mSession;
- @Nullable private ServiceBinder mBinder;
// Fields accessed from mHandler only.
@NonNull private final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap<>();
- @Nullable private ConnectionRecord mCurConnection;
+
+ public ServiceBinder getBinder() {
+ return mBinder;
+ }
+
+ public void postOnHandler(Runnable runnable) {
+ mHandler.post(runnable);
+ }
+
+ public void release() {
+ mHandler.postAtFrontOfQueue(this::clearConnectionsOnHandler);
+ }
+
+ private void clearConnectionsOnHandler() {
+ Iterator<ConnectionRecord> iterator = mConnections.values().iterator();
+ while (iterator.hasNext()) {
+ ConnectionRecord record = iterator.next();
+ iterator.remove();
+ try {
+ record.callbacks.onDisconnect();
+ } catch (RemoteException exception) {
+ Log.w(
+ TAG,
+ TextUtils.formatSimple("onDisconnectRequest for %s failed", record.pkg),
+ exception);
+ }
+ }
+ }
+
+ public void removeConnectionRecordOnHandler(IMediaBrowserServiceCallbacks callbacks) {
+ IBinder b = callbacks.asBinder();
+ // Clear out the old subscriptions. We are getting new ones.
+ ConnectionRecord old = mConnections.remove(b);
+ if (old != null) {
+ old.callbacks.asBinder().unlinkToDeath(old, 0);
+ }
+ }
+
+ public void notifySessionTokenInitializedOnHandler(MediaSession.Token token) {
+ Iterator<ConnectionRecord> iter = mConnections.values().iterator();
+ while (iter.hasNext()) {
+ ConnectionRecord connection = iter.next();
+ try {
+ connection.callbacks.onConnect(
+ connection.root.getRootId(), token, connection.root.getExtras());
+ } catch (RemoteException e) {
+ Log.w(TAG, "Connection for " + connection.pkg + " is no longer valid.");
+ iter.remove();
+ }
+ }
+ }
+
+ public void notifyChildrenChangeOnHandler(String parentId, Bundle options) {
+ for (IBinder binder : mConnections.keySet()) {
+ ConnectionRecord connection = mConnections.get(binder);
+ List<Pair<IBinder, Bundle>> callbackList = connection.subscriptions.get(parentId);
+ if (callbackList != null) {
+ for (Pair<IBinder, Bundle> callback : callbackList) {
+ if (MediaBrowserUtils.hasDuplicatedItems(options, callback.second)) {
+ performLoadChildrenOnHandler(parentId, connection, callback.second);
+ }
+ }
+ }
+ }
+ }
+
+ /** Save the subscription and if it is a new subscription send the results. */
+ public void addSubscriptionOnHandler(
+ String id, IMediaBrowserServiceCallbacks callbacks, IBinder token, Bundle options) {
+ IBinder b = callbacks.asBinder();
+ // Get the record for the connection
+ ConnectionRecord connection = mConnections.get(b);
+ if (connection == null) {
+ Log.w(TAG, "addSubscription for callback that isn't registered id=" + id);
+ return;
+ }
+
+ // Save the subscription
+ List<Pair<IBinder, Bundle>> callbackList = connection.subscriptions.get(id);
+ if (callbackList == null) {
+ callbackList = new ArrayList<>();
+ }
+ for (Pair<IBinder, Bundle> callback : callbackList) {
+ if (token == callback.first
+ && MediaBrowserUtils.areSameOptions(options, callback.second)) {
+ return;
+ }
+ }
+ callbackList.add(new Pair<>(token, options));
+ connection.subscriptions.put(id, callbackList);
+ // send the results
+ performLoadChildrenOnHandler(id, connection, options);
+ }
+
+ public void connectOnHandler(
+ String pkg,
+ int pid,
+ int uid,
+ Bundle rootHints,
+ IMediaBrowserServiceCallbacks callbacks) {
+ IBinder b = callbacks.asBinder();
+ // Clear out the old subscriptions. We are getting new ones.
+ mConnections.remove(b);
+
+ // Temporarily sets a placeholder ConnectionRecord to make getCurrentBrowserInfo() work
+ // in onGetRoot().
+ mCurrentConnectionOnHandler =
+ new ConnectionRecord(
+ /* serviceState= */ this,
+ pkg,
+ pid,
+ uid,
+ rootHints,
+ callbacks,
+ /* root= */ null);
+ BrowserRoot root = onGetRoot(pkg, uid, rootHints);
+ mCurrentConnectionOnHandler = null;
+
+ // If they didn't return something, don't allow this client.
+ if (root == null) {
+ Log.i(TAG, "No root for client " + pkg + " from service " + getClass().getName());
+ try {
+ callbacks.onConnectFailed();
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Calling onConnectFailed() failed. Ignoring. pkg=" + pkg);
+ }
+ } else {
+ try {
+ ConnectionRecord connection =
+ new ConnectionRecord(
+ /* serviceState= */ this,
+ pkg,
+ pid,
+ uid,
+ rootHints,
+ callbacks,
+ root);
+ mConnections.put(b, connection);
+ b.linkToDeath(connection, /* flags= */ 0);
+ if (mSession != null) {
+ callbacks.onConnect(
+ connection.root.getRootId(), mSession, connection.root.getExtras());
+ }
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Calling onConnect() failed. Dropping client. pkg=" + pkg);
+ mConnections.remove(b);
+ }
+ }
+ }
+
+ /** Remove the subscription. */
+ public boolean removeSubscriptionOnHandler(
+ String id, IMediaBrowserServiceCallbacks callbacks, IBinder token) {
+ IBinder b = callbacks.asBinder();
+
+ ConnectionRecord connection = mConnections.get(b);
+ if (connection == null) {
+ Log.w(TAG, "removeSubscription for callback that isn't registered id=" + id);
+ return true;
+ }
+
+ if (token == null) {
+ return connection.subscriptions.remove(id) != null;
+ }
+ boolean removed = false;
+ List<Pair<IBinder, Bundle>> callbackList = connection.subscriptions.get(id);
+ if (callbackList != null) {
+ Iterator<Pair<IBinder, Bundle>> iter = callbackList.iterator();
+ while (iter.hasNext()) {
+ if (token == iter.next().first) {
+ removed = true;
+ iter.remove();
+ }
+ }
+ if (callbackList.isEmpty()) {
+ connection.subscriptions.remove(id);
+ }
+ }
+ return removed;
+ }
+
+ /**
+ * Call onLoadChildren and then send the results back to the connection.
+ *
+ * <p>Callers must make sure that this connection is still connected.
+ */
+ public void performLoadChildrenOnHandler(
+ String parentId, ConnectionRecord connection, Bundle options) {
+ Result<List<MediaBrowser.MediaItem>> result =
+ new Result<>(parentId) {
+ @Override
+ void onResultSent(
+ List<MediaBrowser.MediaItem> list, @ResultFlags int flag) {
+ if (mConnections.get(connection.callbacks.asBinder()) != connection) {
+ if (DBG) {
+ Log.d(
+ TAG,
+ "Not sending onLoadChildren result for connection that"
+ + " has been disconnected. pkg="
+ + connection.pkg
+ + " id="
+ + parentId);
+ }
+ return;
+ }
+
+ List<MediaBrowser.MediaItem> filteredList =
+ (flag & RESULT_FLAG_OPTION_NOT_HANDLED) != 0
+ ? MediaBrowserUtils.applyPagingOptions(list, options)
+ : list;
+ ParceledListSlice<MediaBrowser.MediaItem> pls = null;
+ if (filteredList != null) {
+ pls = new ParceledListSlice<>(filteredList);
+ // Limit the size of initial Parcel to prevent binder buffer
+ // overflow as onLoadChildren is an async binder call.
+ pls.setInlineCountLimit(1);
+ }
+ try {
+ connection.callbacks.onLoadChildren(parentId, pls, options);
+ } catch (RemoteException ex) {
+ // The other side is in the process of crashing.
+ Log.w(
+ TAG,
+ "Calling onLoadChildren() failed for id="
+ + parentId
+ + " package="
+ + connection.pkg);
+ }
+ }
+ };
+
+ mCurrentConnectionOnHandler = connection;
+ if (options == null) {
+ onLoadChildren(parentId, result);
+ } else {
+ onLoadChildren(parentId, result, options);
+ }
+ mCurrentConnectionOnHandler = null;
+
+ if (!result.isDone()) {
+ throw new IllegalStateException(
+ "onLoadChildren must call detach() or sendResult()"
+ + " before returning for package="
+ + connection.pkg
+ + " id="
+ + parentId);
+ }
+ }
+
+ public void performLoadItemOnHandler(
+ String itemId,
+ IMediaBrowserServiceCallbacks callbacks,
+ ResultReceiver receiver) {
+ IBinder b = callbacks.asBinder();
+ ConnectionRecord connection = mConnections.get(b);
+ if (connection == null) {
+ Log.w(TAG, "getMediaItem for callback that isn't registered id=" + itemId);
+ return;
+ }
+
+ Result<MediaBrowser.MediaItem> result =
+ new Result<>(itemId) {
+ @Override
+ void onResultSent(MediaBrowser.MediaItem item, @ResultFlags int flag) {
+ if (mConnections.get(connection.callbacks.asBinder()) != connection) {
+ if (DBG) {
+ Log.d(
+ TAG,
+ "Not sending onLoadItem result for connection that has"
+ + " been disconnected. pkg="
+ + connection.pkg
+ + " id="
+ + itemId);
+ }
+ return;
+ }
+ if ((flag & RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED) != 0) {
+ receiver.send(RESULT_ERROR, null);
+ return;
+ }
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(KEY_MEDIA_ITEM, item);
+ receiver.send(RESULT_OK, bundle);
+ }
+ };
+
+ mCurrentConnectionOnHandler = connection;
+ onLoadItem(itemId, result);
+ mCurrentConnectionOnHandler = null;
+
+ if (!result.isDone()) {
+ throw new IllegalStateException(
+ "onLoadItem must call detach() or sendResult() before returning for id="
+ + itemId);
+ }
+ }
+
+ /** Return whether the given package corresponds to the given uid. */
+ public boolean isValidPackage(String providedPackage, int uid) {
+ if (providedPackage == null) {
+ return false;
+ }
+ PackageManager pm = getPackageManager();
+ for (String packageForUid : pm.getPackagesForUid(uid)) {
+ if (packageForUid.equals(providedPackage)) {
+ return true;
+ }
+ }
+ return false;
+ }
}
}
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 8cdd59e51ffe..8396005b1b63 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -458,6 +458,24 @@ status_t JMediaCodec::queueSecureInputBuffer(
presentationTimeUs, flags, errorDetailMsg);
}
+status_t JMediaCodec::queueSecureInputBuffers(
+ size_t index,
+ size_t offset,
+ size_t size,
+ const sp<RefBase> &auInfos_,
+ const sp<RefBase> &cryptoInfos_,
+ AString *errorDetailMsg) {
+ sp<BufferInfosWrapper> auInfos((BufferInfosWrapper *)auInfos_.get());
+ sp<CryptoInfosWrapper> cryptoInfos((CryptoInfosWrapper *)cryptoInfos_.get());
+ return mCodec->queueSecureInputBuffers(
+ index,
+ offset,
+ size,
+ auInfos,
+ cryptoInfos,
+ errorDetailMsg);
+}
+
status_t JMediaCodec::queueBuffer(
size_t index, const std::shared_ptr<C2Buffer> &buffer,
const sp<RefBase> &infos, const sp<AMessage> &tunings, AString *errorDetailMsg) {
@@ -470,19 +488,16 @@ status_t JMediaCodec::queueEncryptedLinearBlock(
size_t index,
const sp<hardware::HidlMemory> &buffer,
size_t offset,
- const CryptoPlugin::SubSample *subSamples,
- size_t numSubSamples,
- const uint8_t key[16],
- const uint8_t iv[16],
- CryptoPlugin::Mode mode,
- const CryptoPlugin::Pattern &pattern,
+ size_t size,
const sp<RefBase> &infos,
+ const sp<RefBase> &cryptoInfos_,
const sp<AMessage> &tunings,
AString *errorDetailMsg) {
sp<BufferInfosWrapper> auInfo((BufferInfosWrapper *)infos.get());
+ sp<CryptoInfosWrapper> cryptoInfos((CryptoInfosWrapper *)cryptoInfos_.get());
return mCodec->queueEncryptedBuffer(
- index, buffer, offset, subSamples, numSubSamples, key, iv, mode, pattern,
- auInfo, tunings, errorDetailMsg);
+ index, buffer, offset, size, auInfo, cryptoInfos,
+ tunings, errorDetailMsg);
}
status_t JMediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
@@ -2262,6 +2277,61 @@ struct NativeCryptoInfo {
CryptoPlugin::Pattern mPattern;
};
+// This class takes away all dependencies on java(env and jni) and
+// could be used for taking cryptoInfo objects to MediaCodec.
+struct MediaCodecCryptoInfo: public CodecCryptoInfo {
+ explicit MediaCodecCryptoInfo(const NativeCryptoInfo &cryptoInfo) {
+ if (cryptoInfo.mErr == OK) {
+ mNumSubSamples = cryptoInfo.mNumSubSamples;
+ mMode = cryptoInfo.mMode;
+ mPattern = cryptoInfo.mPattern;
+ if (cryptoInfo.mKey != nullptr) {
+ mKeyBuffer = ABuffer::CreateAsCopy(cryptoInfo.mKey, 16);
+ mKey = (uint8_t*)(mKeyBuffer.get() != nullptr ? mKeyBuffer.get()->data() : nullptr);
+ }
+ if (cryptoInfo.mIv != nullptr) {
+ mIvBuffer = ABuffer::CreateAsCopy(cryptoInfo.mIv, 16);
+ mIv = (uint8_t*)(mIvBuffer.get() != nullptr ? mIvBuffer.get()->data() : nullptr);
+ }
+ if (cryptoInfo.mSubSamples != nullptr) {
+ mSubSamplesBuffer = new ABuffer(sizeof(CryptoPlugin::SubSample) * mNumSubSamples);
+ if (mSubSamplesBuffer.get()) {
+ CryptoPlugin::SubSample * samples =
+ (CryptoPlugin::SubSample *)(mSubSamplesBuffer.get()->data());
+ for (int s = 0 ; s < mNumSubSamples ; s++) {
+ samples[s].mNumBytesOfClearData =
+ cryptoInfo.mSubSamples[s].mNumBytesOfClearData;
+ samples[s].mNumBytesOfEncryptedData =
+ cryptoInfo.mSubSamples[s].mNumBytesOfEncryptedData;
+ }
+ mSubSamples = (CryptoPlugin::SubSample *)mSubSamplesBuffer.get()->data();
+ }
+ }
+
+ }
+ }
+
+ explicit MediaCodecCryptoInfo(jint size) {
+ mSubSamplesBuffer = new ABuffer(sizeof(CryptoPlugin::SubSample) * 1);
+ mNumSubSamples = 1;
+ if (mSubSamplesBuffer.get()) {
+ CryptoPlugin::SubSample * samples =
+ (CryptoPlugin::SubSample *)(mSubSamplesBuffer.get()->data());
+ samples[0].mNumBytesOfClearData = size;
+ samples[0].mNumBytesOfEncryptedData = 0;
+ mSubSamples = (CryptoPlugin::SubSample *)mSubSamplesBuffer.get()->data();
+ }
+ }
+ ~MediaCodecCryptoInfo() {}
+
+protected:
+ // all backup buffers for the base object.
+ sp<ABuffer> mKeyBuffer;
+ sp<ABuffer> mIvBuffer;
+ sp<ABuffer> mSubSamplesBuffer;
+
+};
+
static void android_media_MediaCodec_queueSecureInputBuffer(
JNIEnv *env,
jobject thiz,
@@ -2430,6 +2500,99 @@ static void android_media_MediaCodec_queueSecureInputBuffer(
codec->getExceptionMessage(errorDetailMsg.c_str()).c_str(), codec->getCrypto());
}
+static status_t extractCryptoInfosFromObjectArray(JNIEnv * const env,
+ jint * const totalSize,
+ std::vector<std::unique_ptr<CodecCryptoInfo>> * const cryptoInfoObjs,
+ const jobjectArray &objArray,
+ AString * const errorDetailMsg) {
+ if (env == nullptr
+ || cryptoInfoObjs == nullptr
+ || totalSize == nullptr) {
+ if (errorDetailMsg) {
+ *errorDetailMsg = "Error: Null Parameters provided for extracting CryptoInfo";
+ }
+ return BAD_VALUE;
+ }
+ const jsize numEntries = env->GetArrayLength(objArray);
+ if (numEntries <= 0) {
+ if (errorDetailMsg) {
+ *errorDetailMsg = "Error: No CryptoInfo found while queuing for large frame input";
+ }
+ return BAD_VALUE;
+ }
+ cryptoInfoObjs->clear();
+ *totalSize = 0;
+ jint size = 0;
+ for (jsize i = 0; i < numEntries ; i++) {
+ jobject param = env->GetObjectArrayElement(objArray, i);
+ if (param == NULL) {
+ if (errorDetailMsg) {
+ *errorDetailMsg = "Error: Null Parameters provided for extracting CryptoInfo";
+ }
+ return BAD_VALUE;
+ }
+ NativeCryptoInfo nativeInfo(env, param);
+ std::unique_ptr<CodecCryptoInfo> info(new MediaCodecCryptoInfo(nativeInfo));
+ for (int i = 0; i < info->mNumSubSamples; i++) {
+ size += info->mSubSamples[i].mNumBytesOfClearData;
+ size += info->mSubSamples[i].mNumBytesOfEncryptedData;
+ }
+ cryptoInfoObjs->push_back(std::move(info));
+ }
+ *totalSize = size;
+ return OK;
+}
+
+
+static void android_media_MediaCodec_queueSecureInputBuffers(
+ JNIEnv *env,
+ jobject thiz,
+ jint index,
+ jobjectArray bufferInfosObjs,
+ jobjectArray cryptoInfoObjs) {
+ ALOGV("android_media_MediaCodec_queueSecureInputBuffers");
+
+ sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+ if (codec == NULL || codec->initCheck() != OK) {
+ throwExceptionAsNecessary(env, INVALID_OPERATION, codec);
+ return;
+ }
+ sp<BufferInfosWrapper> auInfos =
+ new BufferInfosWrapper{decltype(auInfos->value)()};
+ sp<CryptoInfosWrapper> cryptoInfos =
+ new CryptoInfosWrapper{decltype(cryptoInfos->value)()};
+ AString errorDetailMsg;
+ jint initialOffset = 0;
+ jint totalSize = 0;
+ status_t err = extractInfosFromObject(
+ env,
+ &initialOffset,
+ &totalSize,
+ &auInfos->value,
+ bufferInfosObjs,
+ &errorDetailMsg);
+ if (err == OK) {
+ err = extractCryptoInfosFromObjectArray(env,
+ &totalSize,
+ &cryptoInfos->value,
+ cryptoInfoObjs,
+ &errorDetailMsg);
+ }
+ if (err == OK) {
+ err = codec->queueSecureInputBuffers(
+ index,
+ initialOffset,
+ totalSize,
+ auInfos,
+ cryptoInfos,
+ &errorDetailMsg);
+ }
+ throwExceptionAsNecessary(
+ env, err, ACTION_CODE_FATAL,
+ codec->getExceptionMessage(errorDetailMsg.c_str()).c_str(), codec->getCrypto());
+}
+
static jobject android_media_MediaCodec_mapHardwareBuffer(JNIEnv *env, jclass, jobject bufferObj) {
ALOGV("android_media_MediaCodec_mapHardwareBuffer");
AHardwareBuffer *hardwareBuffer = android_hardware_HardwareBuffer_getNativeHardwareBuffer(
@@ -2762,7 +2925,7 @@ static void extractBufferFromContext(
static void android_media_MediaCodec_native_queueLinearBlock(
JNIEnv *env, jobject thiz, jint index, jobject bufferObj,
- jobject cryptoInfoObj, jobjectArray objArray, jobject keys, jobject values) {
+ jobjectArray cryptoInfoArray, jobjectArray objArray, jobject keys, jobject values) {
ALOGV("android_media_MediaCodec_native_queueLinearBlock");
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
@@ -2780,8 +2943,8 @@ static void android_media_MediaCodec_native_queueLinearBlock(
"error occurred while converting tunings from Java to native");
return;
}
- jint totalSize;
- jint initialOffset;
+ jint totalSize = 0;
+ jint initialOffset = 0;
std::vector<AccessUnitInfo> infoVec;
AString errorDetailMsg;
err = extractInfosFromObject(env,
@@ -2832,8 +2995,19 @@ static void android_media_MediaCodec_native_queueLinearBlock(
"MediaCodec.LinearBlock#obtain method to obtain a compatible buffer.");
return;
}
- auto cryptoInfo =
- cryptoInfoObj ? NativeCryptoInfo{env, cryptoInfoObj} : NativeCryptoInfo{totalSize};
+ sp<CryptoInfosWrapper> cryptoInfos = new CryptoInfosWrapper{decltype(cryptoInfos->value)()};
+ jint sampleSize = 0;
+ if (cryptoInfoArray != nullptr) {
+ extractCryptoInfosFromObjectArray(env,
+ &sampleSize,
+ &cryptoInfos->value,
+ cryptoInfoArray,
+ &errorDetailMsg);
+ } else {
+ sampleSize = totalSize;
+ std::unique_ptr<CodecCryptoInfo> cryptoInfo{new MediaCodecCryptoInfo(totalSize)};
+ cryptoInfos->value.push_back(std::move(cryptoInfo));
+ }
if (env->ExceptionCheck()) {
// Creation of cryptoInfo failed. Let the exception bubble up.
return;
@@ -2842,11 +3016,9 @@ static void android_media_MediaCodec_native_queueLinearBlock(
index,
memory,
initialOffset,
- cryptoInfo.mSubSamples, cryptoInfo.mNumSubSamples,
- (const uint8_t *)cryptoInfo.mKey, (const uint8_t *)cryptoInfo.mIv,
- cryptoInfo.mMode,
- cryptoInfo.mPattern,
+ sampleSize,
infos,
+ cryptoInfos,
tunings,
&errorDetailMsg);
ALOGI_IF(err != OK, "queueEncryptedLinearBlock returned err = %d", err);
@@ -3950,6 +4122,9 @@ static const JNINativeMethod gMethods[] = {
{ "native_queueSecureInputBuffer", "(IILandroid/media/MediaCodec$CryptoInfo;JI)V",
(void *)android_media_MediaCodec_queueSecureInputBuffer },
+ { "native_queueSecureInputBuffers", "(I[Ljava/lang/Object;[Ljava/lang/Object;)V",
+ (void *)android_media_MediaCodec_queueSecureInputBuffers },
+
{ "native_mapHardwareBuffer",
"(Landroid/hardware/HardwareBuffer;)Landroid/media/Image;",
(void *)android_media_MediaCodec_mapHardwareBuffer },
@@ -3957,7 +4132,7 @@ static const JNINativeMethod gMethods[] = {
{ "native_closeMediaImage", "(J)V", (void *)android_media_MediaCodec_closeMediaImage },
{ "native_queueLinearBlock",
- "(ILandroid/media/MediaCodec$LinearBlock;Landroid/media/MediaCodec$CryptoInfo;"
+ "(ILandroid/media/MediaCodec$LinearBlock;[Ljava/lang/Object;"
"[Ljava/lang/Object;Ljava/util/ArrayList;Ljava/util/ArrayList;)V",
(void *)android_media_MediaCodec_native_queueLinearBlock },
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index 02708efdea3a..abb23f516156 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -114,6 +114,14 @@ struct JMediaCodec : public AHandler {
uint32_t flags,
AString *errorDetailMsg);
+ status_t queueSecureInputBuffers(
+ size_t index,
+ size_t offset,
+ size_t size,
+ const sp<RefBase> &auInfos,
+ const sp<RefBase> &cryptoInfos,
+ AString *errorDetailMsg);
+
status_t queueBuffer(
size_t index, const std::shared_ptr<C2Buffer> &buffer,
const sp<RefBase> &infos, const sp<AMessage> &tunings,
@@ -123,13 +131,9 @@ struct JMediaCodec : public AHandler {
size_t index,
const sp<hardware::HidlMemory> &buffer,
size_t offset,
- const CryptoPlugin::SubSample *subSamples,
- size_t numSubSamples,
- const uint8_t key[16],
- const uint8_t iv[16],
- CryptoPlugin::Mode mode,
- const CryptoPlugin::Pattern &pattern,
+ size_t size,
const sp<RefBase> &infos,
+ const sp<RefBase> &cryptoInfos,
const sp<AMessage> &tunings,
AString *errorDetailMsg);
diff --git a/media/jni/audioeffect/Visualizer.cpp b/media/jni/audioeffect/Visualizer.cpp
index 09c45ea97e9d..9ae5c991514a 100644
--- a/media/jni/audioeffect/Visualizer.cpp
+++ b/media/jni/audioeffect/Visualizer.cpp
@@ -25,7 +25,6 @@
#include <limits.h>
#include <audio_utils/fixedfft.h>
-#include <cutils/bitops.h>
#include <utils/Thread.h>
#include <android/content/AttributionSourceState.h>
@@ -59,8 +58,8 @@ status_t Visualizer::set(int32_t priority,
status_t status = AudioEffect::set(
SL_IID_VISUALIZATION, nullptr, priority, cbf, user, sessionId, io, device, probe);
if (status == NO_ERROR || status == ALREADY_EXISTS) {
- initCaptureSize();
- initSampleRate();
+ status = initCaptureSize();
+ if (status == NO_ERROR) initSampleRate();
}
return status;
}
@@ -152,9 +151,8 @@ status_t Visualizer::setCaptureCallBack(capture_cbk_t cbk, void* user, uint32_t
status_t Visualizer::setCaptureSize(uint32_t size)
{
- if (size > VISUALIZER_CAPTURE_SIZE_MAX ||
- size < VISUALIZER_CAPTURE_SIZE_MIN ||
- popcount(size) != 1) {
+ if (!isCaptureSizeValid(size)) {
+ ALOGE("%s with invalid capture size %u from HAL", __func__, size);
return BAD_VALUE;
}
@@ -172,7 +170,7 @@ status_t Visualizer::setCaptureSize(uint32_t size)
*((int32_t *)p->data + 1)= size;
status_t status = setParameter(p);
- ALOGV("setCaptureSize size %d status %d p->status %d", size, status, p->status);
+ ALOGV("setCaptureSize size %u status %d p->status %d", size, status, p->status);
if (status == NO_ERROR) {
status = p->status;
@@ -257,8 +255,8 @@ status_t Visualizer::getIntMeasurements(uint32_t type, uint32_t number, int32_t
if ((type != MEASUREMENT_MODE_PEAK_RMS)
// for peak+RMS measurement, the results are 2 int32_t values
|| (number != 2)) {
- ALOGE("Cannot retrieve int measurements, MEASUREMENT_MODE_PEAK_RMS returns 2 ints, not %d",
- number);
+ ALOGE("Cannot retrieve int measurements, MEASUREMENT_MODE_PEAK_RMS returns 2 ints, not %u",
+ number);
return BAD_VALUE;
}
@@ -390,7 +388,7 @@ void Visualizer::periodicCapture()
}
}
-uint32_t Visualizer::initCaptureSize()
+status_t Visualizer::initCaptureSize()
{
uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
effect_param_t *p = (effect_param_t *)buf32;
@@ -405,14 +403,20 @@ uint32_t Visualizer::initCaptureSize()
}
uint32_t size = 0;
- if (status == NO_ERROR) {
- size = *((int32_t *)p->data + 1);
+ if (status != NO_ERROR) {
+ ALOGE("%s getParameter failed status %d", __func__, status);
+ return status;
}
- mCaptureSize = size;
- ALOGV("initCaptureSize size %d status %d", mCaptureSize, status);
+ size = *((int32_t *)p->data + 1);
+ if (!isCaptureSizeValid(size)) {
+ ALOGE("%s with invalid capture size %u from HAL", __func__, size);
+ return BAD_VALUE;
+ }
- return size;
+ mCaptureSize = size;
+ ALOGV("%s size %u status %d", __func__, mCaptureSize, status);
+ return NO_ERROR;
}
void Visualizer::initSampleRate()
diff --git a/media/jni/audioeffect/Visualizer.h b/media/jni/audioeffect/Visualizer.h
index b38c01f62cf1..26d58d0f56e9 100644
--- a/media/jni/audioeffect/Visualizer.h
+++ b/media/jni/audioeffect/Visualizer.h
@@ -20,6 +20,8 @@
#include <media/AudioEffect.h>
#include <system/audio_effects/effect_visualizer.h>
#include <utils/Thread.h>
+#include <cstdint>
+#include <cutils/bitops.h>
#include "android/content/AttributionSourceState.h"
/**
@@ -170,8 +172,12 @@ private:
status_t doFft(uint8_t *fft, uint8_t *waveform);
void periodicCapture();
- uint32_t initCaptureSize();
+ status_t initCaptureSize();
void initSampleRate();
+ static constexpr bool isCaptureSizeValid(uint32_t size) {
+ return size <= VISUALIZER_CAPTURE_SIZE_MAX && size >= VISUALIZER_CAPTURE_SIZE_MIN &&
+ popcount(size) == 1;
+ }
Mutex mCaptureLock;
uint32_t mCaptureRate = CAPTURE_RATE_DEF;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java
index 388a65d2a904..00068bda60dd 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java
@@ -1709,7 +1709,7 @@ public class CameraTestUtils extends Assert {
* <p>
* Two images are strongly equal if and only if the data, formats, sizes,
* and timestamps are same. For {@link ImageFormat#PRIVATE PRIVATE} format
- * images, the image data is not not accessible thus the data comparison is
+ * images, the image data is not accessible thus the data comparison is
* effectively skipped as the number of planes is zero.
* </p>
* <p>
@@ -2049,7 +2049,7 @@ public class CameraTestUtils extends Assert {
}
} else {
// Case 2.
- collector.expectEquals("Exif orientaiton should match requested orientation",
+ collector.expectEquals("Exif orientation should match requested orientation",
requestedOrientation, getExifOrientationInDegree(exifOrientation,
collector));
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/StaticMetadata.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/StaticMetadata.java
index ed70ab996ccd..5dcd1cba337d 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/StaticMetadata.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/StaticMetadata.java
@@ -1127,8 +1127,8 @@ public class StaticMetadata {
* Get aeAvailableModes and do the validation check.
*
* <p>Depending on the check level this class has, for WAR or COLLECT levels,
- * If the aeMode list is invalid, return an empty mode array. The the caller doesn't
- * have to abort the execution even the aeMode list is invalid.</p>
+ * If the aeMode list is invalid, return an empty mode array. The caller doesn't
+ * have to abort the execution even if the aeMode list is invalid.</p>
* @return AE available modes
*/
public int[] getAeAvailableModesChecked() {
diff --git a/media/tests/mediatestutils/Android.bp b/media/tests/mediatestutils/Android.bp
index 15bc1774d4d4..88938e2f71d0 100644
--- a/media/tests/mediatestutils/Android.bp
+++ b/media/tests/mediatestutils/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_media_framework_audio",
// 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"
diff --git a/media/tests/mediatestutils/tests/Android.bp b/media/tests/mediatestutils/tests/Android.bp
index 24a8360d1187..f586f97f5205 100644
--- a/media/tests/mediatestutils/tests/Android.bp
+++ b/media/tests/mediatestutils/tests/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_media_framework_audio",
// 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"
diff --git a/native/android/input.cpp b/native/android/input.cpp
index 6efb0280ac02..53699bc706ea 100644
--- a/native/android/input.cpp
+++ b/native/android/input.cpp
@@ -87,7 +87,7 @@ int64_t AKeyEvent_getDownTime(const AInputEvent* key_event) {
const AInputEvent* AKeyEvent_fromJava(JNIEnv* env, jobject keyEvent) {
std::unique_ptr<KeyEvent> event = std::make_unique<KeyEvent>();
- *event = android::android_view_KeyEvent_toNative(env, keyEvent);
+ *event = android::android_view_KeyEvent_obtainAsCopy(env, keyEvent);
return event.release();
}
@@ -321,11 +321,13 @@ jobject AInputEvent_toJava(JNIEnv* env, const AInputEvent* aInputEvent) {
case AINPUT_EVENT_TYPE_MOTION:
return android::android_view_MotionEvent_obtainAsCopy(env,
static_cast<const MotionEvent&>(
- *aInputEvent));
+ *aInputEvent))
+ .release();
case AINPUT_EVENT_TYPE_KEY:
- return android::android_view_KeyEvent_fromNative(env,
- static_cast<const KeyEvent&>(
- *aInputEvent));
+ return android::android_view_KeyEvent_obtainAsCopy(env,
+ static_cast<const KeyEvent&>(
+ *aInputEvent))
+ .release();
default:
LOG_ALWAYS_FATAL("Unexpected event type %d in AInputEvent_toJava.", eventType);
}
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 3302265fb80c..35e37b2a6893 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -277,6 +277,7 @@ LIBANDROID {
ASurfaceTransaction_setHdrMetadata_cta861_3; # introduced=29
ASurfaceTransaction_setHdrMetadata_smpte2086; # introduced=29
ASurfaceTransaction_setExtendedRangeBrightness; # introduced=UpsideDownCake
+ ASurfaceTransaction_setDesiredHdrHeadroom; # introduced=VanillaIceCream
ASurfaceTransaction_setOnComplete; # introduced=29
ASurfaceTransaction_setOnCommit; # introduced=31
ASurfaceTransaction_setPosition; # introduced=31
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index 4b63fbf14d4c..9b1330fc048a 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -646,6 +646,24 @@ void ASurfaceTransaction_setExtendedRangeBrightness(ASurfaceTransaction* aSurfac
transaction->setExtendedRangeBrightness(surfaceControl, currentBufferRatio, desiredRatio);
}
+void ASurfaceTransaction_setDesiredHdrHeadroom(ASurfaceTransaction* aSurfaceTransaction,
+ ASurfaceControl* aSurfaceControl,
+ float desiredRatio) {
+ CHECK_NOT_NULL(aSurfaceTransaction);
+ CHECK_NOT_NULL(aSurfaceControl);
+
+ if (!isfinite(desiredRatio) || (desiredRatio < 1.0f && desiredRatio > 0.0f)) {
+ LOG_ALWAYS_FATAL("setDesiredHdrHeadroom, desiredRatio isn't finite && >= 1.0f or 0, got %f",
+ desiredRatio);
+ return;
+ }
+
+ sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+ Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+
+ transaction->setDesiredHdrHeadroom(surfaceControl, desiredRatio);
+}
+
void ASurfaceTransaction_setColor(ASurfaceTransaction* aSurfaceTransaction,
ASurfaceControl* aSurfaceControl,
float r, float g, float b, float alpha,
diff --git a/nfc-extras/Android.bp b/nfc-extras/Android.bp
index 1f187e8eab74..699ad505936f 100644
--- a/nfc-extras/Android.bp
+++ b/nfc-extras/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_fwk_nfc",
// 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"
@@ -28,7 +29,7 @@ java_sdk_library {
name: "com.android.nfc_extras",
srcs: ["java/**/*.java"],
libs: [
- "framework-nfc.impl"
+ "framework-nfc.impl",
],
api_packages: ["com.android.nfc_extras"],
dist_group: "android",
diff --git a/nfc-extras/tests/Android.bp b/nfc-extras/tests/Android.bp
index 4c1f2fb77e72..e8214b0484a9 100644
--- a/nfc-extras/tests/Android.bp
+++ b/nfc-extras/tests/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_fwk_nfc",
// 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"
diff --git a/nfc/Android.bp b/nfc/Android.bp
index 7136866d536f..b6bc40d5fc19 100644
--- a/nfc/Android.bp
+++ b/nfc/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_fwk_nfc",
// 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"
diff --git a/nfc/api/current.txt b/nfc/api/current.txt
index 845a8f97db10..0fb7c95e3680 100644
--- a/nfc/api/current.txt
+++ b/nfc/api/current.txt
@@ -82,7 +82,7 @@ package android.nfc {
method @FlaggedApi("android.nfc.enable_nfc_charging") public boolean isWlcEnabled();
method @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public void resetDiscoveryTechnology(@NonNull android.app.Activity);
method @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public void setDiscoveryTechnology(@NonNull android.app.Activity, int, int);
- method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean setTransactionAllowed(boolean);
+ method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean setObserveModeEnabled(boolean);
field public static final String ACTION_ADAPTER_STATE_CHANGED = "android.nfc.action.ADAPTER_STATE_CHANGED";
field public static final String ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED";
field @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public static final String ACTION_PREFERRED_PAYMENT_CHANGED = "android.nfc.action.PREFERRED_PAYMENT_CHANGED";
@@ -204,11 +204,11 @@ package android.nfc.cardemulation {
method public boolean isDefaultServiceForAid(android.content.ComponentName, String);
method public boolean isDefaultServiceForCategory(android.content.ComponentName, String);
method public boolean registerAidsForService(android.content.ComponentName, String, java.util.List<java.lang.String>);
- method @FlaggedApi("android.nfc.nfc_read_polling_loop") public boolean registerPollingLoopFilterForService(@NonNull android.content.ComponentName, @NonNull String);
+ method @FlaggedApi("android.nfc.nfc_read_polling_loop") public boolean registerPollingLoopFilterForService(@NonNull android.content.ComponentName, @NonNull String, boolean);
method public boolean removeAidsForService(android.content.ComponentName, String);
+ method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean setDefaultToObserveModeForService(@NonNull android.content.ComponentName, boolean);
method @NonNull @RequiresPermission(android.Manifest.permission.NFC) public boolean setOffHostForService(@NonNull android.content.ComponentName, @NonNull String);
method public boolean setPreferredService(android.app.Activity, android.content.ComponentName);
- method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean setServiceObserveModeDefault(@NonNull android.content.ComponentName, boolean);
method public boolean supportsAidPrefixRegistration();
method @NonNull @RequiresPermission(android.Manifest.permission.NFC) public boolean unsetOffHostForService(@NonNull android.content.ComponentName);
method public boolean unsetPreferredService(android.app.Activity);
@@ -228,20 +228,10 @@ package android.nfc.cardemulation {
method public final android.os.IBinder onBind(android.content.Intent);
method public abstract void onDeactivated(int);
method public abstract byte[] processCommandApdu(byte[], android.os.Bundle);
- method @FlaggedApi("android.nfc.nfc_read_polling_loop") public void processPollingFrames(@NonNull java.util.List<android.os.Bundle>);
+ method @FlaggedApi("android.nfc.nfc_read_polling_loop") public void processPollingFrames(@NonNull java.util.List<android.nfc.cardemulation.PollingFrame>);
method public final void sendResponseApdu(byte[]);
field public static final int DEACTIVATION_DESELECTED = 1; // 0x1
field public static final int DEACTIVATION_LINK_LOSS = 0; // 0x0
- field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String KEY_POLLING_LOOP_DATA = "android.nfc.cardemulation.DATA";
- field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String KEY_POLLING_LOOP_GAIN = "android.nfc.cardemulation.GAIN";
- field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String KEY_POLLING_LOOP_TIMESTAMP = "android.nfc.cardemulation.TIMESTAMP";
- field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final String KEY_POLLING_LOOP_TYPE = "android.nfc.cardemulation.TYPE";
- field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_A = 65; // 0x0041 'A'
- field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_B = 66; // 0x0042 'B'
- field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_F = 70; // 0x0046 'F'
- field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_OFF = 88; // 0x0058 'X'
- field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_ON = 79; // 0x004f 'O'
- field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final char POLLING_LOOP_TYPE_UNKNOWN = 85; // 0x0055 'U'
field public static final String SERVICE_INTERFACE = "android.nfc.cardemulation.action.HOST_APDU_SERVICE";
field public static final String SERVICE_META_DATA = "android.nfc.cardemulation.host_apdu_service";
}
@@ -274,6 +264,23 @@ package android.nfc.cardemulation {
field public static final String SERVICE_META_DATA = "android.nfc.cardemulation.off_host_apdu_service";
}
+ @FlaggedApi("android.nfc.nfc_read_polling_loop") public final class PollingFrame implements android.os.Parcelable {
+ ctor public PollingFrame(int, @Nullable byte[], int, int);
+ method public int describeContents();
+ method @NonNull public byte[] getData();
+ method public int getGain();
+ method public int getTimestamp();
+ method public int getType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.PollingFrame> CREATOR;
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final int POLLING_LOOP_TYPE_A = 65; // 0x41
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final int POLLING_LOOP_TYPE_B = 66; // 0x42
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final int POLLING_LOOP_TYPE_F = 70; // 0x46
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final int POLLING_LOOP_TYPE_OFF = 88; // 0x58
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final int POLLING_LOOP_TYPE_ON = 79; // 0x4f
+ field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final int POLLING_LOOP_TYPE_UNKNOWN = 85; // 0x55
+ }
+
}
package android.nfc.tech {
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
index dd2e17409368..a72e53907796 100644
--- a/nfc/api/system-current.txt
+++ b/nfc/api/system-current.txt
@@ -26,6 +26,8 @@ package android.nfc {
method @FlaggedApi("android.nfc.nfc_vendor_cmd") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void unregisterNfcVendorNciCallback(@NonNull android.nfc.NfcAdapter.NfcVendorNciCallback);
method @FlaggedApi("android.nfc.enable_nfc_charging") public void unregisterWlcStateListener(@NonNull android.nfc.NfcAdapter.WlcStateListener);
field @FlaggedApi("android.nfc.enable_nfc_mainline") public static final String ACTION_REQUIRE_UNLOCK_FOR_NFC = "android.nfc.action.REQUIRE_UNLOCK_FOR_NFC";
+ field @FlaggedApi("android.nfc.enable_nfc_mainline") @RequiresPermission(android.Manifest.permission.SHOW_CUSTOMIZED_RESOLVER) public static final String ACTION_SHOW_NFC_RESOLVER = "android.nfc.action.SHOW_NFC_RESOLVER";
+ field @FlaggedApi("android.nfc.enable_nfc_mainline") public static final String EXTRA_RESOLVE_INFOS = "android.nfc.extra.RESOLVE_INFOS";
field @FlaggedApi("android.nfc.nfc_vendor_cmd") public static final int MESSAGE_TYPE_COMMAND = 1; // 0x1
field @FlaggedApi("android.nfc.nfc_vendor_cmd") public static final int SEND_VENDOR_NCI_STATUS_FAILED = 3; // 0x3
field @FlaggedApi("android.nfc.nfc_vendor_cmd") public static final int SEND_VENDOR_NCI_STATUS_MESSAGE_CORRUPTED = 2; // 0x2
diff --git a/nfc/java/android/nfc/INfcCardEmulation.aidl b/nfc/java/android/nfc/INfcCardEmulation.aidl
index 791bd8c9e6f4..64f7fa44c12f 100644
--- a/nfc/java/android/nfc/INfcCardEmulation.aidl
+++ b/nfc/java/android/nfc/INfcCardEmulation.aidl
@@ -30,9 +30,9 @@ interface INfcCardEmulation
boolean isDefaultServiceForAid(int userHandle, in ComponentName service, String aid);
boolean setDefaultServiceForCategory(int userHandle, in ComponentName service, String category);
boolean setDefaultForNextTap(int userHandle, in ComponentName service);
- boolean setServiceObserveModeDefault(int userId, in android.content.ComponentName service, boolean enable);
+ boolean setDefaultToObserveModeForService(int userId, in android.content.ComponentName service, boolean enable);
boolean registerAidGroupForService(int userHandle, in ComponentName service, in AidGroup aidGroup);
- boolean registerPollingLoopFilterForService(int userHandle, in ComponentName service, in String pollingLoopFilter);
+ boolean registerPollingLoopFilterForService(int userHandle, in ComponentName service, in String pollingLoopFilter, boolean autoTransact);
boolean setOffHostForService(int userHandle, in ComponentName service, in String offHostSecureElement);
boolean unsetOffHostForService(int userHandle, in ComponentName service);
AidGroup getAidGroupForService(int userHandle, in ComponentName service, String category);
diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java
index 252f46fc40a4..0ebc3f5178e0 100644
--- a/nfc/java/android/nfc/NfcAdapter.java
+++ b/nfc/java/android/nfc/NfcAdapter.java
@@ -16,6 +16,7 @@
package android.nfc;
+import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
@@ -35,7 +36,9 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.net.Uri;
+import android.nfc.cardemulation.PollingFrame;
import android.nfc.tech.MifareClassic;
import android.nfc.tech.Ndef;
import android.nfc.tech.NfcA;
@@ -485,6 +488,25 @@ public final class NfcAdapter {
"android.nfc.action.REQUIRE_UNLOCK_FOR_NFC";
/**
+ * Intent action to start a NFC resolver activity in a customized share session with list of
+ * {@link ResolveInfo}.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @RequiresPermission(Manifest.permission.SHOW_CUSTOMIZED_RESOLVER)
+ public static final String ACTION_SHOW_NFC_RESOLVER = "android.nfc.action.SHOW_NFC_RESOLVER";
+
+ /**
+ * "Extras" key for an ArrayList of {@link ResolveInfo} records which are to be shown as the
+ * targets in the customized share session.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ public static final String EXTRA_RESOLVE_INFOS = "android.nfc.extra.RESOLVE_INFOS";
+
+ /**
* The requested app is correctly added to the Tag intent app preference.
*
* @see #setTagIntentAppPreferenceForUser(int userId, String pkg, boolean allow)
@@ -1228,16 +1250,16 @@ public final class NfcAdapter {
* and simply observe and notify the APDU service of polling loop frames. See
* {@link #isObserveModeSupported()} for a description of observe mode.
*
- * @param allowed true disables observe mode to allow the transaction to proceed while false
+ * @param enabled false disables observe mode to allow the transaction to proceed while true
* enables observe mode and does not allow transactions to proceed.
*
* @return boolean indicating success or failure.
*/
@FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
- public boolean setTransactionAllowed(boolean allowed) {
+ public boolean setObserveModeEnabled(boolean enabled) {
try {
- return sService.setObserveMode(!allowed);
+ return sService.setObserveMode(enabled);
} catch (RemoteException e) {
attemptDeadServiceRecovery(e);
return false;
@@ -2778,7 +2800,8 @@ public final class NfcAdapter {
*/
@TestApi
@FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
- public void notifyPollingLoop(@NonNull Bundle frame) {
+ public void notifyPollingLoop(@NonNull PollingFrame pollingFrame) {
+ Bundle frame = pollingFrame.toBundle();
try {
if (sService == null) {
attemptDeadServiceRecovery(null);
diff --git a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
index 3254a394c25a..e62e37bd4ca0 100644
--- a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -44,6 +44,8 @@ import android.util.Log;
import android.util.Xml;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.R;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -103,7 +105,6 @@ public final class ApduServiceInfo implements Parcelable {
*/
private final HashMap<String, AidGroup> mDynamicAidGroups;
- private final ArrayList<String> mPollingLoopFilters;
private final Map<String, Boolean> mAutoTransact;
@@ -138,6 +139,11 @@ public final class ApduServiceInfo implements Parcelable {
private boolean mCategoryOtherServiceEnabled;
/**
+ * Whether the NFC stack should default to Observe Mode when this preferred service.
+ */
+ private boolean mDefaultToObserveMode;
+
+ /**
* @hide
*/
@UnsupportedAppUsage
@@ -170,12 +176,25 @@ public final class ApduServiceInfo implements Parcelable {
List<AidGroup> staticAidGroups, List<AidGroup> dynamicAidGroups,
boolean requiresUnlock, boolean requiresScreenOn, int bannerResource, int uid,
String settingsActivityName, String offHost, String staticOffHost, boolean isEnabled) {
+ this(info, onHost, description, staticAidGroups, dynamicAidGroups,
+ requiresUnlock, requiresScreenOn, bannerResource, uid,
+ settingsActivityName, offHost, staticOffHost, isEnabled,
+ new HashMap<String, Boolean>());
+ }
+
+ /**
+ * @hide
+ */
+ public ApduServiceInfo(ResolveInfo info, boolean onHost, String description,
+ List<AidGroup> staticAidGroups, List<AidGroup> dynamicAidGroups,
+ boolean requiresUnlock, boolean requiresScreenOn, int bannerResource, int uid,
+ String settingsActivityName, String offHost, String staticOffHost, boolean isEnabled,
+ HashMap<String, Boolean> autoTransact) {
this.mService = info;
this.mDescription = description;
this.mStaticAidGroups = new HashMap<String, AidGroup>();
this.mDynamicAidGroups = new HashMap<String, AidGroup>();
- this.mPollingLoopFilters = new ArrayList<String>();
- this.mAutoTransact = new HashMap<String, Boolean>();
+ this.mAutoTransact = autoTransact;
this.mOffHostName = offHost;
this.mStaticOffHostName = staticOffHost;
this.mOnHost = onHost;
@@ -191,7 +210,6 @@ public final class ApduServiceInfo implements Parcelable {
this.mUid = uid;
this.mSettingsActivityName = settingsActivityName;
this.mCategoryOtherServiceEnabled = isEnabled;
-
}
/**
@@ -257,6 +275,9 @@ public final class ApduServiceInfo implements Parcelable {
com.android.internal.R.styleable.HostApduService_settingsActivity);
mOffHostName = null;
mStaticOffHostName = mOffHostName;
+ mDefaultToObserveMode = sa.getBoolean(
+ R.styleable.HostApduService_defaultToObserveMode,
+ false);
sa.recycle();
} else {
TypedArray sa = res.obtainAttributes(attrs,
@@ -276,6 +297,9 @@ public final class ApduServiceInfo implements Parcelable {
com.android.internal.R.styleable.HostApduService_settingsActivity);
mOffHostName = sa.getString(
com.android.internal.R.styleable.OffHostApduService_secureElementName);
+ mDefaultToObserveMode = sa.getBoolean(
+ R.styleable.HostApduService_defaultToObserveMode,
+ false);
if (mOffHostName != null) {
if (mOffHostName.equals("eSE")) {
mOffHostName = "eSE1";
@@ -289,7 +313,6 @@ public final class ApduServiceInfo implements Parcelable {
mStaticAidGroups = new HashMap<String, AidGroup>();
mDynamicAidGroups = new HashMap<String, AidGroup>();
- mPollingLoopFilters = new ArrayList<String>();
mAutoTransact = new HashMap<String, Boolean>();
mOnHost = onHost;
@@ -380,7 +403,6 @@ public final class ApduServiceInfo implements Parcelable {
String plf =
a.getString(com.android.internal.R.styleable.PollingLoopFilter_name)
.toUpperCase(Locale.ROOT);
- mPollingLoopFilters.add(plf);
boolean autoTransact = a.getBoolean(
com.android.internal.R.styleable.PollingLoopFilter_autoTransact,
false);
@@ -448,7 +470,7 @@ public final class ApduServiceInfo implements Parcelable {
@FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
@NonNull
public List<String> getPollingLoopFilters() {
- return mPollingLoopFilters;
+ return new ArrayList<>(mAutoTransact.keySet());
}
/**
@@ -611,6 +633,25 @@ public final class ApduServiceInfo implements Parcelable {
}
/**
+ * Returns whether the NFC stack should default to observe mode when this servise is preferred.
+ * @return whether the NFC stack should default to observe mode when this servise is preferred
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
+ public boolean defaultToObserveMode() {
+ return mDefaultToObserveMode;
+ }
+
+ /**
+ * Sets whether the NFC stack should default to observe mode when this servise is preferred.
+ * @param defaultToObserveMode whether the NFC stack should default to observe mode when this
+ * servise is preferred
+ */
+ @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
+ public void setDefaultToObserveMode(boolean defaultToObserveMode) {
+ mDefaultToObserveMode = defaultToObserveMode;
+ }
+
+ /**
* Returns description of service.
* @return user readable description of service
*/
@@ -640,27 +681,15 @@ public final class ApduServiceInfo implements Parcelable {
/**
* Add a Polling Loop Filter. Custom NFC polling frames that match this filter will be
- * delivered to {@link HostApduService#processPollingFrames(List)}.
- * @param pollingLoopFilter this polling loop filter to add.
+ * delivered to {@link HostApduService#processPollingFrames(List)}. Adding a key with this
+ * multiple times will cause the value to be overwritten each time.
+ * @param pollingLoopFilter the polling loop filter to add, must be a valide hexadecimal string
*/
@FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
- public void addPollingLoopFilter(@NonNull String pollingLoopFilter) {
- mPollingLoopFilters.add(pollingLoopFilter.toUpperCase(Locale.ROOT));
- }
+ public void addPollingLoopFilter(@NonNull String pollingLoopFilter,
+ boolean autoTransact) {
+ mAutoTransact.put(pollingLoopFilter, autoTransact);
- /**
- * Add a Polling Loop Filter. Custom NFC polling frames that match this filter will cause the
- * device to exit observe mode, just as if
- * {@link android.nfc.NfcAdapter#setTransactionAllowed(boolean)} had been called with true,
- * allowing transactions to proceed. The matching frame will also be delivered to
- * {@link HostApduService#processPollingFrames(List)}.
- *
- * @param pollingLoopFilter this polling loop filter to add.
- */
- @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
- public void addPollingLoopFilterToAutoTransact(@NonNull String pollingLoopFilter) {
- mPollingLoopFilters.add(pollingLoopFilter.toUpperCase(Locale.ROOT));
- mAutoTransact.put(pollingLoopFilter.toUpperCase(Locale.ROOT), true);
}
/**
@@ -670,7 +699,7 @@ public final class ApduServiceInfo implements Parcelable {
*/
@FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
public void removePollingLoopFilter(@NonNull String pollingLoopFilter) {
- mPollingLoopFilters.remove(pollingLoopFilter.toUpperCase(Locale.ROOT));
+ mAutoTransact.remove(pollingLoopFilter.toUpperCase(Locale.ROOT));
}
/**
@@ -825,6 +854,8 @@ public final class ApduServiceInfo implements Parcelable {
dest.writeString(mSettingsActivityName);
dest.writeInt(mCategoryOtherServiceEnabled ? 1 : 0);
+ dest.writeInt(mAutoTransact.size());
+ dest.writeMap(mAutoTransact);
};
@FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
@@ -853,10 +884,15 @@ public final class ApduServiceInfo implements Parcelable {
int uid = source.readInt();
String settingsActivityName = source.readString();
boolean isEnabled = source.readInt() != 0;
+ int autoTransactSize = source.readInt();
+ HashMap<String, Boolean> autoTransact =
+ new HashMap<String, Boolean>(autoTransactSize);
+ source.readMap(autoTransact, getClass().getClassLoader(),
+ String.class, Boolean.class);
return new ApduServiceInfo(info, onHost, description, staticAidGroups,
dynamicAidGroups, requiresUnlock, requiresScreenOn, bannerResource, uid,
settingsActivityName, offHostName, staticOffHostName,
- isEnabled);
+ isEnabled, autoTransact);
}
@Override
diff --git a/nfc/java/android/nfc/cardemulation/CardEmulation.java b/nfc/java/android/nfc/cardemulation/CardEmulation.java
index 1f41b812164c..47ddd9de224f 100644
--- a/nfc/java/android/nfc/cardemulation/CardEmulation.java
+++ b/nfc/java/android/nfc/cardemulation/CardEmulation.java
@@ -42,6 +42,7 @@ import android.provider.Settings.SettingNotFoundException;
import android.util.Log;
import java.util.HashMap;
+import java.util.HexFormat;
import java.util.List;
import java.util.regex.Pattern;
@@ -59,7 +60,6 @@ import java.util.regex.Pattern;
*/
public final class CardEmulation {
private static final Pattern AID_PATTERN = Pattern.compile("[0-9A-Fa-f]{10,32}\\*?\\#?");
- private static final Pattern PLF_PATTERN = Pattern.compile("[0-9A-Fa-f]{1,32}");
static final String TAG = "CardEmulation";
@@ -338,19 +338,20 @@ public final class CardEmulation {
}
}
/**
- * Sets whether the system should default to observe mode or not when the service is in the
- * foreground or the default payment service. The default is to not enable observe mode when
- * a service either the foreground default service or the default payment service so not
- * calling this method will preserve that behavior.
+ * Sets whether when this service becomes the preferred service, if the NFC stack
+ * should enable observe mode or disable observe mode. The default is to not enable observe
+ * mode when a service either the foreground default service or the default payment service so
+ * not calling this method will preserve that behavior.
*
* @param service The component name of the service
- * @param enable Whether the servic should default to observe mode or not
+ * @param enable Whether the service should default to observe mode or not
* @return whether the change was successful.
*/
@FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
- public boolean setServiceObserveModeDefault(@NonNull ComponentName service, boolean enable) {
+ public boolean setDefaultToObserveModeForService(@NonNull ComponentName service,
+ boolean enable) {
try {
- return sService.setServiceObserveModeDefault(mContext.getUser().getIdentifier(),
+ return sService.setDefaultToObserveModeForService(mContext.getUser().getIdentifier(),
service, enable);
} catch (RemoteException e) {
Log.e(TAG, "Failed to reach CardEmulationService.");
@@ -359,16 +360,28 @@ public final class CardEmulation {
}
/**
- * Register a polling loop filter for a HostApduService.
- * @param service The HostApduService to register the filter for.
- * @param pollingLoopFilter The filter to register.
+ * Register a polling loop filter (PLF) for a HostApduService and indicate whether it should
+ * auto-transact or not. The PLF can be sequence of an
+ * even number of at least 2 hexadecimal numbers (0-9, A-F or a-f), representing a series of
+ * bytes. When non-standard polling loop frame matches this sequence exactly, it may be
+ * delivered to {@link HostApduService#processPollingFrames(List)}. If auto-transact is set to
+ * true, then observe mode will also be disabled. if this service is currently preferred or
+ * there are no other services registered for this filter.
+ * @param service The HostApduService to register the filter for
+ * @param pollingLoopFilter The filter to register
+ * @param autoTransact true to have the NFC stack automatically disable observe mode and allow
+ * transactions to proceed when this filter matches, false otherwise
+ * @return true if the filter was registered, false otherwise
+ * @throws IllegalArgumentException if the passed in string doesn't parse to at least one byte
*/
@FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
public boolean registerPollingLoopFilterForService(@NonNull ComponentName service,
- @NonNull String pollingLoopFilter) {
+ @NonNull String pollingLoopFilter, boolean autoTransact) {
+ pollingLoopFilter = validatePollingLoopFilter(pollingLoopFilter);
+
try {
return sService.registerPollingLoopFilterForService(mContext.getUser().getIdentifier(),
- service, pollingLoopFilter);
+ service, pollingLoopFilter, autoTransact);
} catch (RemoteException e) {
// Try one more time
recoverService();
@@ -378,7 +391,8 @@ public final class CardEmulation {
}
try {
return sService.registerPollingLoopFilterForService(
- mContext.getUser().getIdentifier(), service, pollingLoopFilter);
+ mContext.getUser().getIdentifier(), service,
+ pollingLoopFilter, autoTransact);
} catch (RemoteException ee) {
Log.e(TAG, "Failed to reach CardEmulationService.");
return false;
@@ -973,15 +987,14 @@ public final class CardEmulation {
* @hide
*/
@FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
- public static boolean isValidPollingLoopFilter(@NonNull String pollingLoopFilter) {
+ public static @NonNull String validatePollingLoopFilter(@NonNull String pollingLoopFilter) {
// Verify hex characters
- if (!PLF_PATTERN.matcher(pollingLoopFilter).matches()) {
- Log.e(TAG, "Polling Loop Filter " + pollingLoopFilter
- + " is not a valid Polling Loop Filter.");
- return false;
+ byte[] plfBytes = HexFormat.of().parseHex(pollingLoopFilter);
+ if (plfBytes.length == 0) {
+ throw new IllegalArgumentException(
+ "Polling loop filter must contain at least one byte.");
}
-
- return true;
+ return HexFormat.of().withUpperCase().formatHex(plfBytes);
}
/**
diff --git a/nfc/java/android/nfc/cardemulation/HostApduService.java b/nfc/java/android/nfc/cardemulation/HostApduService.java
index 89b03226ed46..61037a2e7e9e 100644
--- a/nfc/java/android/nfc/cardemulation/HostApduService.java
+++ b/nfc/java/android/nfc/cardemulation/HostApduService.java
@@ -244,85 +244,6 @@ public abstract class HostApduService extends Service {
public static final String KEY_DATA = "data";
/**
- * KEY_POLLING_LOOP_TYPE is the Bundle key for the type of
- * polling loop frame in the Bundle passed to {@link #processPollingFrames(List)}
- */
- @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
- public static final String KEY_POLLING_LOOP_TYPE = "android.nfc.cardemulation.TYPE";
-
- /**
- * POLLING_LOOP_TYPE_A is the value associated with the key
- * POLLING_LOOP_TYPE in the Bundle passed to {@link #processPollingFrames(List)}
- * when the polling loop is for NFC-A.
- */
- @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
- public static final char POLLING_LOOP_TYPE_A = 'A';
-
- /**
- * POLLING_LOOP_TYPE_B is the value associated with the key
- * POLLING_LOOP_TYPE in the Bundle passed to {@link #processPollingFrames(List)}
- * when the polling loop is for NFC-B.
- */
- @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
- public static final char POLLING_LOOP_TYPE_B = 'B';
-
- /**
- * POLLING_LOOP_TYPE_F is the value associated with the key
- * POLLING_LOOP_TYPE in the Bundle passed to {@link #processPollingFrames(List)}
- * when the polling loop is for NFC-F.
- */
- @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
- public static final char POLLING_LOOP_TYPE_F = 'F';
-
- /**
- * POLLING_LOOP_TYPE_ON is the value associated with the key
- * POLLING_LOOP_TYPE in the Bundle passed to {@link #processPollingFrames(List)}
- * when the polling loop turns on.
- */
- @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
- public static final char POLLING_LOOP_TYPE_ON = 'O';
-
- /**
- * POLLING_LOOP_TYPE_OFF is the value associated with the key
- * POLLING_LOOP_TYPE in the Bundle passed to {@link #processPollingFrames(List)}
- * when the polling loop turns off.
- */
- @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
- public static final char POLLING_LOOP_TYPE_OFF = 'X';
-
- /**
- * POLLING_LOOP_TYPE_UNKNOWN is the value associated with the key
- * POLLING_LOOP_TYPE in the Bundle passed to {@link #processPollingFrames(List)}
- * when the polling loop frame isn't recognized.
- */
- @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
- public static final char POLLING_LOOP_TYPE_UNKNOWN = 'U';
-
- /**
- * KEY_POLLING_LOOP_DATA is the Bundle key for the raw data of captured from
- * the polling loop frame in the Bundle passed to {@link #processPollingFrames(List)}
- * when the frame type isn't recognized.
- */
- @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
- public static final String KEY_POLLING_LOOP_DATA = "android.nfc.cardemulation.DATA";
-
- /**
- * KEY_POLLING_LOOP_GAIN is the Bundle key for the field strength of
- * the polling loop frame in the Bundle passed to {@link #processPollingFrames(List)}
- * when the frame type isn't recognized.
- */
- @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
- public static final String KEY_POLLING_LOOP_GAIN = "android.nfc.cardemulation.GAIN";
-
- /**
- * KEY_POLLING_LOOP_TIMESTAMP is the Bundle key for the timestamp of
- * the polling loop frame in the Bundle passed to {@link #processPollingFrames(List)}
- * when the frame type isn't recognized.
- */
- @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
- public static final String KEY_POLLING_LOOP_TIMESTAMP = "android.nfc.cardemulation.TIMESTAMP";
-
- /**
* @hide
*/
public static final String KEY_POLLING_LOOP_FRAMES_BUNDLE =
@@ -407,7 +328,12 @@ public abstract class HostApduService extends Service {
ArrayList<Bundle> frames =
msg.getData().getParcelableArrayList(KEY_POLLING_LOOP_FRAMES_BUNDLE,
Bundle.class);
- processPollingFrames(frames);
+ ArrayList<PollingFrame> pollingFrames =
+ new ArrayList<PollingFrame>(frames.size());
+ for (Bundle frame : frames) {
+ pollingFrames.add(new PollingFrame(frame));
+ }
+ processPollingFrames(pollingFrames);
break;
default:
super.handleMessage(msg);
@@ -470,7 +396,7 @@ public abstract class HostApduService extends Service {
}
/**
- * This method is called when a polling frame has been received from a
+ * This method is called when polling frames have been received from a
* remote device. If the device is in observe mode, the service should
* call {@link NfcAdapter#allowTransaction()} once it is ready to proceed
* with the transaction. If the device is not in observe mode, the service
@@ -482,7 +408,7 @@ public abstract class HostApduService extends Service {
* @param frame A description of the polling frame.
*/
@FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
- public void processPollingFrames(@NonNull List<Bundle> frame) {
+ public void processPollingFrames(@NonNull List<PollingFrame> frame) {
}
/**
diff --git a/nfc/java/android/nfc/cardemulation/PollingFrame.java b/nfc/java/android/nfc/cardemulation/PollingFrame.java
new file mode 100644
index 000000000000..3383f3bd4e7a
--- /dev/null
+++ b/nfc/java/android/nfc/cardemulation/PollingFrame.java
@@ -0,0 +1,243 @@
+/*
+ * 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.nfc.cardemulation;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HexFormat;
+import java.util.List;
+
+/**
+ * Polling Frames represent data about individual frames of an NFC polling loop. These frames will
+ * be deliverd to subclasses of {@link HostApduService} that have registered filters with
+ * {@link CardEmulation#registerPollingLoopFilterForService(ComponentName, String)} that match a
+ * given frame in a loop and will be delivered through calls to
+ * {@link HostApduService#processPollingFrames(List)}.
+ */
+@FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+public final class PollingFrame implements Parcelable{
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = { "POLLING_LOOP_TYPE_"}, value = { POLLING_LOOP_TYPE_A, POLLING_LOOP_TYPE_B,
+ POLLING_LOOP_TYPE_F, POLLING_LOOP_TYPE_OFF, POLLING_LOOP_TYPE_ON })
+ @Retention(RetentionPolicy.SOURCE)
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public @interface PollingFrameType {}
+
+ /**
+ * POLLING_LOOP_TYPE_A is the value associated with the key
+ * POLLING_LOOP_TYPE in the Bundle passed to {@link HostApduService#processPollingFrames(List)}
+ * when the polling loop is for NFC-A.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final int POLLING_LOOP_TYPE_A = 'A';
+
+ /**
+ * POLLING_LOOP_TYPE_B is the value associated with the key
+ * POLLING_LOOP_TYPE in the Bundle passed to {@link HostApduService#processPollingFrames(List)}
+ * when the polling loop is for NFC-B.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final int POLLING_LOOP_TYPE_B = 'B';
+
+ /**
+ * POLLING_LOOP_TYPE_F is the value associated with the key
+ * POLLING_LOOP_TYPE in the Bundle passed to {@link HostApduService#processPollingFrames(List)}
+ * when the polling loop is for NFC-F.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final int POLLING_LOOP_TYPE_F = 'F';
+
+ /**
+ * POLLING_LOOP_TYPE_ON is the value associated with the key
+ * POLLING_LOOP_TYPE in the Bundle passed to {@link HostApduService#processPollingFrames(List)}
+ * when the polling loop turns on.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final int POLLING_LOOP_TYPE_ON = 'O';
+
+ /**
+ * POLLING_LOOP_TYPE_OFF is the value associated with the key
+ * POLLING_LOOP_TYPE in the Bundle passed to {@link HostApduService#processPollingFrames(List)}
+ * when the polling loop turns off.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final int POLLING_LOOP_TYPE_OFF = 'X';
+
+ /**
+ * POLLING_LOOP_TYPE_UNKNOWN is the value associated with the key
+ * POLLING_LOOP_TYPE in the Bundle passed to {@link HostApduService#processPollingFrames(List)}
+ * when the polling loop frame isn't recognized.
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final int POLLING_LOOP_TYPE_UNKNOWN = 'U';
+
+ /**
+ * KEY_POLLING_LOOP_TYPE is the Bundle key for the type of
+ * polling loop frame in the Bundle included in MSG_POLLING_LOOP.
+ *
+ * @hide
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final String KEY_POLLING_LOOP_TYPE = "android.nfc.cardemulation.TYPE";
+
+ /**
+ * KEY_POLLING_LOOP_DATA is the Bundle key for the raw data of captured from
+ * the polling loop frame in the Bundle included in MSG_POLLING_LOOP.
+ *
+ * @hide
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final String KEY_POLLING_LOOP_DATA = "android.nfc.cardemulation.DATA";
+
+ /**
+ * KEY_POLLING_LOOP_GAIN is the Bundle key for the field strength of
+ * the polling loop frame in the Bundle included in MSG_POLLING_LOOP.
+ *
+ * @hide
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final String KEY_POLLING_LOOP_GAIN = "android.nfc.cardemulation.GAIN";
+
+ /**
+ * KEY_POLLING_LOOP_TIMESTAMP is the Bundle key for the timestamp of
+ * the polling loop frame in the Bundle included in MSG_POLLING_LOOP.
+ *
+ * @hide
+ */
+ @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+ public static final String KEY_POLLING_LOOP_TIMESTAMP = "android.nfc.cardemulation.TIMESTAMP";
+
+
+ @PollingFrameType
+ private final int mType;
+ private final byte[] mData;
+ private final int mGain;
+ private final int mTimestamp;
+
+ public static final @NonNull Parcelable.Creator<PollingFrame> CREATOR =
+ new Parcelable.Creator<>() {
+ @Override
+ public PollingFrame createFromParcel(Parcel source) {
+ return new PollingFrame(source.readBundle());
+ }
+
+ @Override
+ public PollingFrame[] newArray(int size) {
+ return new PollingFrame[size];
+ }
+ };
+
+ PollingFrame(Bundle frame) {
+ mType = frame.getInt(KEY_POLLING_LOOP_TYPE);
+ byte[] data = frame.getByteArray(KEY_POLLING_LOOP_DATA);
+ mData = (data == null) ? new byte[0] : data;
+ mGain = frame.getByte(KEY_POLLING_LOOP_GAIN);
+ mTimestamp = frame.getInt(KEY_POLLING_LOOP_TIMESTAMP);
+ }
+
+ public PollingFrame(@PollingFrameType int type, @Nullable byte[] data,
+ int gain, int timestamp) {
+ mType = type;
+ mData = data == null ? new byte[0] : data;
+ mGain = gain;
+ mTimestamp = timestamp;
+ }
+
+ /**
+ * Returns the type of frame for this polling loop frame.
+ * The possible return values are:
+ * <ul>
+ * <li>{@link POLLING_LOOP_TYPE_ON}</li>
+ * <li>{@link POLLING_LOOP_TYPE_OFF}</li>
+ * <li>{@link POLLING_LOOP_TYPE_A}</li>
+ * <li>{@link POLLING_LOOP_TYPE_B}</li>
+ * <li>{@link POLLING_LOOP_TYPE_F}</li>
+ * </ul>
+ */
+ public @PollingFrameType int getType() {
+ return mType;
+ }
+
+ /**
+ * Returns the raw data from the polling type frame.
+ */
+ public @NonNull byte[] getData() {
+ return mData;
+ }
+
+ /**
+ * Returns the gain representing the field strength of the NFC field when this polling loop
+ * frame was observed.
+ */
+ public int getGain() {
+ return mGain;
+ }
+
+ /**
+ * Returns the timestamp of when the polling loop frame was observed in milliseconds. These
+ * timestamps are relative and not absolute and should only be used fro comparing the timing of
+ * frames relative to each other.
+ * @return the timestamp in milliseconds
+ */
+ public int getTimestamp() {
+ return mTimestamp;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeBundle(toBundle());
+ }
+
+ /**
+ *
+ * @hide
+ * @return a Bundle representing this frame
+ */
+ public Bundle toBundle() {
+ Bundle frame = new Bundle();
+ frame.putInt(KEY_POLLING_LOOP_TYPE, getType());
+ frame.putByte(KEY_POLLING_LOOP_GAIN, (byte) getGain());
+ frame.putByteArray(KEY_POLLING_LOOP_DATA, getData());
+ frame.putInt(KEY_POLLING_LOOP_TIMESTAMP, getTimestamp());
+ return frame;
+ }
+
+ @Override
+ public String toString() {
+ return "PollingFrame { Type: " + (char) getType()
+ + ", gain: " + getGain()
+ + ", timestamp: " + Integer.toUnsignedString(getTimestamp())
+ + ", data: [" + HexFormat.ofDelimiter(" ").formatHex(getData()) + "] }";
+ }
+}
diff --git a/nfc/tests/Android.bp b/nfc/tests/Android.bp
index 62566ee89fb8..6ebc03cc6ffc 100644
--- a/nfc/tests/Android.bp
+++ b/nfc/tests/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_fwk_nfc",
// 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"
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiver.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiver.java
index eccf6047b90c..72f67d93383b 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiver.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiver.java
@@ -494,6 +494,10 @@ public class SlicePurchaseBroadcastReceiver extends BroadcastReceiver{
}
private void onUserCanceled(@NonNull Context context, @NonNull Intent intent) {
+ if (!isIntentValid(intent)) {
+ loge("Ignoring onUserCanceled called with invalid intent.");
+ return;
+ }
int capability = intent.getIntExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
SlicePurchaseController.PREMIUM_CAPABILITY_INVALID);
logd("onUserCanceled: " + TelephonyManager.convertPremiumCapabilityToString(capability));
diff --git a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiverTest.java b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiverTest.java
index 3c8ef6ed0550..8989aab61f1b 100644
--- a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiverTest.java
+++ b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiverTest.java
@@ -262,10 +262,10 @@ public class SlicePurchaseBroadcastReceiverTest {
@Test
public void testNotificationCanceled() {
+ displayPerformanceBoostNotification();
+
// send ACTION_NOTIFICATION_CANCELED
doReturn("com.android.phone.slice.action.NOTIFICATION_CANCELED").when(mIntent).getAction();
- doReturn(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY).when(mIntent).getIntExtra(
- eq(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY), anyInt());
mSlicePurchaseBroadcastReceiver.onReceive(mContext, mIntent);
// verify notification was canceled
@@ -276,7 +276,7 @@ public class SlicePurchaseBroadcastReceiverTest {
}
@Test
- public void testNotificationTimeout() throws Exception {
+ public void testNotificationTimeout() {
displayPerformanceBoostNotification();
// send ACTION_SLICE_PURCHASE_APP_RESPONSE_TIMEOUT
@@ -353,7 +353,7 @@ public class SlicePurchaseBroadcastReceiverTest {
verify(mNotificationManager, never()).notifyAsUser(
eq(SlicePurchaseBroadcastReceiver.PERFORMANCE_BOOST_NOTIFICATION_TAG),
eq(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY),
- any(),
+ any(Notification.class),
eq(UserHandle.ALL));
verify(mNotificationShownIntent, never()).send();
diff --git a/packages/CompanionDeviceManager/res/values-nb/strings.xml b/packages/CompanionDeviceManager/res/values-nb/strings.xml
index d910f2fc9904..bc3ad0615ada 100644
--- a/packages/CompanionDeviceManager/res/values-nb/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-nb/strings.xml
@@ -72,6 +72,6 @@
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Strøm apper og andre systemfunksjoner fra telefonen"</string>
<string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Se en liste over tilgjengelige enheter, og kontroller hvilken enhet som strømmer eller caster lyd eller video fra andre apper"</string>
- <string name="device_type" product="default" msgid="8268703872070046263">"telefon"</string>
- <string name="device_type" product="tablet" msgid="5038791954983067774">"nettbrett"</string>
+ <string name="device_type" product="default" msgid="8268703872070046263">"telefonen"</string>
+ <string name="device_type" product="tablet" msgid="5038791954983067774">"nettbrettet"</string>
</resources>
diff --git a/packages/CompanionDeviceManager/res/values/styles.xml b/packages/CompanionDeviceManager/res/values/styles.xml
index 88f1204641ff..0af108052137 100644
--- a/packages/CompanionDeviceManager/res/values/styles.xml
+++ b/packages/CompanionDeviceManager/res/values/styles.xml
@@ -59,8 +59,10 @@
<style name="VendorHelperBackButton"
parent="@android:style/Widget.Material.Button.Borderless.Colored">
- <item name="android:layout_width">70dp</item>
+ <item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">48dp</item>
+ <item name="android:layout_marginStart">12dp</item>
+ <item name="android:layout_marginEnd">12dp</item>
<item name="android:textAllCaps">false</item>
<item name="android:textSize">14sp</item>
<item name="android:textColor">@android:color/system_neutral1_900</item>
diff --git a/packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java
index b5cf011c32a6..ce8fb6568bd5 100644
--- a/packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java
+++ b/packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java
@@ -100,13 +100,15 @@ public class PackageWatchdog {
public static final int FAILURE_REASON_EXPLICIT_HEALTH_CHECK = 2;
public static final int FAILURE_REASON_APP_CRASH = 3;
public static final int FAILURE_REASON_APP_NOT_RESPONDING = 4;
+ public static final int FAILURE_REASON_BOOT_LOOP = 5;
@IntDef(prefix = { "FAILURE_REASON_" }, value = {
FAILURE_REASON_UNKNOWN,
FAILURE_REASON_NATIVE_CRASH,
FAILURE_REASON_EXPLICIT_HEALTH_CHECK,
FAILURE_REASON_APP_CRASH,
- FAILURE_REASON_APP_NOT_RESPONDING
+ FAILURE_REASON_APP_NOT_RESPONDING,
+ FAILURE_REASON_BOOT_LOOP
})
@Retention(RetentionPolicy.SOURCE)
public @interface FailureReasons {}
@@ -542,7 +544,7 @@ public class PackageWatchdog {
mNumberOfNativeCrashPollsRemaining--;
// Check if native watchdog reported a crash
if ("1".equals(SystemProperties.get("sys.init.updatable_crashing"))) {
- // We rollback everything available when crash is unattributable
+ // We rollback all available low impact rollbacks when crash is unattributable
onPackageFailure(Collections.EMPTY_LIST, FAILURE_REASON_NATIVE_CRASH);
// we stop polling after an attempt to execute rollback, regardless of whether the
// attempt succeeds or not
@@ -572,6 +574,7 @@ public class PackageWatchdog {
PackageHealthObserverImpact.USER_IMPACT_LEVEL_30,
PackageHealthObserverImpact.USER_IMPACT_LEVEL_50,
PackageHealthObserverImpact.USER_IMPACT_LEVEL_70,
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_90,
PackageHealthObserverImpact.USER_IMPACT_LEVEL_100})
public @interface PackageHealthObserverImpact {
/** No action to take. */
@@ -582,6 +585,7 @@ public class PackageWatchdog {
int USER_IMPACT_LEVEL_30 = 30;
int USER_IMPACT_LEVEL_50 = 50;
int USER_IMPACT_LEVEL_70 = 70;
+ int USER_IMPACT_LEVEL_90 = 90;
/* Action has high user impact, a last resort, user of a device will be very frustrated. */
int USER_IMPACT_LEVEL_100 = 100;
}
diff --git a/packages/CrashRecovery/services/java/com/android/server/RescueParty.java b/packages/CrashRecovery/services/java/com/android/server/RescueParty.java
index 6766afc5e45a..9217e7012e7e 100644
--- a/packages/CrashRecovery/services/java/com/android/server/RescueParty.java
+++ b/packages/CrashRecovery/services/java/com/android/server/RescueParty.java
@@ -40,7 +40,6 @@ import android.provider.Settings;
import android.sysprop.CrashRecoveryProperties;
import android.text.TextUtils;
import android.util.ArraySet;
-import android.util.ExceptionUtils;
import android.util.Log;
import android.util.Slog;
@@ -76,6 +75,8 @@ import java.util.concurrent.TimeUnit;
*/
public class RescueParty {
@VisibleForTesting
+ static final String PROP_ENABLE_RESCUE = "persist.sys.enable_rescue";
+ @VisibleForTesting
static final int LEVEL_NONE = 0;
@VisibleForTesting
static final int LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS = 1;
@@ -124,7 +125,7 @@ public class RescueParty {
private static boolean isDisabled() {
// Check if we're explicitly enabled for testing
- if (CrashRecoveryProperties.enableRescueParty().orElse(false)) {
+ if (SystemProperties.getBoolean(PROP_ENABLE_RESCUE, false)) {
return false;
}
@@ -136,7 +137,7 @@ public class RescueParty {
}
// We're disabled on all engineering devices
- if (Build.IS_ENG) {
+ if (Build.TYPE.equals("eng")) {
Slog.v(TAG, "Disabled because of eng build");
return true;
}
@@ -144,7 +145,7 @@ public class RescueParty {
// We're disabled on userdebug devices connected over USB, since that's
// a decent signal that someone is actively trying to debug the device,
// or that it's in a lab environment.
- if (Build.IS_USERDEBUG && isUsbActive()) {
+ if (Build.TYPE.equals("userdebug") && isUsbActive()) {
Slog.v(TAG, "Disabled because of active USB connection");
return true;
}
@@ -177,6 +178,29 @@ public class RescueParty {
return CrashRecoveryProperties.attemptingReboot().orElse(false);
}
+ protected static long getLastFactoryResetTimeMs() {
+ return CrashRecoveryProperties.lastFactoryResetTimeMs().orElse(0L);
+ }
+
+ protected static int getMaxRescueLevelAttempted() {
+ return CrashRecoveryProperties.maxRescueLevelAttempted().orElse(LEVEL_NONE);
+ }
+
+ protected static void setFactoryResetProperty(boolean value) {
+ CrashRecoveryProperties.attemptingFactoryReset(value);
+ }
+ protected static void setRebootProperty(boolean value) {
+ CrashRecoveryProperties.attemptingReboot(value);
+ }
+
+ protected static void setLastFactoryResetTimeMs(long value) {
+ CrashRecoveryProperties.lastFactoryResetTimeMs(value);
+ }
+
+ protected static void setMaxRescueLevelAttempted(int level) {
+ CrashRecoveryProperties.maxRescueLevelAttempted(level);
+ }
+
/**
* Called when {@code SettingsProvider} has been published, which is a good
* opportunity to reset any settings depending on our rescue level.
@@ -433,7 +457,7 @@ public class RescueParty {
case LEVEL_WARM_REBOOT:
// Request the reboot from a separate thread to avoid deadlock on PackageWatchdog
// when device shutting down.
- CrashRecoveryProperties.attemptingReboot(true);
+ setRebootProperty(true);
runnable = () -> {
try {
PowerManager pm = context.getSystemService(PowerManager.class);
@@ -455,9 +479,9 @@ public class RescueParty {
if (isRebootPropertySet()) {
break;
}
- CrashRecoveryProperties.attemptingFactoryReset(true);
+ setFactoryResetProperty(true);
long now = System.currentTimeMillis();
- CrashRecoveryProperties.lastFactoryResetTimeMs(now);
+ setLastFactoryResetTimeMs(now);
runnable = new Runnable() {
@Override
public void run() {
@@ -478,9 +502,18 @@ public class RescueParty {
}
}
+ private static String getCompleteMessage(Throwable t) {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(t.getMessage());
+ while ((t = t.getCause()) != null) {
+ builder.append(": ").append(t.getMessage());
+ }
+ return builder.toString();
+ }
+
private static void logRescueException(int level, @Nullable String failedPackageName,
Throwable t) {
- final String msg = ExceptionUtils.getCompleteMessage(t);
+ final String msg = getCompleteMessage(t);
EventLogTags.writeRescueFailure(level, msg);
String failureMsg = "Failed rescue level " + levelToString(level);
if (!TextUtils.isEmpty(failedPackageName)) {
@@ -507,10 +540,10 @@ public class RescueParty {
private static void resetAllSettingsIfNecessary(Context context, int mode,
int level) throws Exception {
// No need to reset Settings again if they are already reset in the current level once.
- if (CrashRecoveryProperties.maxRescueLevelAttempted().orElse(LEVEL_NONE) >= level) {
+ if (getMaxRescueLevelAttempted() >= level) {
return;
}
- CrashRecoveryProperties.maxRescueLevelAttempted(level);
+ setMaxRescueLevelAttempted(level);
// Try our best to reset all settings possible, and once finished
// rethrow any exception that we encountered
Exception res = null;
@@ -725,7 +758,7 @@ public class RescueParty {
* Will return {@code false} if a factory reset was already offered recently.
*/
private boolean shouldThrottleReboot() {
- Long lastResetTime = CrashRecoveryProperties.lastFactoryResetTimeMs().orElse(0L);
+ Long lastResetTime = getLastFactoryResetTimeMs();
long now = System.currentTimeMillis();
long throttleDurationMin = SystemProperties.getLong(PROP_THROTTLE_DURATION_MIN_FLAG,
DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN);
diff --git a/packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index dd74a2a978b2..5fb47dd9b95a 100644
--- a/packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -28,6 +28,7 @@ import android.content.pm.VersionedPackage;
import android.content.rollback.PackageRollbackInfo;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
+import android.crashrecovery.flags.Flags;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Handler;
@@ -45,7 +46,6 @@ import com.android.server.PackageWatchdog;
import com.android.server.PackageWatchdog.FailureReasons;
import com.android.server.PackageWatchdog.PackageHealthObserver;
import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
-import com.android.server.SystemConfig;
import com.android.server.crashrecovery.proto.CrashRecoveryStatsLog;
import com.android.server.pm.ApexManager;
@@ -57,6 +57,7 @@ import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
@@ -84,7 +85,8 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
// True if needing to roll back only rebootless apexes when native crash happens
private boolean mTwoPhaseRollbackEnabled;
- RollbackPackageHealthObserver(Context context) {
+ @VisibleForTesting
+ RollbackPackageHealthObserver(Context context, ApexManager apexManager) {
mContext = context;
HandlerThread handlerThread = new HandlerThread("RollbackPackageHealthObserver");
handlerThread.start();
@@ -94,7 +96,7 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
mLastStagedRollbackIdsFile = new File(dataDir, "last-staged-rollback-ids");
mTwoPhaseRollbackEnabledFile = new File(dataDir, "two-phase-rollback-enabled");
PackageWatchdog.getInstance(mContext).registerHealthObserver(this);
- mApexManager = ApexManager.getInstance();
+ mApexManager = apexManager;
if (SystemProperties.getBoolean("sys.boot_completed", false)) {
// Load the value from the file if system server has crashed and restarted
@@ -107,24 +109,46 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
}
}
+ RollbackPackageHealthObserver(Context context) {
+ this(context, ApexManager.getInstance());
+ }
+
@Override
public int onHealthCheckFailed(@Nullable VersionedPackage failedPackage,
@FailureReasons int failureReason, int mitigationCount) {
- boolean anyRollbackAvailable = !mContext.getSystemService(RollbackManager.class)
- .getAvailableRollbacks().isEmpty();
int impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
-
- if (failureReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH
- && anyRollbackAvailable) {
- // For native crashes, we will directly roll back any available rollbacks
- // Note: For non-native crashes the rollback-all step has higher impact
- impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30;
- } else if (getAvailableRollback(failedPackage) != null) {
- // Rollback is available, we may get a callback into #execute
- impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30;
- } else if (anyRollbackAvailable) {
- // If any rollbacks are available, we will commit them
- impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_70;
+ if (Flags.recoverabilityDetection()) {
+ List<RollbackInfo> availableRollbacks = getAvailableRollbacks();
+ List<RollbackInfo> lowImpactRollbacks = getRollbacksAvailableForImpactLevel(
+ availableRollbacks, PackageManager.ROLLBACK_USER_IMPACT_LOW);
+ if (!lowImpactRollbacks.isEmpty()) {
+ if (failureReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) {
+ // For native crashes, we will directly roll back any available rollbacks at low
+ // impact level
+ impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30;
+ } else if (getRollbackForPackage(failedPackage, lowImpactRollbacks) != null) {
+ // Rollback is available for crashing low impact package
+ impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30;
+ } else {
+ impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_70;
+ }
+ }
+ } else {
+ boolean anyRollbackAvailable = !mContext.getSystemService(RollbackManager.class)
+ .getAvailableRollbacks().isEmpty();
+
+ if (failureReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH
+ && anyRollbackAvailable) {
+ // For native crashes, we will directly roll back any available rollbacks
+ // Note: For non-native crashes the rollback-all step has higher impact
+ impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30;
+ } else if (getAvailableRollback(failedPackage) != null) {
+ // Rollback is available, we may get a callback into #execute
+ impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30;
+ } else if (anyRollbackAvailable) {
+ // If any rollbacks are available, we will commit them
+ impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_70;
+ }
}
return impact;
@@ -133,16 +157,34 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
@Override
public boolean execute(@Nullable VersionedPackage failedPackage,
@FailureReasons int rollbackReason, int mitigationCount) {
- if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) {
- mHandler.post(() -> rollbackAll(rollbackReason));
- return true;
- }
+ if (Flags.recoverabilityDetection()) {
+ List<RollbackInfo> availableRollbacks = getAvailableRollbacks();
+ if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) {
+ mHandler.post(() -> rollbackAllLowImpact(availableRollbacks, rollbackReason));
+ return true;
+ }
- RollbackInfo rollback = getAvailableRollback(failedPackage);
- if (rollback != null) {
- mHandler.post(() -> rollbackPackage(rollback, failedPackage, rollbackReason));
+ List<RollbackInfo> lowImpactRollbacks = getRollbacksAvailableForImpactLevel(
+ availableRollbacks, PackageManager.ROLLBACK_USER_IMPACT_LOW);
+ RollbackInfo rollback = getRollbackForPackage(failedPackage, lowImpactRollbacks);
+ if (rollback != null) {
+ mHandler.post(() -> rollbackPackage(rollback, failedPackage, rollbackReason));
+ } else if (!lowImpactRollbacks.isEmpty()) {
+ // Apply all available low impact rollbacks.
+ mHandler.post(() -> rollbackAllLowImpact(availableRollbacks, rollbackReason));
+ }
} else {
- mHandler.post(() -> rollbackAll(rollbackReason));
+ if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) {
+ mHandler.post(() -> rollbackAll(rollbackReason));
+ return true;
+ }
+
+ RollbackInfo rollback = getAvailableRollback(failedPackage);
+ if (rollback != null) {
+ mHandler.post(() -> rollbackPackage(rollback, failedPackage, rollbackReason));
+ } else {
+ mHandler.post(() -> rollbackAll(rollbackReason));
+ }
}
// Assume rollbacks executed successfully
@@ -150,6 +192,31 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
}
@Override
+ public int onBootLoop(int mitigationCount) {
+ int impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
+ if (Flags.recoverabilityDetection()) {
+ List<RollbackInfo> availableRollbacks = getAvailableRollbacks();
+ if (!availableRollbacks.isEmpty()) {
+ impact = getUserImpactBasedOnRollbackImpactLevel(availableRollbacks);
+ }
+ }
+ return impact;
+ }
+
+ @Override
+ public boolean executeBootLoopMitigation(int mitigationCount) {
+ if (Flags.recoverabilityDetection()) {
+ List<RollbackInfo> availableRollbacks = getAvailableRollbacks();
+
+ triggerLeastImpactLevelRollback(availableRollbacks,
+ PackageWatchdog.FAILURE_REASON_BOOT_LOOP);
+ return true;
+ }
+ return false;
+ }
+
+
+ @Override
public String getName() {
return NAME;
}
@@ -161,13 +228,16 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
@Override
public boolean mayObservePackage(String packageName) {
- if (mContext.getSystemService(RollbackManager.class)
- .getAvailableRollbacks().isEmpty()) {
+ if (getAvailableRollbacks().isEmpty()) {
return false;
}
return isPersistentSystemApp(packageName);
}
+ private List<RollbackInfo> getAvailableRollbacks() {
+ return mContext.getSystemService(RollbackManager.class).getAvailableRollbacks();
+ }
+
private boolean isPersistentSystemApp(@NonNull String packageName) {
PackageManager pm = mContext.getPackageManager();
try {
@@ -272,6 +342,40 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
return null;
}
+ @AnyThread
+ private RollbackInfo getRollbackForPackage(@Nullable VersionedPackage failedPackage,
+ List<RollbackInfo> availableRollbacks) {
+ if (failedPackage == null) {
+ return null;
+ }
+
+ for (RollbackInfo rollback : availableRollbacks) {
+ for (PackageRollbackInfo packageRollback : rollback.getPackages()) {
+ if (packageRollback.getVersionRolledBackFrom().equals(failedPackage)) {
+ return rollback;
+ }
+ // TODO(b/147666157): Extract version number of apk-in-apex so that we don't have
+ // to rely on complicated reasoning as below
+
+ // Due to b/147666157, for apk in apex, we do not know the version we are rolling
+ // back from. But if a package X is embedded in apex A exclusively (not embedded in
+ // any other apex), which is not guaranteed, then it is sufficient to check only
+ // package names here, as the version of failedPackage and the PackageRollbackInfo
+ // can't be different. If failedPackage has a higher version, then it must have
+ // been updated somehow. There are two ways: it was updated by an update of apex A
+ // or updated directly as apk. In both cases, this rollback would have gotten
+ // expired when onPackageReplaced() was called. Since the rollback exists, it has
+ // same version as failedPackage.
+ if (packageRollback.isApkInApex()
+ && packageRollback.getVersionRolledBackFrom().getPackageName()
+ .equals(failedPackage.getPackageName())) {
+ return rollback;
+ }
+ }
+ }
+ return null;
+ }
+
/**
* Returns {@code true} if staged session associated with {@code rollbackId} was marked
* as handled, {@code false} if already handled.
@@ -396,12 +500,6 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
@FailureReasons int rollbackReason) {
assertInWorkerThread();
- if (isAutomaticRollbackDenied(SystemConfig.getInstance(), failedPackage)) {
- Slog.d(TAG, "Automatic rollback not allowed for package "
- + failedPackage.getPackageName());
- return;
- }
-
final RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
int reasonToLog = WatchdogRollbackLogger.mapFailureReasonToMetric(rollbackReason);
final String failedPackageToLog;
@@ -465,17 +563,6 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
}
/**
- * Returns true if this package is not eligible for automatic rollback.
- */
- @VisibleForTesting
- @AnyThread
- public static boolean isAutomaticRollbackDenied(SystemConfig systemConfig,
- VersionedPackage versionedPackage) {
- return systemConfig.getAutomaticRollbackDenylistedPackages()
- .contains(versionedPackage.getPackageName());
- }
-
- /**
* Two-phase rollback:
* 1. roll back rebootless apexes first
* 2. roll back all remaining rollbacks if native crash doesn't stop after (1) is done
@@ -495,14 +582,62 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
boolean found = false;
for (RollbackInfo rollback : rollbacks) {
if (isRebootlessApex(rollback)) {
- VersionedPackage sample = rollback.getPackages().get(0).getVersionRolledBackFrom();
- rollbackPackage(rollback, sample, PackageWatchdog.FAILURE_REASON_NATIVE_CRASH);
+ VersionedPackage firstRollback =
+ rollback.getPackages().get(0).getVersionRolledBackFrom();
+ rollbackPackage(rollback, firstRollback,
+ PackageWatchdog.FAILURE_REASON_NATIVE_CRASH);
found = true;
}
}
return found;
}
+ /**
+ * Rollback the package that has minimum rollback impact level.
+ * @param availableRollbacks all available rollbacks
+ * @param rollbackReason reason to rollback
+ */
+ private void triggerLeastImpactLevelRollback(List<RollbackInfo> availableRollbacks,
+ @FailureReasons int rollbackReason) {
+ int minRollbackImpactLevel = getMinRollbackImpactLevel(availableRollbacks);
+
+ if (minRollbackImpactLevel == PackageManager.ROLLBACK_USER_IMPACT_LOW) {
+ // Apply all available low impact rollbacks.
+ mHandler.post(() -> rollbackAllLowImpact(availableRollbacks, rollbackReason));
+ } else if (minRollbackImpactLevel == PackageManager.ROLLBACK_USER_IMPACT_HIGH) {
+ // Rollback one package at a time. If that doesn't resolve the issue, rollback
+ // next with same impact level.
+ mHandler.post(() -> rollbackHighImpact(availableRollbacks, rollbackReason));
+ }
+ }
+
+ /**
+ * sort the available high impact rollbacks by first package name to have a deterministic order.
+ * Apply the first available rollback.
+ * @param availableRollbacks all available rollbacks
+ * @param rollbackReason reason to rollback
+ */
+ @WorkerThread
+ private void rollbackHighImpact(List<RollbackInfo> availableRollbacks,
+ @FailureReasons int rollbackReason) {
+ assertInWorkerThread();
+ List<RollbackInfo> highImpactRollbacks =
+ getRollbacksAvailableForImpactLevel(
+ availableRollbacks, PackageManager.ROLLBACK_USER_IMPACT_HIGH);
+
+ // sort rollbacks based on package name of the first package. This is to have a
+ // deterministic order of rollbacks.
+ List<RollbackInfo> sortedHighImpactRollbacks = highImpactRollbacks.stream().sorted(
+ Comparator.comparing(a -> a.getPackages().get(0).getPackageName())).toList();
+ VersionedPackage firstRollback =
+ sortedHighImpactRollbacks
+ .get(0)
+ .getPackages()
+ .get(0)
+ .getVersionRolledBackFrom();
+ rollbackPackage(sortedHighImpactRollbacks.get(0), firstRollback, rollbackReason);
+ }
+
@WorkerThread
private void rollbackAll(@FailureReasons int rollbackReason) {
assertInWorkerThread();
@@ -522,8 +657,77 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver {
}
for (RollbackInfo rollback : rollbacks) {
- VersionedPackage sample = rollback.getPackages().get(0).getVersionRolledBackFrom();
- rollbackPackage(rollback, sample, rollbackReason);
+ VersionedPackage firstRollback =
+ rollback.getPackages().get(0).getVersionRolledBackFrom();
+ rollbackPackage(rollback, firstRollback, rollbackReason);
}
}
+
+ /**
+ * Rollback all available low impact rollbacks
+ * @param availableRollbacks all available rollbacks
+ * @param rollbackReason reason to rollbacks
+ */
+ @WorkerThread
+ private void rollbackAllLowImpact(
+ List<RollbackInfo> availableRollbacks, @FailureReasons int rollbackReason) {
+ assertInWorkerThread();
+
+ List<RollbackInfo> lowImpactRollbacks = getRollbacksAvailableForImpactLevel(
+ availableRollbacks,
+ PackageManager.ROLLBACK_USER_IMPACT_LOW);
+ if (useTwoPhaseRollback(lowImpactRollbacks)) {
+ return;
+ }
+
+ Slog.i(TAG, "Rolling back all available low impact rollbacks");
+ // Add all rollback ids to mPendingStagedRollbackIds, so that we do not reboot before all
+ // pending staged rollbacks are handled.
+ for (RollbackInfo rollback : lowImpactRollbacks) {
+ if (rollback.isStaged()) {
+ mPendingStagedRollbackIds.add(rollback.getRollbackId());
+ }
+ }
+
+ for (RollbackInfo rollback : lowImpactRollbacks) {
+ VersionedPackage firstRollback =
+ rollback.getPackages().get(0).getVersionRolledBackFrom();
+ rollbackPackage(rollback, firstRollback, rollbackReason);
+ }
+ }
+
+ private List<RollbackInfo> getRollbacksAvailableForImpactLevel(
+ List<RollbackInfo> availableRollbacks, int impactLevel) {
+ return availableRollbacks.stream()
+ .filter(rollbackInfo -> rollbackInfo.getRollbackImpactLevel() == impactLevel)
+ .toList();
+ }
+
+ private int getMinRollbackImpactLevel(List<RollbackInfo> availableRollbacks) {
+ return availableRollbacks.stream()
+ .mapToInt(RollbackInfo::getRollbackImpactLevel)
+ .min()
+ .orElse(-1);
+ }
+
+ private int getUserImpactBasedOnRollbackImpactLevel(List<RollbackInfo> availableRollbacks) {
+ int impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
+ int minImpact = getMinRollbackImpactLevel(availableRollbacks);
+ switch (minImpact) {
+ case PackageManager.ROLLBACK_USER_IMPACT_LOW:
+ impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_70;
+ break;
+ case PackageManager.ROLLBACK_USER_IMPACT_HIGH:
+ impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_90;
+ break;
+ default:
+ impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
+ }
+ return impact;
+ }
+
+ @VisibleForTesting
+ Handler getHandler() {
+ return mHandler;
+ }
}
diff --git a/packages/CrashRecovery/services/java/com/android/server/rollback/WatchdogRollbackLogger.java b/packages/CrashRecovery/services/java/com/android/server/rollback/WatchdogRollbackLogger.java
index 898c5439a293..519c0edfc532 100644
--- a/packages/CrashRecovery/services/java/com/android/server/rollback/WatchdogRollbackLogger.java
+++ b/packages/CrashRecovery/services/java/com/android/server/rollback/WatchdogRollbackLogger.java
@@ -18,6 +18,7 @@ package com.android.server.rollback;
import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH;
import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING;
+import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_BOOT_LOOPING;
import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_EXPLICIT_HEALTH_CHECK;
import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH;
import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH_DURING_BOOT;
@@ -258,6 +259,8 @@ public final class WatchdogRollbackLogger {
return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH;
case PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING:
return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING;
+ case PackageWatchdog.FAILURE_REASON_BOOT_LOOP:
+ return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_BOOT_LOOPING;
default:
return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN;
}
diff --git a/packages/CredentialManager/res/values-kk/strings.xml b/packages/CredentialManager/res/values-kk/strings.xml
index 4f5196d017e6..79fe5ce096a6 100644
--- a/packages/CredentialManager/res/values-kk/strings.xml
+++ b/packages/CredentialManager/res/values-kk/strings.xml
@@ -92,8 +92,6 @@
<string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Басқа құрылғыдан жасау"</string>
<string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Басқа құрылғыны пайдалану"</string>
<string name="request_cancelled_by" msgid="3735222326886267820">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасы сұрауды тоқтатты."</string>
- <!-- no translation found for dropdown_presentation_more_sign_in_options_text (1693727354272417902) -->
- <skip />
- <!-- no translation found for more_options_content_description (1323427365788198808) -->
- <skip />
+ <string name="dropdown_presentation_more_sign_in_options_text" msgid="1693727354272417902">"Кіру опциялары"</string>
+ <string name="more_options_content_description" msgid="1323427365788198808">"Жаю"</string>
</resources>
diff --git a/packages/CredentialManager/res/values/colors.xml b/packages/CredentialManager/res/values/colors.xml
index 7cb1d01972b7..b4d2eeb3bd0f 100644
--- a/packages/CredentialManager/res/values/colors.xml
+++ b/packages/CredentialManager/res/values/colors.xml
@@ -22,4 +22,7 @@
<color name="dropdown_container">#F3F3FA</color>
<color name="sign_in_options_container">#DADADA</color>
<color name="sign_in_options_icon_color">#1B1B1B</color>
+
+ <!-- These colors are used for Inline Suggestions. -->
+ <color name="inline_background">#FFFFFF</color>
</resources> \ No newline at end of file
diff --git a/packages/CredentialManager/res/values/dimens.xml b/packages/CredentialManager/res/values/dimens.xml
index b47a4dc2b76f..350920b23c69 100644
--- a/packages/CredentialManager/res/values/dimens.xml
+++ b/packages/CredentialManager/res/values/dimens.xml
@@ -28,4 +28,6 @@
<dimen name="dropdown_layout_horizontal_margin">24dp</dimen>
<integer name="autofill_max_visible_datasets">5</integer>
<dimen name="dropdown_touch_target_min_height">48dp</dimen>
+ <dimen name="horizontal_chip_padding">8dp</dimen>
+ <dimen name="vertical_chip_padding">6dp</dimen>
</resources> \ No newline at end of file
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/client/CredentialManagerClient.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/client/CredentialManagerClient.kt
index 3fbff37e0416..6902b6fcc69d 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/client/CredentialManagerClient.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/client/CredentialManagerClient.kt
@@ -19,6 +19,7 @@ package com.android.credentialmanager.client
import android.content.Intent
import android.credentials.selection.BaseDialogResult
import android.credentials.selection.UserSelectionDialogResult
+import com.android.credentialmanager.model.EntryInfo
import com.android.credentialmanager.model.Request
import kotlinx.coroutines.flow.StateFlow
@@ -30,10 +31,7 @@ interface CredentialManagerClient {
fun updateRequest(intent: Intent)
/** Sends an error encountered during the UI. */
- fun sendError(
- @BaseDialogResult.ResultCode resultCode: Int,
- errorMessage: String? = null,
- )
+ fun sendError(@BaseDialogResult.ResultCode resultCode: Int)
/**
* Sends a response to the system service. The response
@@ -54,4 +52,20 @@ interface CredentialManagerClient {
* @throws [IllegalStateException] if [requests] is not [Request.Get].
*/
fun sendResult(result: UserSelectionDialogResult)
+
+ /**
+ * Sends a response to the system service with a selected [EntryInfo].
+ *
+ * @return if the current [Request.Get] flow can be ended peacefully.
+ * if not, App has to keep reacting to the further update from [requests] until [Request.Cancel]
+ * or [Request.Close] is received.
+ *
+ * @throws [IllegalStateException] if [requests] is not [Request.Get].
+ */
+ fun sendEntrySelectionResult(
+ entryInfo: EntryInfo,
+ resultCode: Int? = null,
+ resultData: Intent? = null,
+ isAutoSelected: Boolean = false,
+ ): Boolean
} \ No newline at end of file
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt
index ec1f052839e4..ab70394057f3 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/client/impl/CredentialManagerClientImpl.kt
@@ -16,16 +16,24 @@
package com.android.credentialmanager.client.impl
+import android.app.Activity
import android.content.Context
import android.content.Intent
import android.credentials.selection.BaseDialogResult
+import android.credentials.selection.BaseDialogResult.RESULT_CODE_DIALOG_USER_CANCELED
+import android.credentials.selection.Constants
+import android.credentials.selection.ProviderPendingIntentResponse
import android.credentials.selection.UserSelectionDialogResult
import android.os.Bundle
+import android.os.IBinder
+import android.os.ResultReceiver
import android.util.Log
import com.android.credentialmanager.TAG
import com.android.credentialmanager.model.Request
import com.android.credentialmanager.parse
import com.android.credentialmanager.client.CredentialManagerClient
+import com.android.credentialmanager.model.EntryInfo
+
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -40,9 +48,13 @@ class CredentialManagerClientImpl @Inject constructor(
override fun updateRequest(intent: Intent) {
- val request = intent.parse(
- context = context,
- )
+ val request: Request
+ try {
+ request = intent.parse(context)
+ } catch (e: Exception) {
+ sendError(BaseDialogResult.RESULT_CODE_DATA_PARSING_FAILURE)
+ return
+ }
Log.d(TAG, "Request parsed: $request, client instance: $this")
if (request is Request.Cancel || request is Request.Close) {
if (request.token != null && request.token != _requests.value?.token) {
@@ -53,8 +65,9 @@ class CredentialManagerClientImpl @Inject constructor(
_requests.value = request
}
- override fun sendError(resultCode: Int, errorMessage: String?) {
- TODO("b/300422310 - [Wear] Implement UI for cancellation request with message")
+ override fun sendError(resultCode: Int) {
+ Log.w(TAG, "Error occurred, resultCode: $resultCode, current request: ${ requests.value }")
+ requests.value?.sendCancellationCode(resultCode)
}
override fun sendResult(result: UserSelectionDialogResult) {
@@ -69,4 +82,58 @@ class CredentialManagerClientImpl @Inject constructor(
)
}
}
+
+ override fun sendEntrySelectionResult(
+ entryInfo: EntryInfo,
+ resultCode: Int?,
+ resultData: Intent?,
+ isAutoSelected: Boolean,
+ ): Boolean {
+ Log.d(TAG, "sendEntrySelectionResult, resultCode: $resultCode, resultData: $resultData," +
+ " entryInfo: $entryInfo")
+ val currentRequest = requests.value
+ check(currentRequest is Request.Get) { "current request is not get." }
+ if (resultCode == Activity.RESULT_CANCELED) {
+ if (isAutoSelected) {
+ currentRequest.sendCancellationCode(RESULT_CODE_DIALOG_USER_CANCELED)
+ }
+ return isAutoSelected
+ }
+ val userSelectionDialogResult = UserSelectionDialogResult(
+ currentRequest.token,
+ entryInfo.providerId,
+ entryInfo.entryKey,
+ entryInfo.entrySubkey,
+ if (resultCode != null) ProviderPendingIntentResponse(
+ resultCode,
+ resultData
+ ) else null
+ )
+ sendResult(userSelectionDialogResult)
+ return entryInfo.shouldTerminateUiUponSuccessfulProviderResult
+ }
+
+ private fun Request.sendCancellationCode(cancelCode: Int) {
+ sendCancellationCode(
+ cancelCode = cancelCode,
+ requestToken = token,
+ resultReceiver = resultReceiver,
+ finalResponseReceiver = finalResponseReceiver
+ )
+ }
+
+ private fun sendCancellationCode(
+ cancelCode: Int,
+ requestToken: IBinder?,
+ resultReceiver: ResultReceiver?,
+ finalResponseReceiver: ResultReceiver?
+ ) {
+ if (requestToken != null && resultReceiver != null) {
+ val resultData = Bundle().apply {
+ putParcelable(Constants.EXTRA_FINAL_RESPONSE_RECEIVER, finalResponseReceiver)
+ }
+ BaseDialogResult.addToBundle(BaseDialogResult(requestToken), resultData)
+ resultReceiver.send(cancelCode, resultData)
+ }
+ }
}
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt
index 9242141cfd63..786c441bb2e3 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt
@@ -54,3 +54,9 @@ val Intent.resultReceiver: ResultReceiver?
Constants.EXTRA_RESULT_RECEIVER,
ResultReceiver::class.java
)
+
+val Intent.finalResponseReceiver: ResultReceiver?
+ get() = this.getParcelableExtra(
+ Constants.EXTRA_FINAL_RESPONSE_RECEIVER,
+ ResultReceiver::class.java
+ )
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
index f1f1f7ca842e..1683cc43eef1 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
@@ -20,6 +20,7 @@ import android.content.Context
import android.content.Intent
import com.android.credentialmanager.ktx.getCredentialProviderDataList
import com.android.credentialmanager.ktx.requestInfo
+import com.android.credentialmanager.ktx.finalResponseReceiver
import com.android.credentialmanager.ktx.resultReceiver
import com.android.credentialmanager.ktx.toProviderList
import com.android.credentialmanager.model.Request
@@ -28,6 +29,7 @@ fun Intent.toGet(context: Context): Request.Get {
return Request.Get(
token = requestInfo?.token,
resultReceiver = resultReceiver,
+ finalResponseReceiver = finalResponseReceiver,
providerInfos = getCredentialProviderDataList.toProviderList(context)
)
}
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
index 763646233324..fd99275ebc8e 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
@@ -25,6 +25,8 @@ import com.android.credentialmanager.model.get.ProviderInfo
*/
sealed class Request private constructor(
open val token: IBinder?,
+ open val resultReceiver: ResultReceiver? = null,
+ open val finalResponseReceiver: ResultReceiver? = null,
) {
/**
@@ -48,9 +50,10 @@ sealed class Request private constructor(
*/
data class Get(
override val token: IBinder?,
- val resultReceiver: ResultReceiver?,
+ override val resultReceiver: ResultReceiver?,
+ override val finalResponseReceiver: ResultReceiver?,
val providerInfos: List<ProviderInfo>,
- ) : Request(token)
+ ) : Request(token, resultReceiver, finalResponseReceiver)
/**
* Request to start the create credentials flow.
*/
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index 30973879de10..879d64c761ec 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -50,7 +50,6 @@ import com.android.credentialmanager.createflow.isFlowAutoSelectable
class CredentialManagerRepo(
private val context: Context,
intent: Intent,
- userConfigRepo: UserConfigRepo,
isNewActivity: Boolean,
) {
val requestInfo: RequestInfo?
@@ -111,11 +110,7 @@ class CredentialManagerRepo(
ResultReceiver::class.java
)
- isReqForAllOptions = intent.getBooleanExtra(
- Constants.EXTRA_REQ_FOR_ALL_OPTIONS,
- /*defaultValue=*/ false
- ) || (requestInfo?.isShowAllOptionsRequested ?: false) // TODO(b/323552850) - Remove
- // usage on Constants.EXTRA_REQ_FOR_ALL_OPTIONS once it is deprecated.
+ isReqForAllOptions = requestInfo?.isShowAllOptionsRequested ?: false
val cancellationRequest = getCancelUiRequest(intent)
val cancelUiRequestState = cancellationRequest?.let {
@@ -124,7 +119,6 @@ class CredentialManagerRepo(
initialUiState = when (requestInfo?.type) {
RequestInfo.TYPE_CREATE -> {
- val isPasskeyFirstUse = userConfigRepo.getIsPasskeyFirstUse()
val providerEnableListUiState = getCreateProviderEnableListInitialUiState()
val providerDisableListUiState = getCreateProviderDisableListInitialUiState()
val requestDisplayInfoUiState =
@@ -137,8 +131,6 @@ class CredentialManagerRepo(
defaultProviderIdsSetByUser =
requestDisplayInfoUiState.userSetDefaultProviderIds,
requestDisplayInfo = requestDisplayInfoUiState,
- isOnPasskeyIntroStateAlready = false,
- isPasskeyFirstUse = isPasskeyFirstUse,
)!!
val isFlowAutoSelectable = isFlowAutoSelectable(createCredentialUiState)
UiState(
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
index 4771237d449f..ec0da0986e45 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -61,9 +61,7 @@ class CredentialSelectorActivity : ComponentActivity() {
if (isCancellationRequest && !shouldShowCancellationUi) {
return
}
- val userConfigRepo = UserConfigRepo(this)
- val credManRepo = CredentialManagerRepo(
- this, intent, userConfigRepo, isNewActivity = true)
+ val credManRepo = CredentialManagerRepo(this, intent, isNewActivity = true)
val backPressedCallback = object : OnBackPressedCallback(
true // default to enabled
@@ -78,10 +76,7 @@ class CredentialSelectorActivity : ComponentActivity() {
setContent {
PlatformTheme {
- CredentialManagerBottomSheet(
- credManRepo,
- userConfigRepo
- )
+ CredentialManagerBottomSheet(credManRepo)
}
}
} catch (e: Exception) {
@@ -103,9 +98,7 @@ class CredentialSelectorActivity : ComponentActivity() {
return
}
} else {
- val userConfigRepo = UserConfigRepo(this)
- val credManRepo = CredentialManagerRepo(
- this, intent, userConfigRepo, isNewActivity = false)
+ val credManRepo = CredentialManagerRepo(this, intent, isNewActivity = false)
viewModel.onNewCredentialManagerRepo(credManRepo)
}
} catch (e: Exception) {
@@ -147,10 +140,9 @@ class CredentialSelectorActivity : ComponentActivity() {
@Composable
private fun CredentialManagerBottomSheet(
credManRepo: CredentialManagerRepo,
- userConfigRepo: UserConfigRepo,
) {
val viewModel: CredentialSelectorViewModel = viewModel {
- CredentialSelectorViewModel(credManRepo, userConfigRepo)
+ CredentialSelectorViewModel(credManRepo)
}
val launcher = rememberLauncherForActivityResult(
StartBalIntentSenderForResultContract()
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
index f4da1e6c4770..1f2fa200e43d 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -34,7 +34,6 @@ import com.android.credentialmanager.common.DialogState
import com.android.credentialmanager.common.ProviderActivityResult
import com.android.credentialmanager.common.ProviderActivityState
import com.android.credentialmanager.createflow.ActiveEntry
-import com.android.credentialmanager.createflow.isFlowAutoSelectable
import com.android.credentialmanager.createflow.CreateCredentialUiState
import com.android.credentialmanager.createflow.CreateScreenState
import com.android.credentialmanager.getflow.GetCredentialUiState
@@ -63,7 +62,6 @@ data class CancelUiRequestState(
class CredentialSelectorViewModel(
private var credManRepo: CredentialManagerRepo,
- private val userConfigRepo: UserConfigRepo,
) : ViewModel() {
var uiState by mutableStateOf(credManRepo.initState())
private set
@@ -266,42 +264,6 @@ class CredentialSelectorViewModel(
/**************************************************************************/
/***** Create Flow Callbacks *****/
/**************************************************************************/
- fun createFlowOnConfirmIntro() {
- userConfigRepo.setIsPasskeyFirstUse(false)
- val prevUiState = uiState.createCredentialUiState
- if (prevUiState == null) {
- Log.d(Constants.LOG_TAG, "Encountered unexpected null create ui state")
- onInternalError()
- return
- }
- val newScreenState = CreateFlowUtils.toCreateScreenState(
- createOptionSize = prevUiState.sortedCreateOptionsPairs.size,
- isOnPasskeyIntroStateAlready = true,
- requestDisplayInfo = prevUiState.requestDisplayInfo,
- remoteEntry = prevUiState.remoteEntry,
- isPasskeyFirstUse = true,
- )
- if (newScreenState == null) {
- Log.d(Constants.LOG_TAG, "Unexpected: couldn't resolve new screen state")
- onInternalError()
- return
- }
- val newCreateCredentialUiState = prevUiState.copy(
- currentScreenState = newScreenState,
- )
- val isFlowAutoSelectable = isFlowAutoSelectable(newCreateCredentialUiState)
- uiState = uiState.copy(
- createCredentialUiState = newCreateCredentialUiState,
- isAutoSelectFlow = isFlowAutoSelectable,
- providerActivityState =
- if (isFlowAutoSelectable) ProviderActivityState.READY_TO_LAUNCH
- else ProviderActivityState.NOT_APPLICABLE,
- selectedEntry =
- if (isFlowAutoSelectable) newCreateCredentialUiState.activeEntry?.activeEntryInfo
- else null,
- )
- }
-
fun createFlowOnMoreOptionsSelectedOnCreationSelection() {
uiState = uiState.copy(
createCredentialUiState = uiState.createCredentialUiState?.copy(
@@ -318,14 +280,6 @@ class CredentialSelectorViewModel(
)
}
- fun createFlowOnBackPasskeyIntroButtonSelected() {
- uiState = uiState.copy(
- createCredentialUiState = uiState.createCredentialUiState?.copy(
- currentScreenState = CreateScreenState.PASSKEY_INTRO,
- )
- )
- }
-
fun createFlowOnEntrySelectedFromMoreOptionScreen(activeEntry: ActiveEntry) {
uiState = uiState.copy(
createCredentialUiState = uiState.createCredentialUiState?.copy(
@@ -348,14 +302,6 @@ class CredentialSelectorViewModel(
uiState = uiState.copy(dialogState = DialogState.CANCELED_FOR_SETTINGS)
}
- fun createFlowOnLearnMore() {
- uiState = uiState.copy(
- createCredentialUiState = uiState.createCredentialUiState?.copy(
- currentScreenState = CreateScreenState.MORE_ABOUT_PASSKEYS_INTRO,
- )
- )
- }
-
fun createFlowOnUseOnceSelected() {
uiState = uiState.copy(
createCredentialUiState = uiState.createCredentialUiState?.copy(
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index 997c45e84180..5830b9fc1028 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -342,8 +342,6 @@ class CreateFlowUtils {
defaultProviderIdPreferredByApp: String?,
defaultProviderIdsSetByUser: Set<String>,
requestDisplayInfo: RequestDisplayInfo,
- isOnPasskeyIntroStateAlready: Boolean,
- isPasskeyFirstUse: Boolean,
): CreateCredentialUiState? {
var remoteEntry: RemoteInfo? = null
var remoteEntryProvider: EnabledProviderInfo? = null
@@ -392,11 +390,8 @@ class CreateFlowUtils {
val defaultProvider = defaultProviderPreferredByApp ?: defaultProviderSetByUser
val initialScreenState = toCreateScreenState(
createOptionSize = createOptionsPairs.size,
- isOnPasskeyIntroStateAlready = isOnPasskeyIntroStateAlready,
- requestDisplayInfo = requestDisplayInfo,
remoteEntry = remoteEntry,
- isPasskeyFirstUse = isPasskeyFirstUse
- ) ?: return null
+ )
val sortedCreateOptionsPairs = createOptionsPairs.sortedWith(
compareByDescending { it.first.lastUsedTime }
)
@@ -419,15 +414,9 @@ class CreateFlowUtils {
fun toCreateScreenState(
createOptionSize: Int,
- isOnPasskeyIntroStateAlready: Boolean,
- requestDisplayInfo: RequestDisplayInfo,
remoteEntry: RemoteInfo?,
- isPasskeyFirstUse: Boolean,
- ): CreateScreenState? {
- return if (isPasskeyFirstUse && requestDisplayInfo.type == CredentialType.PASSKEY &&
- !isOnPasskeyIntroStateAlready) {
- CreateScreenState.PASSKEY_INTRO
- } else if (createOptionSize == 0 && remoteEntry != null) {
+ ): CreateScreenState {
+ return if (createOptionSize == 0 && remoteEntry != null) {
CreateScreenState.EXTERNAL_ONLY_SELECTION
} else {
CreateScreenState.CREATION_OPTION_SELECTION
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/UserConfigRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/UserConfigRepo.kt
deleted file mode 100644
index bfcca4970a71..000000000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/UserConfigRepo.kt
+++ /dev/null
@@ -1,44 +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.credentialmanager
-
-import android.content.Context
-import android.content.SharedPreferences
-
-class UserConfigRepo(context: Context) {
- val sharedPreferences: SharedPreferences = context.getSharedPreferences(
- context.packageName, Context.MODE_PRIVATE)
-
- fun setIsPasskeyFirstUse(
- isFirstUse: Boolean
- ) {
- sharedPreferences.edit().apply {
- putBoolean(IS_PASSKEY_FIRST_USE, isFirstUse)
- apply()
- }
- }
-
- fun getIsPasskeyFirstUse(): Boolean {
- return sharedPreferences.getBoolean(IS_PASSKEY_FIRST_USE, true)
- }
-
- companion object {
- // This first use value only applies to passkeys, not related with if generally
- // credential manager is first use or not
- const val IS_PASSKEY_FIRST_USE = "is_passkey_first_use"
- }
-}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index 121f207122a0..0f1721790295 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -40,6 +40,7 @@ import android.service.autofill.Field
import android.service.autofill.FillCallback
import android.service.autofill.FillRequest
import android.service.autofill.FillResponse
+import android.service.autofill.Flags
import android.service.autofill.InlinePresentation
import android.service.autofill.Presentations
import android.service.autofill.SaveCallback
@@ -56,6 +57,7 @@ import androidx.credentials.provider.CustomCredentialEntry
import androidx.credentials.provider.PasswordCredentialEntry
import androidx.credentials.provider.PublicKeyCredentialEntry
import com.android.credentialmanager.GetFlowUtils
+import com.android.credentialmanager.common.ui.InlinePresentationsFactory
import com.android.credentialmanager.common.ui.RemoteViewsFactory
import com.android.credentialmanager.getflow.ProviderDisplayInfo
import com.android.credentialmanager.getflow.toProviderDisplayInfo
@@ -250,7 +252,7 @@ class CredentialAutofillService : AutofillService() {
maxInlineItemCount = maxInlineItemCount.coerceAtMost(inlineMaxSuggestedCount)
val lastDropdownDatasetIndex = Settings.Global.getInt(this.contentResolver,
Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS,
- (maxDropdownDisplayLimit - 1)).coerceAtMost(totalEntryCount - 1)
+ (maxDropdownDisplayLimit - 1)).coerceAtMost(totalEntryCount)
var i = 0
var datasetAdded = false
@@ -293,8 +295,12 @@ class CredentialAutofillService : AutofillService() {
} else {
inlinePresentationSpecs[inlinePresentationSpecsCount - 1]
}
- inlinePresentation = createInlinePresentation(primaryEntry, pendingIntent, icon,
- spec!!, duplicateDisplayNamesForPasskeys)
+ if (spec != null) {
+ inlinePresentation = createInlinePresentation(primaryEntry, pendingIntent, icon,
+ InlinePresentationsFactory.modifyInlinePresentationSpec
+ (this@CredentialAutofillService, spec),
+ duplicateDisplayNamesForPasskeys)
+ }
}
var dropdownPresentation: RemoteViews? = null
if (i < lastDropdownDatasetIndex) {
@@ -479,18 +485,28 @@ class CredentialAutofillService : AutofillService() {
val autofillIdToCredentialEntries:
MutableMap<AutofillId, ArrayList<Entry>> = mutableMapOf()
credentialEntryList.forEach entryLoop@{ credentialEntry ->
- val autofillId: AutofillId? = credentialEntry
- .frameworkExtrasIntent
- ?.getParcelableExtra(
- CredentialProviderService.EXTRA_AUTOFILL_ID,
- AutofillId::class.java)
- if (autofillId == null) {
- Log.e(TAG, "AutofillId is missing from credential entry. Credential" +
- " Integration might be disabled.")
- return@entryLoop
- }
- autofillIdToCredentialEntries.getOrPut(autofillId) { ArrayList() }
- .add(credentialEntry)
+ val intent = credentialEntry.frameworkExtrasIntent
+ intent?.getParcelableExtra(
+ CredentialProviderService.EXTRA_GET_CREDENTIAL_REQUEST,
+ android.service.credentials.GetCredentialRequest::class.java)
+ ?.credentialOptions
+ ?.forEach { credentialOption ->
+ credentialOption.candidateQueryData.getParcelableArrayList(
+ CredentialProviderService.EXTRA_AUTOFILL_ID, AutofillId::class.java)
+ ?.forEach { autofillId ->
+ intent.putExtra(
+ CredentialProviderService.EXTRA_AUTOFILL_ID,
+ autofillId)
+ val entry = Entry(
+ credentialEntry.key,
+ credentialEntry.subkey,
+ credentialEntry.slice,
+ intent)
+ autofillIdToCredentialEntries
+ .getOrPut(autofillId) { ArrayList() }
+ .add(entry)
+ }
+ }
}
return autofillIdToCredentialEntries
}
@@ -573,23 +589,31 @@ class CredentialAutofillService : AutofillService() {
cmRequests: MutableList<CredentialOption>,
responseClientState: Bundle
) {
+ val traversedViewNodes: MutableSet<AutofillId> = mutableSetOf()
+ val credentialOptionsFromHints: MutableMap<String, CredentialOption> = mutableMapOf()
val windowNodes: List<AssistStructure.WindowNode> =
structure.run {
(0 until windowNodeCount).map { getWindowNodeAt(it) }
}
windowNodes.forEach { windowNode: AssistStructure.WindowNode ->
- traverseNodeForRequest(windowNode.rootViewNode, cmRequests, responseClientState)
+ traverseNodeForRequest(
+ windowNode.rootViewNode, cmRequests, responseClientState, traversedViewNodes,
+ credentialOptionsFromHints)
}
}
private fun traverseNodeForRequest(
viewNode: AssistStructure.ViewNode,
cmRequests: MutableList<CredentialOption>,
- responseClientState: Bundle
+ responseClientState: Bundle,
+ traversedViewNodes: MutableSet<AutofillId>,
+ credentialOptionsFromHints: MutableMap<String, CredentialOption>
) {
viewNode.autofillId?.let {
- cmRequests.addAll(getCredentialOptionsFromViewNode(viewNode, it, responseClientState))
+ cmRequests.addAll(getCredentialOptionsFromViewNode(viewNode, it, responseClientState,
+ traversedViewNodes, credentialOptionsFromHints))
+ traversedViewNodes.add(it)
}
val children: List<AssistStructure.ViewNode> =
@@ -598,22 +622,37 @@ class CredentialAutofillService : AutofillService() {
}
children.forEach { childNode: AssistStructure.ViewNode ->
- traverseNodeForRequest(childNode, cmRequests, responseClientState)
+ traverseNodeForRequest(childNode, cmRequests, responseClientState, traversedViewNodes,
+ credentialOptionsFromHints)
}
}
private fun getCredentialOptionsFromViewNode(
viewNode: AssistStructure.ViewNode,
autofillId: AutofillId,
- responseClientState: Bundle
+ responseClientState: Bundle,
+ traversedViewNodes: MutableSet<AutofillId>,
+ credentialOptionsFromHints: MutableMap<String, CredentialOption>
): MutableList<CredentialOption> {
- if (viewNode.credentialManagerRequest != null &&
- viewNode.credentialManagerCallback != null) {
- val options = viewNode.credentialManagerRequest?.getCredentialOptions()
- if (options != null) {
- return options
+ val credentialOptions: MutableList<CredentialOption> = mutableListOf()
+ if (Flags.autofillCredmanDevIntegration() && viewNode.credentialManagerRequest != null) {
+ viewNode.credentialManagerRequest
+ ?.getCredentialOptions()
+ ?.forEach { credentialOption ->
+ credentialOption.candidateQueryData
+ .getParcelableArrayList(
+ CredentialProviderService.EXTRA_AUTOFILL_ID, AutofillId::class.java)
+ ?.let { associatedAutofillIds ->
+ // Check whether any of the associated autofill ids have already been
+ // traversed. If so, skip, to dedupe on duplicate credential options.
+ if ((traversedViewNodes intersect associatedAutofillIds.toSet())
+ .isEmpty()) {
+ credentialOptions.add(credentialOption)
+ }
+ }
}
}
+ // TODO(b/325502552): clean up cred option logic in autofill hint
val credentialHints: MutableList<String> = mutableListOf()
if (viewNode.autofillHints != null) {
@@ -627,10 +666,10 @@ class CredentialAutofillService : AutofillService() {
}
}
- val credentialOptions: MutableList<CredentialOption> = mutableListOf()
for (credentialHint in credentialHints) {
try {
- convertJsonToCredentialOption(credentialHint, autofillId)
+ convertJsonToCredentialOption(
+ credentialHint, autofillId, credentialOptionsFromHints)
.let { credentialOptions.addAll(it) }
} catch (e: JSONException) {
Log.i(TAG, "Exception while parsing response: " + e.message)
@@ -639,10 +678,11 @@ class CredentialAutofillService : AutofillService() {
return credentialOptions
}
- private fun convertJsonToCredentialOption(jsonString: String, autofillId: AutofillId):
- List<CredentialOption> {
- // TODO(b/302000646) Move this logic to jetpack so that is consistent
- // with building the json
+ private fun convertJsonToCredentialOption(
+ jsonString: String,
+ autofillId: AutofillId,
+ credentialOptionsFromHints: MutableMap<String, CredentialOption>
+ ): List<CredentialOption> {
val credentialOptions: MutableList<CredentialOption> = mutableListOf()
val json = JSONObject(jsonString)
@@ -650,16 +690,34 @@ class CredentialAutofillService : AutofillService() {
val options = jsonGet.getJSONArray(CRED_OPTIONS_KEY)
for (i in 0 until options.length()) {
val option = options.getJSONObject(i)
- val candidateBundle = convertJsonToBundle(option.getJSONObject(CANDIDATE_DATA_KEY))
- candidateBundle.putParcelable(
+ val optionString = option.toString()
+ credentialOptionsFromHints[optionString]
+ ?.let { credentialOption ->
+ // if the current credential option was seen before, add the current
+ // viewNode to the credential option, but do not add it to the option list
+ // again. This will result in the same result as deduping based on
+ // traversed viewNode.
+ credentialOption.candidateQueryData.getParcelableArrayList(
+ CredentialProviderService.EXTRA_AUTOFILL_ID, AutofillId::class.java)
+ ?.let {
+ it.add(autofillId)
+ credentialOption.candidateQueryData.putParcelableArrayList(
+ CredentialProviderService.EXTRA_AUTOFILL_ID, it)
+ }
+ } ?: run {
+ val candidateBundle = convertJsonToBundle(option.getJSONObject(CANDIDATE_DATA_KEY))
+ candidateBundle.putParcelableArrayList(
CredentialProviderService.EXTRA_AUTOFILL_ID,
- autofillId)
- credentialOptions.add(CredentialOption(
+ arrayListOf(autofillId))
+ val credentialOption = CredentialOption(
option.getString(TYPE_KEY),
convertJsonToBundle(option.getJSONObject(REQUEST_DATA_KEY)),
candidateBundle,
option.getBoolean(SYS_PROVIDER_REQ_KEY),
- ))
+ )
+ credentialOptions.add(credentialOption)
+ credentialOptionsFromHints[optionString] = credentialOption
+ }
}
return credentialOptions
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/InlinePresentationFactory.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/InlinePresentationFactory.kt
new file mode 100644
index 000000000000..3ebdd204b640
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/InlinePresentationFactory.kt
@@ -0,0 +1,84 @@
+/*
+* Copyright (C) 2024 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.credentialmanager.common.ui
+
+
+import android.content.Context
+import android.util.Size
+import android.widget.inline.InlinePresentationSpec
+import androidx.autofill.inline.common.TextViewStyle
+import androidx.autofill.inline.common.ViewStyle
+import androidx.autofill.inline.UiVersions
+import androidx.autofill.inline.UiVersions.Style
+import androidx.autofill.inline.v1.InlineSuggestionUi
+import androidx.core.content.ContextCompat
+import android.util.TypedValue
+import android.graphics.Typeface
+
+
+class InlinePresentationsFactory {
+ companion object {
+ private const val googleSansMediumFontFamily = "google-sans-medium"
+ private const val googleSansTextFontFamily = "google-sans-text"
+ // There is no min width required for now but this is needed for the spec builder
+ private const val minInlineWidth = 5000
+
+
+ fun modifyInlinePresentationSpec(context: Context,
+ originalSpec: InlinePresentationSpec): InlinePresentationSpec {
+ return InlinePresentationSpec.Builder(Size(originalSpec.minSize.width, originalSpec
+ .minSize.height),
+ Size(minInlineWidth, originalSpec
+ .maxSize.height))
+ .setStyle(UiVersions.newStylesBuilder().addStyle(getStyle(context)).build())
+ .build()
+ }
+
+
+ fun getStyle(context: Context): Style {
+ val textColorPrimary = ContextCompat.getColor(context,
+ com.android.credentialmanager.R.color.text_primary)
+ val textColorSecondary = ContextCompat.getColor(context,
+ com.android.credentialmanager.R.color.text_secondary)
+ val textColorBackground = ContextCompat.getColor(context,
+ com.android.credentialmanager.R.color.inline_background)
+ val chipHorizontalPadding = context.resources.getDimensionPixelSize(com.android
+ .credentialmanager.R.dimen.horizontal_chip_padding)
+ val chipVerticalPadding = context.resources.getDimensionPixelSize(com.android
+ .credentialmanager.R.dimen.vertical_chip_padding)
+ return InlineSuggestionUi.newStyleBuilder()
+ .setChipStyle(
+ ViewStyle.Builder().setPadding(chipHorizontalPadding,
+ chipVerticalPadding,
+ chipHorizontalPadding, chipVerticalPadding).build()
+ )
+ .setTitleStyle(
+ TextViewStyle.Builder().setTextColor(textColorPrimary).setTextSize
+ (TypedValue.COMPLEX_UNIT_DIP, 14F)
+ .setTypeface(googleSansMediumFontFamily,
+ Typeface.NORMAL).setBackgroundColor(textColorBackground)
+ .build()
+ )
+ .setSubtitleStyle(TextViewStyle.Builder().setTextColor(textColorSecondary)
+ .setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12F).setTypeface
+ (googleSansTextFontFamily, Typeface.NORMAL).setBackgroundColor
+ (textColorBackground).build())
+ .build()
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
index f261d1fa062d..d24adb567bc4 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
@@ -20,8 +20,6 @@ import android.text.TextUtils
import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.result.ActivityResult
import androidx.activity.result.IntentSenderRequest
-import androidx.compose.foundation.isSystemInDarkTheme
-import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -36,12 +34,9 @@ import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.outlined.QrCodeScanner
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap
-import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
@@ -63,10 +58,8 @@ import com.android.credentialmanager.common.ui.Entry
import com.android.credentialmanager.common.ui.HeadlineIcon
import com.android.credentialmanager.common.ui.LargeLabelTextOnSurfaceVariant
import com.android.credentialmanager.common.ui.ModalBottomSheet
-import com.android.credentialmanager.common.ui.MoreAboutPasskeySectionHeader
import com.android.credentialmanager.common.ui.MoreOptionTopAppBar
import com.android.credentialmanager.common.ui.SheetContainerCard
-import com.android.credentialmanager.common.ui.PasskeyBenefitRow
import com.android.credentialmanager.common.ui.HeadlineText
import com.android.credentialmanager.logging.CreateCredentialEvent
import com.android.credentialmanager.model.creation.CreateOptionInfo
@@ -87,11 +80,6 @@ fun CreateCredentialScreen(
when (viewModel.uiState.providerActivityState) {
ProviderActivityState.NOT_APPLICABLE -> {
when (createCredentialUiState.currentScreenState) {
- CreateScreenState.PASSKEY_INTRO -> PasskeyIntroCard(
- onConfirm = viewModel::createFlowOnConfirmIntro,
- onLearnMore = viewModel::createFlowOnLearnMore,
- onLog = { viewModel.logUiEvent(it) },
- )
CreateScreenState.CREATION_OPTION_SELECTION -> CreationSelectionCard(
requestDisplayInfo = createCredentialUiState.requestDisplayInfo,
enabledProviderList = createCredentialUiState.enabledProviders,
@@ -144,11 +132,6 @@ fun CreateCredentialScreen(
onConfirm = viewModel::createFlowOnConfirmEntrySelected,
onLog = { viewModel.logUiEvent(it) },
)
- CreateScreenState.MORE_ABOUT_PASSKEYS_INTRO -> MoreAboutPasskeysIntroCard(
- onBackPasskeyIntroButtonSelected =
- viewModel::createFlowOnBackPasskeyIntroButtonSelected,
- onLog = { viewModel.logUiEvent(it) },
- )
}
}
ProviderActivityState.READY_TO_LAUNCH -> {
@@ -188,78 +171,6 @@ fun CreateCredentialScreen(
}
@Composable
-fun PasskeyIntroCard(
- onConfirm: () -> Unit,
- onLearnMore: () -> Unit,
- onLog: @Composable (UiEventEnum) -> Unit,
-) {
- SheetContainerCard {
- item {
- val onboardingImageResource = remember {
- mutableStateOf(R.drawable.ic_passkeys_onboarding)
- }
- if (isSystemInDarkTheme()) {
- onboardingImageResource.value = R.drawable.ic_passkeys_onboarding_dark
- } else {
- onboardingImageResource.value = R.drawable.ic_passkeys_onboarding
- }
- Row(
- modifier = Modifier.wrapContentHeight().fillMaxWidth(),
- horizontalArrangement = Arrangement.Center,
- ) {
- Image(
- painter = painterResource(onboardingImageResource.value),
- contentDescription = null,
- modifier = Modifier.size(316.dp, 168.dp)
- )
- }
- }
- item { Divider(thickness = 16.dp, color = Color.Transparent) }
- item { HeadlineText(text = stringResource(R.string.passkey_creation_intro_title)) }
- item { Divider(thickness = 16.dp, color = Color.Transparent) }
- item {
- PasskeyBenefitRow(
- leadingIconPainter = painterResource(R.drawable.ic_passkeys_onboarding_password),
- text = stringResource(R.string.passkey_creation_intro_body_password),
- )
- }
- item { Divider(thickness = 16.dp, color = Color.Transparent) }
- item {
- PasskeyBenefitRow(
- leadingIconPainter = painterResource(R.drawable.ic_passkeys_onboarding_fingerprint),
- text = stringResource(R.string.passkey_creation_intro_body_fingerprint),
- )
- }
- item { Divider(thickness = 16.dp, color = Color.Transparent) }
- item {
- PasskeyBenefitRow(
- leadingIconPainter = painterResource(R.drawable.ic_passkeys_onboarding_device),
- text = stringResource(R.string.passkey_creation_intro_body_device),
- )
- }
- item { Divider(thickness = 24.dp, color = Color.Transparent) }
-
- item {
- CtaButtonRow(
- leftButton = {
- ActionButton(
- stringResource(R.string.string_learn_more),
- onClick = onLearnMore
- )
- },
- rightButton = {
- ConfirmButton(
- stringResource(R.string.string_continue),
- onClick = onConfirm
- )
- },
- )
- }
- }
- onLog(CreateCredentialEvent.CREDMAN_CREATE_CRED_PASSKEY_INTRO)
-}
-
-@Composable
fun MoreOptionsSelectionCard(
requestDisplayInfo: RequestDisplayInfo,
enabledProviderList: List<EnabledProviderInfo>,
@@ -522,59 +433,6 @@ fun ExternalOnlySelectionCard(
}
@Composable
-fun MoreAboutPasskeysIntroCard(
- onBackPasskeyIntroButtonSelected: () -> Unit,
- onLog: @Composable (UiEventEnum) -> Unit,
-) {
- SheetContainerCard(
- topAppBar = {
- MoreOptionTopAppBar(
- text = stringResource(R.string.more_about_passkeys_title),
- onNavigationIconClicked = onBackPasskeyIntroButtonSelected,
- bottomPadding = 0.dp,
- )
- },
- ) {
- item {
- MoreAboutPasskeySectionHeader(
- text = stringResource(R.string.passwordless_technology_title)
- )
- Row(modifier = Modifier.fillMaxWidth().wrapContentHeight()) {
- BodyMediumText(text = stringResource(R.string.passwordless_technology_detail))
- }
- }
- item {
- Divider(thickness = 8.dp, color = Color.Transparent)
- MoreAboutPasskeySectionHeader(
- text = stringResource(R.string.public_key_cryptography_title)
- )
- Row(modifier = Modifier.fillMaxWidth().wrapContentHeight()) {
- BodyMediumText(text = stringResource(R.string.public_key_cryptography_detail))
- }
- }
- item {
- Divider(thickness = 8.dp, color = Color.Transparent)
- MoreAboutPasskeySectionHeader(
- text = stringResource(R.string.improved_account_security_title)
- )
- Row(modifier = Modifier.fillMaxWidth().wrapContentHeight()) {
- BodyMediumText(text = stringResource(R.string.improved_account_security_detail))
- }
- }
- item {
- Divider(thickness = 8.dp, color = Color.Transparent)
- MoreAboutPasskeySectionHeader(
- text = stringResource(R.string.seamless_transition_title)
- )
- Row(modifier = Modifier.fillMaxWidth().wrapContentHeight()) {
- BodyMediumText(text = stringResource(R.string.seamless_transition_detail))
- }
- }
- }
- onLog(CreateCredentialEvent.CREDMAN_CREATE_CRED_MORE_ABOUT_PASSKEYS_INTRO)
-}
-
-@Composable
fun PrimaryCreateOptionRow(
requestDisplayInfo: RequestDisplayInfo,
entryInfo: EntryInfo,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
index 8b0ba87fa9be..617a981fc4ba 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
@@ -37,10 +37,6 @@ internal fun isFlowAutoSelectable(
uiState: CreateCredentialUiState
): Boolean {
return uiState.requestDisplayInfo.isAutoSelectRequest &&
- // Even if the flow is auto selectable, still allow passkey intro screen to show once if
- // applicable.
- uiState.currentScreenState != CreateScreenState.PASSKEY_INTRO &&
- uiState.currentScreenState != CreateScreenState.MORE_ABOUT_PASSKEYS_INTRO &&
uiState.sortedCreateOptionsPairs.size == 1 &&
uiState.activeEntry?.activeEntryInfo?.let {
it is CreateOptionInfo && it.allowAutoSelect
@@ -98,8 +94,6 @@ data class ActiveEntry (
/** The name of the current screen. */
enum class CreateScreenState {
- PASSKEY_INTRO,
- MORE_ABOUT_PASSKEYS_INTRO,
CREATION_OPTION_SELECTION,
MORE_OPTIONS_SELECTION,
DEFAULT_PROVIDER_CONFIRMATION,
diff --git a/packages/CredentialManager/wear/res/values/strings.xml b/packages/CredentialManager/wear/res/values/strings.xml
index 4e9174e53864..9480e64aa134 100644
--- a/packages/CredentialManager/wear/res/values/strings.xml
+++ b/packages/CredentialManager/wear/res/values/strings.xml
@@ -27,11 +27,11 @@
<!-- Title of a screen prompting if the user would like to sign in with provider
[CHAR LIMIT=80] -->
<string name="use_password_title">Use password?</string>
- <!-- Content description for the dismiss button of a screen. [CHAR LIMIT=NONE] -->
+ <!-- Text on this dismiss button of a screen. [CHAR LIMIT=NONE] -->
<string name="dialog_dismiss_button">Dismiss</string>
- <!-- Content description for the continue button of a screen. [CHAR LIMIT=NONE] -->
+ <!-- Text on the continue button of a screen. [CHAR LIMIT=NONE] -->
<string name="dialog_continue_button">Continue</string>
- <!-- Content description for the sign in options button of a screen. [CHAR LIMIT=NONE] -->
+ <!-- Text on the sign in options button of a screen. [CHAR LIMIT=NONE] -->
<string name="dialog_sign_in_options_button">Sign-in Options</string>
<!-- Title for multiple credentials folded screen. [CHAR LIMIT=NONE] -->
<string name="sign_in_options_title">Sign-in Options</string>
@@ -41,4 +41,11 @@
<string name="choose_passkey_title">Choose passkey</string>
<!-- Title for multiple credentials screen with only passwords. [CHAR LIMIT=NONE] -->
<string name="choose_password_title">Choose password</string>
+ <!-- Text on the sign in on phone button [CHAR LIMIT=NONE] -->
+ <string name="sign_in_on_phone_button">Sign in on phone</string>
+ <!-- Text on the locked provider button when unlocked[CHAR LIMIT=NONE] -->
+ <string name="locked_credential_entry_label_subtext_no_sign_in">No sign-in info</string>
+ <!-- Text on the locked provider button when locked[CHAR LIMIT=NONE] -->
+ <string name="locked_credential_entry_label_subtext_tap_to_unlock">Tap to unlock</string>
+
</resources> \ No newline at end of file
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
index 0df40d7adba5..283dc7d6fe08 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -25,7 +25,6 @@ import androidx.wear.compose.material.MaterialTheme
import com.android.credentialmanager.ui.WearApp
import com.google.android.horologist.annotations.ExperimentalHorologistApi
import dagger.hilt.android.AndroidEntryPoint
-import kotlin.system.exitProcess
@AndroidEntryPoint(ComponentActivity::class)
class CredentialSelectorActivity : Hilt_CredentialSelectorActivity() {
@@ -40,7 +39,7 @@ class CredentialSelectorActivity : Hilt_CredentialSelectorActivity() {
MaterialTheme {
WearApp(
viewModel = viewModel,
- onCloseApp = { exitProcess(0) },
+ onCloseApp = { finish() },
)
}
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt
index 6bd166e855ff..8c5c085d2880 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt
@@ -19,5 +19,6 @@ package com.android.credentialmanager
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
+/** [Application] of credential selector. */
@HiltAndroidApp(Application::class)
class CredentialSelectorApp : Hilt_CredentialSelectorApp() \ No newline at end of file
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
index 2fc98e27bea1..463c4d1b063e 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -17,13 +17,22 @@
package com.android.credentialmanager
import android.content.Intent
+import android.credentials.selection.BaseDialogResult
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
+import com.android.credentialmanager.CredentialSelectorUiState.Get
import com.android.credentialmanager.model.Request
import com.android.credentialmanager.client.CredentialManagerClient
+import com.android.credentialmanager.model.EntryInfo
import com.android.credentialmanager.model.get.ActionEntryInfo
+import com.android.credentialmanager.model.get.AuthenticationEntryInfo
import com.android.credentialmanager.model.get.CredentialEntryInfo
import com.android.credentialmanager.ui.mappers.toGet
+import android.util.Log
+import com.android.credentialmanager.CredentialSelectorUiState.Cancel
+import com.android.credentialmanager.CredentialSelectorUiState.Close
+import com.android.credentialmanager.CredentialSelectorUiState.Create
+import com.android.credentialmanager.CredentialSelectorUiState.Idle
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
@@ -35,27 +44,70 @@ import javax.inject.Inject
@HiltViewModel
class CredentialSelectorViewModel @Inject constructor(
private val credentialManagerClient: CredentialManagerClient,
-) : ViewModel() {
- private val isPrimaryScreen = MutableStateFlow(false)
- val uiState: StateFlow<CredentialSelectorUiState> = credentialManagerClient.requests
- .combine(isPrimaryScreen) { request, isPrimary ->
+) : FlowEngine, ViewModel() {
+ private val isPrimaryScreen = MutableStateFlow(true)
+ private val shouldClose = MutableStateFlow(false)
+ val uiState: StateFlow<CredentialSelectorUiState> =
+ combine(
+ credentialManagerClient.requests,
+ isPrimaryScreen,
+ shouldClose
+ ) { request, isPrimary, shouldClose ->
+ if (shouldClose) {
+ Log.d(TAG, "Request finished, closing ")
+ return@combine Close
+ }
+
when (request) {
- null -> CredentialSelectorUiState.Idle
- is Request.Cancel -> CredentialSelectorUiState.Cancel(request.appName)
- is Request.Close -> CredentialSelectorUiState.Close
- is Request.Create -> CredentialSelectorUiState.Create
+ null -> Idle
+ is Request.Cancel -> Cancel(request.appName)
+ is Request.Close -> Close
+ is Request.Create -> Create
is Request.Get -> request.toGet(isPrimary)
}
}
.stateIn(
viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
- initialValue = CredentialSelectorUiState.Idle,
+ initialValue = Idle,
)
fun updateRequest(intent: Intent) {
credentialManagerClient.updateRequest(intent = intent)
}
+
+ override fun back() {
+ Log.d(TAG, "OnBackPressed")
+ when (uiState.value) {
+ is Get.MultipleEntry -> isPrimaryScreen.value = true
+ is Create, Close, is Cancel, Idle -> shouldClose.value = true
+ is Get.SingleEntry, is Get.SingleEntryPerAccount -> cancel()
+ }
+ }
+
+ override fun cancel() {
+ credentialManagerClient.sendError(BaseDialogResult.RESULT_CODE_DIALOG_USER_CANCELED)
+ shouldClose.value = true
+ }
+
+ override fun openSecondaryScreen() {
+ isPrimaryScreen.value = false
+ }
+
+ override fun sendSelectionResult(
+ entryInfo: EntryInfo,
+ resultCode: Int?,
+ resultData: Intent?,
+ isAutoSelected: Boolean,
+ ) {
+ val result = credentialManagerClient.sendEntrySelectionResult(
+ entryInfo = entryInfo,
+ resultCode = resultCode,
+ resultData = resultData,
+ isAutoSelected = isAutoSelected
+ )
+ shouldClose.value = result
+ }
}
sealed class CredentialSelectorUiState {
@@ -66,6 +118,7 @@ sealed class CredentialSelectorUiState {
data class MultipleEntry(
val accounts: List<PerUserNameEntries>,
val actionEntryList: List<ActionEntryInfo>,
+ val authenticationEntryList: List<AuthenticationEntryInfo>,
) : Get() {
data class PerUserNameEntries(
val userName: String,
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/FlowEngine.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/FlowEngine.kt
new file mode 100644
index 000000000000..e4216446772b
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/FlowEngine.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 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.credentialmanager
+
+import android.content.Intent
+import com.android.credentialmanager.model.EntryInfo
+
+/** Engine of the credential selecting flow. */
+interface FlowEngine {
+ /** Back from previous stage. */
+ fun back()
+ /** Cancels the selection flow. */
+ fun cancel()
+ /** Opens secondary screen. */
+ fun openSecondaryScreen()
+ /**
+ * Sends [entryInfo] as long as result after launching [EntryInfo.pendingIntent] with
+ * [EntryInfo.fillInIntent].
+ *
+ * @param entryInfo: selected entry.
+ * @param resultCode: result code received after launch.
+ * @param resultData: data received after launch
+ * @param isAutoSelected: whether the entry is auto selected or by user.
+ */
+ fun sendSelectionResult(
+ entryInfo: EntryInfo,
+ resultCode: Int? = null,
+ resultData: Intent? = null,
+ isAutoSelected: Boolean = false,
+ )
+} \ No newline at end of file
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
index f7158e89a5cd..f9a5887158eb 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
@@ -18,8 +18,11 @@
package com.android.credentialmanager.ui
+import android.util.Log
+import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavController
import androidx.wear.compose.foundation.rememberSwipeToDismissBoxState
@@ -29,6 +32,8 @@ import com.android.credentialmanager.CredentialSelectorUiState
import com.android.credentialmanager.CredentialSelectorUiState.Get.SingleEntry
import com.android.credentialmanager.CredentialSelectorUiState.Get.MultipleEntry
import com.android.credentialmanager.CredentialSelectorViewModel
+import com.android.credentialmanager.FlowEngine
+import com.android.credentialmanager.TAG
import com.android.credentialmanager.ui.screens.LoadingScreen
import com.android.credentialmanager.ui.screens.single.passkey.SinglePasskeyScreen
import com.android.credentialmanager.ui.screens.single.password.SinglePasswordScreen
@@ -44,6 +49,7 @@ import com.android.credentialmanager.ui.screens.multiple.MultiCredentialsFoldScr
@Composable
fun WearApp(
viewModel: CredentialSelectorViewModel,
+ flowEngine: FlowEngine = viewModel,
onCloseApp: () -> Unit,
) {
val navController = rememberSwipeDismissableNavController()
@@ -52,7 +58,6 @@ fun WearApp(
rememberSwipeDismissableNavHostState(swipeToDismissBoxState = swipeToDismissBoxState)
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
-
WearNavScaffold(
startDestination = Screen.Loading.route,
navController = navController,
@@ -61,11 +66,11 @@ fun WearApp(
composable(Screen.Loading.route) {
LoadingScreen()
}
-
scrollable(Screen.SinglePasswordScreen.route) {
SinglePasswordScreen(
- credentialSelectorUiState = viewModel.uiState.value as SingleEntry,
+ entry = (remember { uiState } as SingleEntry).entry,
columnState = it.columnState,
+ flowEngine = flowEngine,
)
}
@@ -88,10 +93,13 @@ fun WearApp(
credentialSelectorUiState = viewModel.uiState.value as MultipleEntry,
screenIcon = null,
columnState = it.columnState,
- )
+ )
}
}
-
+ BackHandler(true) {
+ viewModel.back()
+ }
+ Log.d(TAG, "uiState change, state: $uiState")
when (val state = uiState) {
CredentialSelectorUiState.Idle -> {
if (navController.currentDestination?.route != Screen.Loading.route) {
@@ -112,7 +120,6 @@ fun WearApp(
}
is CredentialSelectorUiState.Cancel -> {
- // TODO: b/300422310 - Implement cancel with message flow
onCloseApp()
}
@@ -142,7 +149,7 @@ private fun handleGetNavigation(
}
}
- is CredentialSelectorUiState.Get.MultipleEntry -> {
+ is MultipleEntry -> {
navController.navigateToMultipleCredentialsFoldScreen()
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/CredentialsScreenChip.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/CredentialsScreenChip.kt
index 7cd6bb3a6cef..8e5a8666621f 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/CredentialsScreenChip.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/CredentialsScreenChip.kt
@@ -15,32 +15,40 @@
*/
package com.android.credentialmanager.ui.components
+import androidx.compose.foundation.layout.Row
+import androidx.compose.material3.Icon
import android.graphics.drawable.Drawable
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.wear.compose.material.Chip
+import androidx.core.graphics.drawable.toBitmap
import androidx.wear.compose.material.ChipColors
+import androidx.compose.ui.graphics.asImageBitmap
+import androidx.compose.ui.graphics.Color
import androidx.wear.compose.material.ChipDefaults
import androidx.wear.compose.material.Text
import com.android.credentialmanager.R
+import com.android.credentialmanager.model.get.AuthenticationEntryInfo
import com.android.credentialmanager.ui.components.CredentialsScreenChip.TOPPADDING
+/* Used as credential suggestion or user action chip. */
@Composable
fun CredentialsScreenChip(
label: String,
onClick: () -> Unit,
secondaryLabel: String? = null,
icon: Drawable? = null,
+ isAuthenticationEntryLocked: Boolean = false,
modifier: Modifier = Modifier,
colors: ChipColors = ChipDefaults.secondaryChipColors(),
) {
@@ -56,18 +64,37 @@ fun CredentialsScreenChip(
val secondaryLabelParam: (@Composable RowScope.() -> Unit)? =
secondaryLabel?.let {
{
- Text(
- text = secondaryLabel,
- overflow = TextOverflow.Ellipsis,
- maxLines = 1,
- )
+ Row {
+ Text(
+ text = secondaryLabel,
+ overflow = TextOverflow.Ellipsis,
+ maxLines = 1,
+ )
+
+ if (isAuthenticationEntryLocked)
+ // TODO(b/324465527) change this to lock icon and correct size once figma mocks are
+ // updated
+ Icon(
+ bitmap = checkNotNull(icon?.toBitmap()?.asImageBitmap()),
+ // Decorative purpose only.
+ contentDescription = null,
+ modifier = Modifier.size(20.dp),
+ tint = Color.Unspecified
+ )
+ }
}
}
val iconParam: (@Composable BoxScope.() -> Unit)? =
- icon?.let {
+ icon?.toBitmap()?.asImageBitmap()?.let {
{
- ChipDefaults.IconSize
+ Icon(
+ bitmap = it,
+ // Decorative purpose only.
+ contentDescription = null,
+ modifier = Modifier.size(32.dp),
+ tint = Color.Unspecified
+ )
}
}
@@ -139,6 +166,37 @@ fun DismissChip(onClick: () -> Unit) {
)
}
+@Composable
+fun SignInOnPhoneChip(onClick: () -> Unit) {
+ CredentialsScreenChip(
+ label = stringResource(R.string.sign_in_on_phone_button),
+ onClick = onClick,
+ modifier = Modifier
+ .padding(top = TOPPADDING),
+ )
+}
+
+@Composable
+fun LockedProviderChip(
+ authenticationEntryInfo: AuthenticationEntryInfo,
+ onClick: () -> Unit
+) {
+ val secondaryLabel = stringResource(
+ if (authenticationEntryInfo.isUnlockedAndEmpty)
+ R.string.locked_credential_entry_label_subtext_no_sign_in
+ else R.string.locked_credential_entry_label_subtext_tap_to_unlock
+ )
+
+ CredentialsScreenChip(
+ label = authenticationEntryInfo.title,
+ icon = authenticationEntryInfo.icon,
+ secondaryLabel = secondaryLabel,
+ isAuthenticationEntryLocked = !authenticationEntryInfo.isUnlockedAndEmpty,
+ onClick = onClick,
+ modifier = Modifier.padding(top = TOPPADDING),
+ )
+}
+
@Preview
@Composable
fun DismissChipPreview() {
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/SignInHeader.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/SignInHeader.kt
index 1ddf4af1d923..423662c30d6e 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/SignInHeader.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/SignInHeader.kt
@@ -27,10 +27,12 @@ import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.unit.dp
import androidx.core.graphics.drawable.toBitmap
import androidx.wear.compose.material.Text
+import androidx.compose.ui.graphics.Color
import androidx.compose.material3.Icon
import androidx.wear.compose.material.MaterialTheme as WearMaterialTheme
import androidx.compose.ui.text.style.TextAlign
+/* Used as header across Credential Selector screens. */
@Composable
fun SignInHeader(
icon: Drawable?,
@@ -46,7 +48,9 @@ fun SignInHeader(
bitmap = icon.toBitmap().asImageBitmap(),
modifier = Modifier.size(32.dp),
// Decorative purpose only.
- contentDescription = null
+ contentDescription = null,
+ tint = Color.Unspecified,
+
)
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
index 5898a40d2edd..03b0931f3eb4 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
@@ -47,6 +47,7 @@ fun Request.Get.toGet(isPrimary: Boolean): CredentialSelectorUiState.Get {
)
},
actionEntryList = providerInfos.flatMap { it.actionEntryList },
+ authenticationEntryList = providerInfos.flatMap { it.authenticationEntryList }
)
}
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFoldScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFoldScreen.kt
index a0ea4ee10fbf..5515c8696b87 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFoldScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFoldScreen.kt
@@ -39,6 +39,7 @@ import com.android.credentialmanager.ui.components.DismissChip
import com.android.credentialmanager.ui.components.CredentialsScreenChip
import com.android.credentialmanager.ui.components.SignInHeader
import com.android.credentialmanager.ui.components.SignInOptionsChip
+import com.android.credentialmanager.ui.components.LockedProviderChip
import com.google.android.horologist.annotations.ExperimentalHorologistApi
import com.google.android.horologist.compose.layout.ScalingLazyColumn
import com.google.android.horologist.compose.layout.ScalingLazyColumnState
@@ -142,7 +143,16 @@ fun MultiCredentialsFoldScreen(
)
}
}
- item { SignInOptionsChip(onSignInOptionsClicked) }
+
+ state.authenticationEntryList.forEach { authenticationEntryInfo ->
+ item {
+ LockedProviderChip(authenticationEntryInfo) {
+ // TODO(b/322797032) invoke LockedProviderScreen here using flow engine
+ }
+ }
+ }
+
+ item { SignInOptionsChip(onSignInOptionsClicked)}
item { DismissChip(onCancelClicked) }
}
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
index 4c7f583fcb09..1f1a296dca9b 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
@@ -18,22 +18,19 @@
package com.android.credentialmanager.ui.screens.single.password
+import android.util.Log
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.SideEffect
-import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
-import androidx.hilt.navigation.compose.hiltViewModel
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import androidx.navigation.NavHostController
-import androidx.navigation.compose.rememberNavController
-import com.android.credentialmanager.CredentialSelectorUiState
+import com.android.credentialmanager.FlowEngine
import com.android.credentialmanager.R
+import com.android.credentialmanager.TAG
import com.android.credentialmanager.activity.StartBalIntentSenderForResultContract
+import com.android.credentialmanager.ktx.getIntentSenderRequest
import com.android.credentialmanager.ui.components.PasswordRow
import com.android.credentialmanager.ui.components.ContinueChip
import com.android.credentialmanager.ui.components.DismissChip
@@ -41,71 +38,30 @@ import com.android.credentialmanager.ui.components.SignInHeader
import com.android.credentialmanager.ui.components.SignInOptionsChip
import com.android.credentialmanager.ui.screens.single.SingleAccountScreen
import com.android.credentialmanager.model.get.CredentialEntryInfo
-import com.android.credentialmanager.ui.screens.single.UiState
import com.google.android.horologist.annotations.ExperimentalHorologistApi
import com.google.android.horologist.compose.layout.ScalingLazyColumnState
/**
* Screen that shows sign in with provider credential.
*
- * @param credentialSelectorUiState The app bar view model.
+ * @param entry The password entry.
* @param columnState ScalingLazyColumn configuration to be be applied to SingleAccountScreen
* @param modifier styling for composable
- * @param viewModel ViewModel that updates ui state for this screen
- * @param navController handles navigation events from this screen
+ * @param flowEngine [FlowEngine] that updates ui state for this screen
*/
@OptIn(ExperimentalHorologistApi::class)
@Composable
fun SinglePasswordScreen(
- credentialSelectorUiState: CredentialSelectorUiState.Get.SingleEntry,
- columnState: ScalingLazyColumnState,
- modifier: Modifier = Modifier,
- viewModel: SinglePasswordScreenViewModel = hiltViewModel(),
- navController: NavHostController = rememberNavController(),
-) {
- viewModel.initialize(credentialSelectorUiState.entry)
-
- val uiState by viewModel.uiState.collectAsStateWithLifecycle()
-
- when (val state = uiState) {
- UiState.CredentialScreen -> {
- SinglePasswordScreen(
- credentialSelectorUiState.entry,
- columnState,
- modifier,
- viewModel
- )
- }
-
- is UiState.CredentialSelected -> {
- val launcher = rememberLauncherForActivityResult(
- StartBalIntentSenderForResultContract()
- ) {
- viewModel.onPasswordInfoRetrieved(it.resultCode, null)
- }
-
- SideEffect {
- state.intentSenderRequest?.let {
- launcher.launch(it)
- }
- }
- }
-
- UiState.Cancel -> {
- // TODO(b/322797032) add valid navigation path here for going back
- navController.popBackStack()
- }
- }
-}
-
-@OptIn(ExperimentalHorologistApi::class)
-@Composable
-private fun SinglePasswordScreen(
entry: CredentialEntryInfo,
columnState: ScalingLazyColumnState,
modifier: Modifier = Modifier,
- viewModel: SinglePasswordScreenViewModel,
+ flowEngine: FlowEngine,
) {
+ val launcher = rememberLauncherForActivityResult(
+ StartBalIntentSenderForResultContract()
+ ) {
+ flowEngine.sendSelectionResult(entry, it.resultCode, it.data)
+ }
SingleAccountScreen(
headerContent = {
SignInHeader(
@@ -124,9 +80,13 @@ private fun SinglePasswordScreen(
) {
item {
Column {
- ContinueChip(viewModel::onContinueClick)
- SignInOptionsChip(viewModel::onSignInOptionsClick)
- DismissChip(viewModel::onDismissClick)
+ ContinueChip {
+ entry.getIntentSenderRequest()?.let {
+ launcher.launch(it)
+ } ?: Log.w(TAG, "Cannot parse IntentSenderRequest")
+ }
+ SignInOptionsChip{ flowEngine.openSecondaryScreen() }
+ DismissChip { flowEngine.cancel() }
}
}
}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt
deleted file mode 100644
index 8debecbac599..000000000000
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2024 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.0N
- *
- * 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.credentialmanager.ui.screens.single.password
-
-import android.content.Intent
-import android.credentials.selection.UserSelectionDialogResult
-import android.credentials.selection.ProviderPendingIntentResponse
-import androidx.annotation.MainThread
-import androidx.lifecycle.ViewModel
-import com.android.credentialmanager.ktx.getIntentSenderRequest
-import com.android.credentialmanager.model.Request
-import com.android.credentialmanager.client.CredentialManagerClient
-import com.android.credentialmanager.model.get.CredentialEntryInfo
-import com.android.credentialmanager.ui.screens.single.UiState
-import dagger.hilt.android.lifecycle.HiltViewModel
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import javax.inject.Inject
-
-@HiltViewModel
-class SinglePasswordScreenViewModel @Inject constructor(
- private val credentialManagerClient: CredentialManagerClient,
-) : ViewModel() {
-
- private lateinit var requestGet: Request.Get
- private lateinit var entryInfo: CredentialEntryInfo
-
- private val _uiState =
- MutableStateFlow<UiState>(UiState.CredentialScreen)
- val uiState: StateFlow<UiState> = _uiState
-
- @MainThread
- fun initialize(entryInfo: CredentialEntryInfo) {
- this.entryInfo = entryInfo
- }
-
- fun onDismissClick() {
- _uiState.value = UiState.Cancel
- }
-
- fun onContinueClick() {
- _uiState.value = UiState.CredentialSelected(
- intentSenderRequest = entryInfo.getIntentSenderRequest()
- )
- }
-
- fun onSignInOptionsClick() {
- }
-
- fun onPasswordInfoRetrieved(
- resultCode: Int? = null,
- resultData: Intent? = null,
- ) {
- val userSelectionDialogResult = UserSelectionDialogResult(
- requestGet.token,
- entryInfo.providerId,
- entryInfo.entryKey,
- entryInfo.entrySubkey,
- if (resultCode != null) ProviderPendingIntentResponse(resultCode, resultData) else null
- )
- credentialManagerClient.sendResult(userSelectionDialogResult)
- }
-}
diff --git a/packages/FusedLocation/AndroidManifest.xml b/packages/FusedLocation/AndroidManifest.xml
index 05561d79c3d5..158c33ae2035 100644
--- a/packages/FusedLocation/AndroidManifest.xml
+++ b/packages/FusedLocation/AndroidManifest.xml
@@ -28,6 +28,7 @@
<uses-permission android:name="android.permission.INSTALL_LOCATION_PROVIDER" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
+ <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<application
android:label="@string/app_label"
@@ -49,5 +50,17 @@
<meta-data android:name="serviceVersion" android:value="0" />
<meta-data android:name="serviceIsMultiuser" android:value="true" />
</service>
+
+ <!-- GNSS overlay Service that LocationManagerService binds to.
+ LocationManagerService will bind to the service with the highest
+ version. -->
+ <service android:name="com.android.location.gnss.GnssOverlayLocationService"
+ android:exported="false">
+ <intent-filter>
+ <action android:name="android.location.provider.action.GNSS_PROVIDER" />
+ </intent-filter>
+ <meta-data android:name="serviceVersion" android:value="0" />
+ <meta-data android:name="serviceIsMultiuser" android:value="true" />
+ </service>
</application>
</manifest>
diff --git a/packages/FusedLocation/src/com/android/location/gnss/GnssOverlayLocationProvider.java b/packages/FusedLocation/src/com/android/location/gnss/GnssOverlayLocationProvider.java
new file mode 100644
index 000000000000..c6576e39de99
--- /dev/null
+++ b/packages/FusedLocation/src/com/android/location/gnss/GnssOverlayLocationProvider.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2024 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.location.gnss;
+
+import static android.location.provider.ProviderProperties.ACCURACY_FINE;
+import static android.location.provider.ProviderProperties.POWER_USAGE_HIGH;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.location.LocationRequest;
+import android.location.provider.LocationProviderBase;
+import android.location.provider.ProviderProperties;
+import android.location.provider.ProviderRequest;
+import android.os.Bundle;
+import android.util.SparseArray;
+
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ConcurrentUtils;
+
+import java.util.List;
+
+/** Basic pass-through GNSS location provider implementation. */
+public class GnssOverlayLocationProvider extends LocationProviderBase {
+
+ private static final String TAG = "GnssOverlay";
+
+ private static final ProviderProperties PROPERTIES = new ProviderProperties.Builder()
+ .setHasAltitudeSupport(true)
+ .setHasSpeedSupport(true)
+ .setHasBearingSupport(true)
+ .setPowerUsage(POWER_USAGE_HIGH)
+ .setAccuracy(ACCURACY_FINE)
+ .build();
+
+ @GuardedBy("mPendingFlushes")
+ private final SparseArray<OnFlushCompleteCallback> mPendingFlushes = new SparseArray<>();
+
+ private final LocationManager mLocationManager;
+
+ private final GnssLocationListener mGnssLocationListener = new GnssLocationListener();
+
+ @GuardedBy("mPendingFlushes")
+ private int mFlushCode = 0;
+
+ /** Location listener for receiving locations from LocationManager. */
+ private class GnssLocationListener implements LocationListener {
+ @Override
+ public void onLocationChanged(Location location) {
+ reportLocation(location);
+ }
+
+ @Override
+ public void onLocationChanged(List<Location> locations) {
+ reportLocations(locations);
+ }
+
+ @Override
+ public void onFlushComplete(int requestCode) {
+ OnFlushCompleteCallback flushCompleteCallback;
+ synchronized (mPendingFlushes) {
+ flushCompleteCallback = mPendingFlushes.get(requestCode);
+ mPendingFlushes.remove(requestCode);
+ }
+ if (flushCompleteCallback != null) {
+ flushCompleteCallback.onFlushComplete();
+ }
+ }
+ }
+
+ public GnssOverlayLocationProvider(Context context) {
+ super(context, TAG, PROPERTIES);
+ mLocationManager = context.getSystemService(LocationManager.class);
+ }
+
+ void start() {
+ }
+
+ void stop() {
+ mLocationManager.removeUpdates(mGnssLocationListener);
+ }
+
+ @Override
+ public void onSendExtraCommand(String command, @Nullable Bundle extras) {
+ mLocationManager.sendExtraCommand(LocationManager.GPS_HARDWARE_PROVIDER, command, extras);
+ }
+
+ @Override
+ public void onFlush(OnFlushCompleteCallback callback) {
+ int flushCodeCopy;
+ synchronized (mPendingFlushes) {
+ flushCodeCopy = mFlushCode++;
+ mPendingFlushes.put(flushCodeCopy, callback);
+ }
+ mLocationManager.requestFlush(
+ LocationManager.GPS_HARDWARE_PROVIDER, mGnssLocationListener, flushCodeCopy);
+ }
+
+ @Override
+ public void onSetRequest(ProviderRequest request) {
+ if (request.isActive()) {
+ mLocationManager.requestLocationUpdates(
+ LocationManager.GPS_HARDWARE_PROVIDER,
+ new LocationRequest.Builder(request.getIntervalMillis())
+ .setMaxUpdateDelayMillis(request.getMaxUpdateDelayMillis())
+ .setLowPower(request.isLowPower())
+ .setLocationSettingsIgnored(request.isLocationSettingsIgnored())
+ .setWorkSource(request.getWorkSource())
+ .setQuality(request.getQuality())
+ .build(),
+ ConcurrentUtils.DIRECT_EXECUTOR,
+ mGnssLocationListener);
+ } else {
+ mLocationManager.removeUpdates(mGnssLocationListener);
+ }
+ }
+}
diff --git a/packages/FusedLocation/src/com/android/location/gnss/GnssOverlayLocationService.java b/packages/FusedLocation/src/com/android/location/gnss/GnssOverlayLocationService.java
new file mode 100644
index 000000000000..dd034fec9495
--- /dev/null
+++ b/packages/FusedLocation/src/com/android/location/gnss/GnssOverlayLocationService.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2024 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.location.gnss;
+
+import android.annotation.Nullable;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+public class GnssOverlayLocationService extends Service {
+
+ @Nullable private GnssOverlayLocationProvider mProvider;
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ if (mProvider == null) {
+ mProvider = new GnssOverlayLocationProvider(this);
+ mProvider.start();
+ }
+
+ return mProvider.getBinder();
+ }
+
+ @Override
+ public void onDestroy() {
+ if (mProvider != null) {
+ mProvider.stop();
+ mProvider = null;
+ }
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ }
+}
diff --git a/packages/FusedLocation/test/src/com/android/location/gnss/tests/GnssOverlayLocationServiceTest.java b/packages/FusedLocation/test/src/com/android/location/gnss/tests/GnssOverlayLocationServiceTest.java
new file mode 100644
index 000000000000..5b33deb60759
--- /dev/null
+++ b/packages/FusedLocation/test/src/com/android/location/gnss/tests/GnssOverlayLocationServiceTest.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2024 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.location.gnss.tests;
+
+import static android.location.LocationManager.GPS_HARDWARE_PROVIDER;
+
+import static androidx.test.ext.truth.location.LocationSubject.assertThat;
+
+import android.content.Context;
+import android.location.Criteria;
+import android.location.Location;
+import android.location.LocationManager;
+import android.location.LocationRequest;
+import android.location.provider.ILocationProvider;
+import android.location.provider.ILocationProviderManager;
+import android.location.provider.ProviderProperties;
+import android.location.provider.ProviderRequest;
+import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.location.gnss.GnssOverlayLocationProvider;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+public class GnssOverlayLocationServiceTest {
+
+ private static final String TAG = "GnssOverlayLocationServiceTest";
+
+ private static final long TIMEOUT_MS = 5000;
+
+ private Random mRandom;
+ private LocationManager mLocationManager;
+
+ private ILocationProvider mProvider;
+ private LocationProviderManagerCapture mManager;
+
+ @Before
+ public void setUp() throws Exception {
+ long seed = System.currentTimeMillis();
+ Log.i(TAG, "location seed: " + seed);
+
+ Context context = ApplicationProvider.getApplicationContext();
+ mRandom = new Random(seed);
+ mLocationManager = context.getSystemService(LocationManager.class);
+
+ setMockLocation(true);
+
+ mManager = new LocationProviderManagerCapture();
+ mProvider = ILocationProvider.Stub.asInterface(
+ new GnssOverlayLocationProvider(context).getBinder());
+ mProvider.setLocationProviderManager(mManager);
+
+ mLocationManager.addTestProvider(GPS_HARDWARE_PROVIDER,
+ true,
+ false,
+ true,
+ false,
+ false,
+ false,
+ false,
+ Criteria.POWER_MEDIUM,
+ Criteria.ACCURACY_FINE);
+ mLocationManager.setTestProviderEnabled(GPS_HARDWARE_PROVIDER, true);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ for (String provider : mLocationManager.getAllProviders()) {
+ mLocationManager.removeTestProvider(provider);
+ }
+
+ setMockLocation(false);
+ }
+
+ @Test
+ public void testGpsRequest() throws Exception {
+ mProvider.setRequest(
+ new ProviderRequest.Builder()
+ .setQuality(LocationRequest.QUALITY_HIGH_ACCURACY)
+ .setIntervalMillis(1000)
+ .build());
+
+ Location location = createLocation(GPS_HARDWARE_PROVIDER, mRandom);
+ mLocationManager.setTestProviderLocation(GPS_HARDWARE_PROVIDER, location);
+
+ assertThat(mManager.getNextLocation(TIMEOUT_MS)).isEqualTo(location);
+ }
+
+ private static class LocationProviderManagerCapture extends ILocationProviderManager.Stub {
+
+ private final LinkedBlockingQueue<Location> mLocations;
+
+ private LocationProviderManagerCapture() {
+ mLocations = new LinkedBlockingQueue<>();
+ }
+
+ @Override
+ public void onInitialize(boolean allowed, ProviderProperties properties,
+ String attributionTag) {}
+
+ @Override
+ public void onSetAllowed(boolean allowed) {}
+
+ @Override
+ public void onSetProperties(ProviderProperties properties) {}
+
+ @Override
+ public void onReportLocation(Location location) {
+ mLocations.add(location);
+ }
+
+ @Override
+ public void onReportLocations(List<Location> locations) {
+ mLocations.addAll(locations);
+ }
+
+ @Override
+ public void onFlushComplete() {}
+
+ public Location getNextLocation(long timeoutMs) throws InterruptedException {
+ return mLocations.poll(timeoutMs, TimeUnit.MILLISECONDS);
+ }
+ }
+
+ private static final double MIN_LATITUDE = -90D;
+ private static final double MAX_LATITUDE = 90D;
+ private static final double MIN_LONGITUDE = -180D;
+ private static final double MAX_LONGITUDE = 180D;
+
+ private static final float MIN_ACCURACY = 1;
+ private static final float MAX_ACCURACY = 100;
+
+ private static Location createLocation(String provider, Random random) {
+ return createLocation(provider,
+ MIN_LATITUDE + random.nextDouble() * (MAX_LATITUDE - MIN_LATITUDE),
+ MIN_LONGITUDE + random.nextDouble() * (MAX_LONGITUDE - MIN_LONGITUDE),
+ MIN_ACCURACY + random.nextFloat() * (MAX_ACCURACY - MIN_ACCURACY));
+ }
+
+ private static Location createLocation(String provider, double latitude, double longitude,
+ float accuracy) {
+ Location location = new Location(provider);
+ location.setLatitude(latitude);
+ location.setLongitude(longitude);
+ location.setAccuracy(accuracy);
+ location.setTime(System.currentTimeMillis());
+ location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
+ return location;
+ }
+
+ private static void setMockLocation(boolean allowed) throws IOException {
+ ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .executeShellCommand("appops set "
+ + InstrumentationRegistry.getTargetContext().getPackageName()
+ + " android:mock_location " + (allowed ? "allow" : "deny"));
+ try (FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ byte[] buffer = new byte[32768];
+ int count;
+ try {
+ while ((count = fis.read(buffer)) != -1) {
+ os.write(buffer, 0, count);
+ }
+ fis.close();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ Log.e(TAG, new String(os.toByteArray()));
+ }
+ }
+}
diff --git a/packages/PackageInstaller/res/values-af/strings.xml b/packages/PackageInstaller/res/values-af/strings.xml
index 7ceaa8123249..140fa36b341c 100644
--- a/packages/PackageInstaller/res/values-af/strings.xml
+++ b/packages/PackageInstaller/res/values-af/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Hierdie app sal in die agtergrond begin aflaai"</string>
<string name="restore" msgid="8460854736328970444">"Stel terug"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Jy is vanlyn"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Om hierdie app terug te stel, gaan jou internetverbinding na en probeer weer"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Iets het skeefgeloop"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Kon nie hierdie app terugstel nie"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Te min berging"</string>
diff --git a/packages/PackageInstaller/res/values-am/strings.xml b/packages/PackageInstaller/res/values-am/strings.xml
index 4208346dadb0..e127152c8377 100644
--- a/packages/PackageInstaller/res/values-am/strings.xml
+++ b/packages/PackageInstaller/res/values-am/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"ይህ መተግበሪያ በዳራ ማውረድ ይጀምራል።"</string>
<string name="restore" msgid="8460854736328970444">"ወደነበረበት መልስ"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"ከመስመር ውጭ ነዎት"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"ይህን መተግበሪያ ወደነበረበት ለመመለስ፣ የበይነመረብ ግንኙነትዎን ይፈትሹ እና እንደገና ይሞክሩ"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"የሆነ ስህተት ተከስቷል"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"ይህን መተግበሪያ ወደነበረበት ለመመለስ እየተሞከረ ሳለ አንድ ችግር ነበር"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"በቂ ማከማቻ የለም"</string>
diff --git a/packages/PackageInstaller/res/values-ar/strings.xml b/packages/PackageInstaller/res/values-ar/strings.xml
index 6bc508537065..de91c09c007f 100644
--- a/packages/PackageInstaller/res/values-ar/strings.xml
+++ b/packages/PackageInstaller/res/values-ar/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"سيبدأ تنزيل هذا التطبيق في الخلفية."</string>
<string name="restore" msgid="8460854736328970444">"استعادة"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"لا يتوفر اتصال بالإنترنت"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"لاستعادة هذا التطبيق، يُرجى التحقُّق من الاتصال بالإنترنت ثم إعادة المحاولة."</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"حدث خطأ"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"حدثت مشكلة أثناء محاولة استعادة هذا التطبيق."</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"مساحة التخزين غير كافية"</string>
diff --git a/packages/PackageInstaller/res/values-as/strings.xml b/packages/PackageInstaller/res/values-as/strings.xml
index e07cf7b3c6c4..323f7259d7e4 100644
--- a/packages/PackageInstaller/res/values-as/strings.xml
+++ b/packages/PackageInstaller/res/values-as/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"এই এপ্‌টোৱে নেপথ্যত ডাউনল’ড কৰিবলৈ আৰম্ভ কৰিব"</string>
<string name="restore" msgid="8460854736328970444">"পুনঃস্থাপন কৰক"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"আপুনি অফলাইন হৈ আছে"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"এই এপ্‌টো পুনঃস্থাপন কৰিবলৈ, আপোনাৰ ইণ্টাৰনেট সংযোগ পৰীক্ষা কৰক আৰু পুনৰ চেষ্টা কৰক"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"কিবা ভুল হ’ল"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"এই এপ্‌টো পুনঃস্থাপন কৰাত কিবা অসুবিধা হৈছিল"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"ষ্ট’ৰেজত পৰ্যাপ্ত ঠাই নাই"</string>
diff --git a/packages/PackageInstaller/res/values-az/strings.xml b/packages/PackageInstaller/res/values-az/strings.xml
index 6e7a84b701ea..8b4b68d7d963 100644
--- a/packages/PackageInstaller/res/values-az/strings.xml
+++ b/packages/PackageInstaller/res/values-az/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Bu tətbiq arxa fonda endirilməyə başlayacaq"</string>
<string name="restore" msgid="8460854736328970444">"Bərpa edin"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Oflaynsınız"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Bu tətbiqi bərpa etmək üçün internet bağlantısını yoxlayın və yenidən cəhd edin"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Xəta oldu"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Bu tətbiqi bərpa edərkən problem oldu"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Kifayət qədər yaddaş yoxdur"</string>
diff --git a/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml b/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml
index d7133ec2273e..ccb0aeb3cecd 100644
--- a/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml
+++ b/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Aplikacija će započeti preuzimanje u pozadini."</string>
<string name="restore" msgid="8460854736328970444">"Vrati"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Oflajn ste"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Da biste vratili ovu aplikaciju, proverite internet vezu i probajte ponovo."</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Došlo je do greške"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Došlo je do problema pri vraćanju ove aplikacije"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Nema dovoljno memorijskog prostora"</string>
diff --git a/packages/PackageInstaller/res/values-be/strings.xml b/packages/PackageInstaller/res/values-be/strings.xml
index 70ee292771ed..d1cb7d05e682 100644
--- a/packages/PackageInstaller/res/values-be/strings.xml
+++ b/packages/PackageInstaller/res/values-be/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Праграма пачне спампоўвацца ў фонавым рэжыме"</string>
<string name="restore" msgid="8460854736328970444">"Аднавіць"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Вы па-за сеткай"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Каб аднавіць гэту праграму, праверце падключэнне да інтэрнэту і паўтарыце спробу"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Адбылася памылка"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Пры аднаўленні праграмы ўзнікла праблема"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Не хапае месца ў сховішчы"</string>
diff --git a/packages/PackageInstaller/res/values-bg/strings.xml b/packages/PackageInstaller/res/values-bg/strings.xml
index c90009b01c13..f6efdf677343 100644
--- a/packages/PackageInstaller/res/values-bg/strings.xml
+++ b/packages/PackageInstaller/res/values-bg/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Това приложение ще започне да се изтегля на заден план"</string>
<string name="restore" msgid="8460854736328970444">"Възстановяване"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Офлайн сте"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"За да възстановите това приложение, проверете връзката си с интернет и опитайте отново"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Нещо се обърка"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"При опита за възстановяване на това приложение възникна проблем"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Няма достатъчно място в хранилището"</string>
diff --git a/packages/PackageInstaller/res/values-bn/strings.xml b/packages/PackageInstaller/res/values-bn/strings.xml
index 806c8863cbd2..231f451478de 100644
--- a/packages/PackageInstaller/res/values-bn/strings.xml
+++ b/packages/PackageInstaller/res/values-bn/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"এই অ্যাপটি ব্যাকগ্রাউন্ডে ডাউনলোড হওয়া শুরু হবে"</string>
<string name="restore" msgid="8460854736328970444">"ফিরিয়ে আনুন"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"আপনি অফলাইন আছেন"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"এই অ্যাপ ফিরিয়ে আনতে, আপনার ইন্টারনেট কানেকশন চেক করে আবার চেষ্টা করুন"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"কোনও সমস্যা হয়েছে"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"এই অ্যাপ ফিরিয়ে আনতে চেষ্টা করার সময় সমস্যা হয়েছে"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"পর্যাপ্ত স্টোরেজ নেই"</string>
diff --git a/packages/PackageInstaller/res/values-bs/strings.xml b/packages/PackageInstaller/res/values-bs/strings.xml
index 8f9c007421ab..c1f8698e49b0 100644
--- a/packages/PackageInstaller/res/values-bs/strings.xml
+++ b/packages/PackageInstaller/res/values-bs/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Preuzimanje aplikacije će započeti u pozadini"</string>
<string name="restore" msgid="8460854736328970444">"Vrati"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Offline ste"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Da vratite ovu aplikaciju, provjerite internetsku vezu i pokušajte ponovo"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Nešto nije uredu"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Došlo je do problema prilikom pokušaja vraćanja aplikacije"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Nema dovoljno prostora za pohranu"</string>
diff --git a/packages/PackageInstaller/res/values-ca/strings.xml b/packages/PackageInstaller/res/values-ca/strings.xml
index ad35c7abca59..2fb6fd86b956 100644
--- a/packages/PackageInstaller/res/values-ca/strings.xml
+++ b/packages/PackageInstaller/res/values-ca/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Aquesta aplicació començarà a baixar-se en segon pla"</string>
<string name="restore" msgid="8460854736328970444">"Restaura"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"No tens connexió"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Per restaurar aquesta aplicació, comprova la connexió a Internet i torna-ho a provar"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"S\'ha produït un error"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Hi ha hagut un problema en intentar restaurar aquesta aplicació"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"No hi ha prou emmagatzematge"</string>
diff --git a/packages/PackageInstaller/res/values-cs/strings.xml b/packages/PackageInstaller/res/values-cs/strings.xml
index c4bb8f1c8164..5147c52d4acc 100644
--- a/packages/PackageInstaller/res/values-cs/strings.xml
+++ b/packages/PackageInstaller/res/values-cs/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Aplikace se začne stahovat na pozadí"</string>
<string name="restore" msgid="8460854736328970444">"Obnovit"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Nejste připojeni k internetu"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Pokud chcete aplikaci obnovit, zkontrolujte připojení k internetu a zkuste to znovu"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Něco se pokazilo"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Při pokusu o obnovení této aplikace došlo k problému"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Nedostatek úložného prostoru"</string>
diff --git a/packages/PackageInstaller/res/values-da/strings.xml b/packages/PackageInstaller/res/values-da/strings.xml
index 835d1d2494dc..1638ef4c5b16 100644
--- a/packages/PackageInstaller/res/values-da/strings.xml
+++ b/packages/PackageInstaller/res/values-da/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Download af denne app startes i baggrunden"</string>
<string name="restore" msgid="8460854736328970444">"Gendan"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Du er offline"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Hvis du vil gendanne denne app, skal du tjekke din internetforbindelse og prøve igen"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Noget gik galt"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Der opstod et problem under gendannelsen af denne app"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Ikke nok lagerplads"</string>
diff --git a/packages/PackageInstaller/res/values-de/strings.xml b/packages/PackageInstaller/res/values-de/strings.xml
index eab0e9aec24e..ef9d207473c0 100644
--- a/packages/PackageInstaller/res/values-de/strings.xml
+++ b/packages/PackageInstaller/res/values-de/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Diese App beginnt im Hintergrund mit dem Herunterladen"</string>
<string name="restore" msgid="8460854736328970444">"Wiederherstellen"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Du bist offline"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Prüfe deine Internetverbindung und versuch es noch einmal, um die App wiederherzustellen"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Ein Fehler ist aufgetreten"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Beim Wiederherstellen der App ist ein Fehler aufgetreten"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Nicht genügend Speicherplatz"</string>
diff --git a/packages/PackageInstaller/res/values-el/strings.xml b/packages/PackageInstaller/res/values-el/strings.xml
index 6ab61daf8d9e..61aa4e65128a 100644
--- a/packages/PackageInstaller/res/values-el/strings.xml
+++ b/packages/PackageInstaller/res/values-el/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Η λήψη της εφαρμογής θα ξεκινήσει στο παρασκήνιο"</string>
<string name="restore" msgid="8460854736328970444">"Επαναφορά"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Είστε εκτός σύνδεσης"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Για να επαναφέρετε αυτή την εφαρμογή, ελέγξτε τη σύνδεσή σας στο διαδίκτυο και δοκιμάστε ξανά"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Κάτι πήγε στραβά"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Παρουσιάστηκε κάποιο πρόβλημα κατά την επαναφορά αυτής της εφαρμογής"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Δεν επαρκεί ο αποθηκευτικός χώρος"</string>
diff --git a/packages/PackageInstaller/res/values-en-rAU/strings.xml b/packages/PackageInstaller/res/values-en-rAU/strings.xml
index df336d1ebe84..88c33182b1d3 100644
--- a/packages/PackageInstaller/res/values-en-rAU/strings.xml
+++ b/packages/PackageInstaller/res/values-en-rAU/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"This app will start downloading in the background"</string>
<string name="restore" msgid="8460854736328970444">"Restore"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"You\'re offline"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"To restore this app, please check your Internet connection and try again"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Something went wrong"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"There was a problem trying to restore this app"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Not enough storage"</string>
diff --git a/packages/PackageInstaller/res/values-en-rGB/strings.xml b/packages/PackageInstaller/res/values-en-rGB/strings.xml
index df336d1ebe84..88c33182b1d3 100644
--- a/packages/PackageInstaller/res/values-en-rGB/strings.xml
+++ b/packages/PackageInstaller/res/values-en-rGB/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"This app will start downloading in the background"</string>
<string name="restore" msgid="8460854736328970444">"Restore"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"You\'re offline"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"To restore this app, please check your Internet connection and try again"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Something went wrong"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"There was a problem trying to restore this app"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Not enough storage"</string>
diff --git a/packages/PackageInstaller/res/values-en-rIN/strings.xml b/packages/PackageInstaller/res/values-en-rIN/strings.xml
index df336d1ebe84..88c33182b1d3 100644
--- a/packages/PackageInstaller/res/values-en-rIN/strings.xml
+++ b/packages/PackageInstaller/res/values-en-rIN/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"This app will start downloading in the background"</string>
<string name="restore" msgid="8460854736328970444">"Restore"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"You\'re offline"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"To restore this app, please check your Internet connection and try again"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Something went wrong"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"There was a problem trying to restore this app"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Not enough storage"</string>
diff --git a/packages/PackageInstaller/res/values-es-rUS/strings.xml b/packages/PackageInstaller/res/values-es-rUS/strings.xml
index 99e0df189550..0e0bda550fe7 100644
--- a/packages/PackageInstaller/res/values-es-rUS/strings.xml
+++ b/packages/PackageInstaller/res/values-es-rUS/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Esta app comenzará la descarga en segundo plano"</string>
<string name="restore" msgid="8460854736328970444">"Restablecer"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"No tienes conexión"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Para restablecer esta app, revisa tu conexión a Internet y vuelve a intentarlo"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Se produjo un error"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Hubo un problema al intentar restablecer esta app"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"No hay suficiente espacio de almacenamiento"</string>
diff --git a/packages/PackageInstaller/res/values-es/strings.xml b/packages/PackageInstaller/res/values-es/strings.xml
index 53fbada7d80f..5c5bcac4d19a 100644
--- a/packages/PackageInstaller/res/values-es/strings.xml
+++ b/packages/PackageInstaller/res/values-es/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Esta aplicación comenzará a descargarse en segundo plano"</string>
<string name="restore" msgid="8460854736328970444">"Restaurar"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"No tienes conexión a Internet"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Para restaurar esta aplicación, comprueba tu conexión a Internet y vuelve a intentarlo"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Se ha producido un error"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"No se ha podido restaurar esta aplicación"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"No hay suficiente espacio"</string>
diff --git a/packages/PackageInstaller/res/values-et/strings.xml b/packages/PackageInstaller/res/values-et/strings.xml
index 3be261138c53..29eb94fa10c3 100644
--- a/packages/PackageInstaller/res/values-et/strings.xml
+++ b/packages/PackageInstaller/res/values-et/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Selle rakenduse allalaadimine algab taustal"</string>
<string name="restore" msgid="8460854736328970444">"Taasta"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Võrguühendus puudub"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Selle rakenduse taastamiseks kontrollige oma internetiühendust ja proovige uuesti"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Midagi läks valesti"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Selle rakenduse taastamisel ilmnes probleem"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Pole piisavalt salvestusruumi"</string>
diff --git a/packages/PackageInstaller/res/values-eu/strings.xml b/packages/PackageInstaller/res/values-eu/strings.xml
index e54502568a9b..7075ed0febf0 100644
--- a/packages/PackageInstaller/res/values-eu/strings.xml
+++ b/packages/PackageInstaller/res/values-eu/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Atzeko planoan deskargatuko da aplikazioa"</string>
<string name="restore" msgid="8460854736328970444">"Leheneratu"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Ez zaude konektatuta Internetera"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Aplikazioa leheneratzeko, egiaztatu Internetera konektatuta zaudela eta saiatu berriro"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Arazoren bat izan da"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Arazo bat izan da aplikazioa leheneratzean"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Ez dago behar adina toki"</string>
diff --git a/packages/PackageInstaller/res/values-fa/strings.xml b/packages/PackageInstaller/res/values-fa/strings.xml
index 9a70ca04a7ab..288653ad5b36 100644
--- a/packages/PackageInstaller/res/values-fa/strings.xml
+++ b/packages/PackageInstaller/res/values-fa/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"این برنامه در پس‌زمینه شروع به بارگیری می‌کند"</string>
<string name="restore" msgid="8460854736328970444">"بازیابی"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"آفلاین هستید"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"برای بازیابی این برنامه، اتصال اینترنت را بررسی کنید و دوباره امتحان کنید"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"مشکلی پیش آمد"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"هنگام بازیابی این برنامه مشکلی پیش آمد"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"فضای ذخیره‌سازی کافی نیست"</string>
diff --git a/packages/PackageInstaller/res/values-fi/strings.xml b/packages/PackageInstaller/res/values-fi/strings.xml
index ce02ad3bf9bc..cbe94c387c11 100644
--- a/packages/PackageInstaller/res/values-fi/strings.xml
+++ b/packages/PackageInstaller/res/values-fi/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Sovelluksen lataus aloitetaan taustalla"</string>
<string name="restore" msgid="8460854736328970444">"Palauta"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Olet offline-tilassa"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Palauta tämä sovellus tarkistamalla internetyhteys ja yrittämällä uudelleen"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Jotain meni pieleen"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Sovelluksen palauttamisessa oli ongelma"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Tallennustila ei riitä"</string>
diff --git a/packages/PackageInstaller/res/values-fr-rCA/strings.xml b/packages/PackageInstaller/res/values-fr-rCA/strings.xml
index 8abcf43cdb0a..de38df3ad708 100644
--- a/packages/PackageInstaller/res/values-fr-rCA/strings.xml
+++ b/packages/PackageInstaller/res/values-fr-rCA/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Le téléchargement de cette application commencera en arrière-plan"</string>
<string name="restore" msgid="8460854736328970444">"Restaurer"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Vous êtes hors ligne"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Pour restaurer cette application, vérifiez votre connexion Internet et réessayez."</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Un problème est survenu"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Un problème est survenu lors de la restauration de cette application"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Espace de stockage insuffisant"</string>
diff --git a/packages/PackageInstaller/res/values-fr/strings.xml b/packages/PackageInstaller/res/values-fr/strings.xml
index 03d000d6d927..531190327165 100644
--- a/packages/PackageInstaller/res/values-fr/strings.xml
+++ b/packages/PackageInstaller/res/values-fr/strings.xml
@@ -85,7 +85,7 @@
<string name="uninstalling_cloned_app" msgid="1826380164974984870">"Suppression du clone <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
<string name="uninstall_failed_device_policy_manager" msgid="785293813665540305">"Impossible de désinstaller une application d\'administration de l\'appareil active"</string>
<string name="uninstall_failed_device_policy_manager_of_user" msgid="4813104025494168064">"Impossible de désinstaller une application d\'administration de l\'appareil active pour <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
- <string name="uninstall_all_blocked_profile_owner" msgid="2009393666026751501">"Cette application nécessaire pour certains utilisateurs ou profils a été désinstallée pour d\'autres"</string>
+ <string name="uninstall_all_blocked_profile_owner" msgid="2009393666026751501">"Cette application est requise pour certains utilisateurs ou profils et a été désinstallée pour d\'autres"</string>
<string name="uninstall_blocked_profile_owner" msgid="6373897407002404848">"Impossible de désinstaller l\'application, car elle est nécessaire pour votre profil."</string>
<string name="uninstall_blocked_device_owner" msgid="6724602931761073901">"Impossible désinstaller appli, car elle est requise par administrateur appareil."</string>
<string name="manage_device_administrators" msgid="3092696419363842816">"Gérer les applis d\'administration de l\'appareil"</string>
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Cette application commencera à se télécharger en arrière-plan"</string>
<string name="restore" msgid="8460854736328970444">"Restaurer"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Vous n\'êtes pas connecté"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Pour restaurer cette appli, vérifiez votre connexion Internet, puis réessayez"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Un problème est survenu"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Un problème est survenu lors de la restauration de cette application"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Espace de stockage insuffisant"</string>
diff --git a/packages/PackageInstaller/res/values-gl/strings.xml b/packages/PackageInstaller/res/values-gl/strings.xml
index 6b3c930e6311..af8a14c2ff63 100644
--- a/packages/PackageInstaller/res/values-gl/strings.xml
+++ b/packages/PackageInstaller/res/values-gl/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Esta aplicación comezará a descargarse en segundo plano"</string>
<string name="restore" msgid="8460854736328970444">"Restaurar"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Estás sen conexión"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Para restaurar a aplicación, comproba a túa conexión a Internet e téntao de novo"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Produciuse un erro"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Produciuse un problema ao restaurar esta aplicación"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Non hai almacenamento suficiente"</string>
diff --git a/packages/PackageInstaller/res/values-gu/strings.xml b/packages/PackageInstaller/res/values-gu/strings.xml
index 202888cd0203..f642e145c78f 100644
--- a/packages/PackageInstaller/res/values-gu/strings.xml
+++ b/packages/PackageInstaller/res/values-gu/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"બૅકગ્રાઉન્ડમાં આ ઍપ ડાઉનલોડ થવાનું શરૂ થશે"</string>
<string name="restore" msgid="8460854736328970444">"રિસ્ટોર કરો"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"તમે ઑફલાઇન છો"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"આ ઍપ રિસ્ટોર કરવા માટે, તમારું ઇન્ટરનેટ કનેક્શન ચેક કરો અને ફરી પ્રયત્ન કરો"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"કંઈક ખોટું થયું"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"આ ઍપને રિસ્ટોર કરવામાં કોઈ સમસ્યા આવી હતી"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"પર્યાપ્ત સ્ટોરેજ નથી"</string>
diff --git a/packages/PackageInstaller/res/values-hi/strings.xml b/packages/PackageInstaller/res/values-hi/strings.xml
index 1e2d20dcbbea..ce71f17d0df1 100644
--- a/packages/PackageInstaller/res/values-hi/strings.xml
+++ b/packages/PackageInstaller/res/values-hi/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"यह ऐप्लिकेशन, बैकग्राउंड में डाउनलोड होना शुरू हो जाएगा"</string>
<string name="restore" msgid="8460854736328970444">"वापस लाएं"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"आप ऑफ़लाइन हैं"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"इस ऐप्लिकेशन को वापस लाने के लिए, अपने इंटरनेट कनेक्शन की जांच करें और फिर से कोशिश करें"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"कोई गड़बड़ी हुई"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"इस ऐप्लिकेशन को वापस लाने में समस्या हुई"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"डिवाइस का स्टोरेज ज़रूरत से कम है"</string>
diff --git a/packages/PackageInstaller/res/values-hr/strings.xml b/packages/PackageInstaller/res/values-hr/strings.xml
index 1010db8b64f5..92ad7f049606 100644
--- a/packages/PackageInstaller/res/values-hr/strings.xml
+++ b/packages/PackageInstaller/res/values-hr/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Ova aplikacija počet će se preuzimati u pozadini"</string>
<string name="restore" msgid="8460854736328970444">"Vrati"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Niste povezani s internetom"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Da biste vratili ovu aplikaciju, provjerite internetsku vezu i pokušajte ponovo"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Nešto nije u redu"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Došlo je do problema s vraćanjem aplikacije"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Nema dovoljno prostora za pohranu"</string>
diff --git a/packages/PackageInstaller/res/values-hu/strings.xml b/packages/PackageInstaller/res/values-hu/strings.xml
index 74de4a7280a8..436d6ce3a192 100644
--- a/packages/PackageInstaller/res/values-hu/strings.xml
+++ b/packages/PackageInstaller/res/values-hu/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"A háttérben megkezdődik az app letöltése"</string>
<string name="restore" msgid="8460854736328970444">"Visszaállítás"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Az eszköz offline állapotban van"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Az alkalmazás visszaállításához ellenőrizze az internetkapcsolatot, majd próbálkozzon újra."</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Hiba történt"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Hiba történt az alkalmazás visszaállítása során."</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Nincs elegendő tárhely"</string>
diff --git a/packages/PackageInstaller/res/values-hy/strings.xml b/packages/PackageInstaller/res/values-hy/strings.xml
index 2121f8bd9e60..f2bc41e21e88 100644
--- a/packages/PackageInstaller/res/values-hy/strings.xml
+++ b/packages/PackageInstaller/res/values-hy/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Այս հավելվածը կներբեռնվի ֆոնային ռեժիմում"</string>
<string name="restore" msgid="8460854736328970444">"Վերականգնել"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Կապ չկա"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Այս հավելվածը վերականգնելու համար ստուգեք ձեր ինտերնետ կապը և նորից փորձեք"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Սխալ առաջացավ"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Չհաջողվեց վերականգնել այս հավելվածը"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Բավարար տարածք չկա"</string>
diff --git a/packages/PackageInstaller/res/values-in/strings.xml b/packages/PackageInstaller/res/values-in/strings.xml
index d2d912dccdb2..8115b50d2dad 100644
--- a/packages/PackageInstaller/res/values-in/strings.xml
+++ b/packages/PackageInstaller/res/values-in/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Aplikasi ini akan mulai didownload di latar belakang"</string>
<string name="restore" msgid="8460854736328970444">"Pulihkan"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Anda offline"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Untuk memulihkan aplikasi ini, periksa koneksi internet Anda, lalu coba lagi"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Terjadi error"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Terjadi error saat mencoba memulihkan aplikasi ini"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Penyimpanan tidak cukup"</string>
diff --git a/packages/PackageInstaller/res/values-is/strings.xml b/packages/PackageInstaller/res/values-is/strings.xml
index e49f5559a5c6..9d24d1859fb9 100644
--- a/packages/PackageInstaller/res/values-is/strings.xml
+++ b/packages/PackageInstaller/res/values-is/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Forritið verður sótt í bakgrunni"</string>
<string name="restore" msgid="8460854736328970444">"Endurheimta"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Þú ert ekki á netinu"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Til að endurheimta forritið skaltu athuga nettenginguna þína og reyna aftur"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Eitthvað fór úrskeiðis"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Vandamál kom upp við að endurheimta þetta forrit"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Ekki nógu mikið geymslurými"</string>
diff --git a/packages/PackageInstaller/res/values-it/strings.xml b/packages/PackageInstaller/res/values-it/strings.xml
index 9d1be0a4b041..20fc299edd01 100644
--- a/packages/PackageInstaller/res/values-it/strings.xml
+++ b/packages/PackageInstaller/res/values-it/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Il download di quest\'app inizierà in background"</string>
<string name="restore" msgid="8460854736328970444">"Ripristina"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Sei offline"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Per ripristinare questa app, controlla la tua connessione a internet e riprova"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Si è verificato un problema"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Si è verificato un problema durante il ripristino di questa app"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Spazio di archiviazione insufficiente"</string>
diff --git a/packages/PackageInstaller/res/values-iw/strings.xml b/packages/PackageInstaller/res/values-iw/strings.xml
index 79d95b417478..1c7bd117c355 100644
--- a/packages/PackageInstaller/res/values-iw/strings.xml
+++ b/packages/PackageInstaller/res/values-iw/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"תהליך ההורדה של האפליקציה יתחיל ברקע"</string>
<string name="restore" msgid="8460854736328970444">"שחזור"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"אין חיבור לאינטרנט"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"כדי לשחזר את האפליקציה הזו, מומלץ לבדוק את החיבור לאינטרנט ולנסות שוב."</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"משהו השתבש"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"הייתה בעיה בניסיון לשחזר את האפליקציה הזו"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"אין מספיק נפח אחסון פנוי"</string>
diff --git a/packages/PackageInstaller/res/values-ja/strings.xml b/packages/PackageInstaller/res/values-ja/strings.xml
index 2660797bab11..5d961f6e6ed7 100644
--- a/packages/PackageInstaller/res/values-ja/strings.xml
+++ b/packages/PackageInstaller/res/values-ja/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"このアプリのダウンロードをバックグラウンドで開始します"</string>
<string name="restore" msgid="8460854736328970444">"復元する"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"オフラインです"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"このアプリを復元するには、インターネット接続を確認してからもう一度お試しください"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"エラーが発生しました"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"このアプリを復元しようとしたときに問題が発生しました"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"空き容量が不足しています"</string>
diff --git a/packages/PackageInstaller/res/values-ka/strings.xml b/packages/PackageInstaller/res/values-ka/strings.xml
index d5f56852315e..29cc33f5101e 100644
--- a/packages/PackageInstaller/res/values-ka/strings.xml
+++ b/packages/PackageInstaller/res/values-ka/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"ამ აპის ჩამოტვირთვა ფონურ რეჟიმში დაიწყება"</string>
<string name="restore" msgid="8460854736328970444">"აღდგენა"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"თქვენ ხაზგარეშე რეჟიმში ხართ"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"აპის აღსადგენად შეამოწმეთ ინტერნეტთან კავშირი და სცადეთ ხელახლა"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"წარმოიქმნა შეფერხება"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"ამ აპის აღდგენისას წარმოიქმნა პრობლემა"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"მეხსიერება საკმარისი არ არის"</string>
diff --git a/packages/PackageInstaller/res/values-kk/strings.xml b/packages/PackageInstaller/res/values-kk/strings.xml
index 5da373afdabe..a13cc4bf6917 100644
--- a/packages/PackageInstaller/res/values-kk/strings.xml
+++ b/packages/PackageInstaller/res/values-kk/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Бұл қолданба фондық режимде жүктеп алына бастайды."</string>
<string name="restore" msgid="8460854736328970444">"Қалпына келтіру"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Құрылғыңыз офлайн режимде"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Бұл қолданбаны қалпына келтіру үшін интернет байланысын тексеріп, қайталап көріңіз."</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Бірдеңе дұрыс болмады"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Бұл құрылғыны қалпына келтіру үшін әрекет жасалғанда мәселе туындады."</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Жадта орын жеткіліксіз"</string>
diff --git a/packages/PackageInstaller/res/values-km/strings.xml b/packages/PackageInstaller/res/values-km/strings.xml
index 4b57163e7e60..2ec2f4d615f8 100644
--- a/packages/PackageInstaller/res/values-km/strings.xml
+++ b/packages/PackageInstaller/res/values-km/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"កម្មវិធីនេះនឹងចាប់ផ្ដើមទាញយកនៅផ្ទៃខាងក្រោយ"</string>
<string name="restore" msgid="8460854736328970444">"ស្ដារ"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"អ្នកគ្មាន​អ៊ីនធឺណិត​ទេ"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"ដើម្បីស្ដារកម្មវិធីនេះ សូមពិនិត្យមើលការតភ្ជាប់អ៊ីនធឺណិតរបស់អ្នក រួចព្យាយាមម្ដងទៀត"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"មានអ្វីមួយខុសប្រក្រតី"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"មានបញ្ហាក្នុងការព្យាយាមស្ដារកម្មវិធីនេះ"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"ទំហំផ្ទុក​មិន​គ្រប់គ្រាន់"</string>
diff --git a/packages/PackageInstaller/res/values-kn/strings.xml b/packages/PackageInstaller/res/values-kn/strings.xml
index a1ebc2a6b216..f26ac2d0229a 100644
--- a/packages/PackageInstaller/res/values-kn/strings.xml
+++ b/packages/PackageInstaller/res/values-kn/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"ಹಿನ್ನೆಲೆಯಲ್ಲಿ ಡೌನ್‌ಲೋಡ್ ಆಗಲು ಈ ಆ್ಯಪ್ ಪ್ರಾರಂಭವಾಗುತ್ತದೆ"</string>
<string name="restore" msgid="8460854736328970444">"ಮರುಸ್ಥಾಪಿಸಿ"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"ನೀವು ಆಫ್‌ಲೈನ್‌ನಲ್ಲಿರುವಿರಿ"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"ಈ ಆ್ಯಪ್ ಅನ್ನು ಮರುಸ್ಥಾಪಿಸಲು, ನಿಮ್ಮ ಇಂಟರ್ನೆಟ್ ಕನೆಕ್ಷನ್ ಅನ್ನು ಪರಿಶೀಲಿಸಿ ಹಾಗೂ ಪುನಃ ಪ್ರಯತ್ನಿಸಿ"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"ಏನೋ ತಪ್ಪಾಗಿದೆ"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"ಈ ಆ್ಯಪ್ ಅನ್ನು ಮರುಸ್ಥಾಪಿಸಲು ಪ್ರಯತ್ನಿಸುವಾಗ ಸಮಸ್ಯೆ ಕಂಡುಬಂದಿದೆ"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"ಸಾಕಷ್ಟು ಸಂಗ್ರಹಣೆ ಇಲ್ಲ"</string>
diff --git a/packages/PackageInstaller/res/values-ko/strings.xml b/packages/PackageInstaller/res/values-ko/strings.xml
index d679bf11e78b..21da3e913df3 100644
--- a/packages/PackageInstaller/res/values-ko/strings.xml
+++ b/packages/PackageInstaller/res/values-ko/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"백그라운드에서 앱이 다운로드되기 시작합니다"</string>
<string name="restore" msgid="8460854736328970444">"복원"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"오프라인 상태"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"이 앱을 복원하려면 인터넷 연결을 확인하고 다시 시도해 보세요"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"문제가 발생했습니다"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"이 앱을 복원하려고 시도하던 중 문제가 발생했습니다"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"저장용량 부족"</string>
diff --git a/packages/PackageInstaller/res/values-ky/strings.xml b/packages/PackageInstaller/res/values-ky/strings.xml
index 04119616e3ff..e788bf7e2f8f 100644
--- a/packages/PackageInstaller/res/values-ky/strings.xml
+++ b/packages/PackageInstaller/res/values-ky/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Бул колдонмо фондо жүктөлүп алына баштайт"</string>
<string name="restore" msgid="8460854736328970444">"Калыбына келтирүү"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Интернет байланышыңыз жок"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Бул колдонмону калыбына келтирүү үчүн Интернет байланышыңызды текшерип, кайталап көрүңүз"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Бир жерден ката кетти"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Бул колдонмону калыбына келтирүүдө маселе келип чыкты"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Сактагычта орун жетишсиз"</string>
diff --git a/packages/PackageInstaller/res/values-lo/strings.xml b/packages/PackageInstaller/res/values-lo/strings.xml
index 2ac57be4c140..57c358d1f1e0 100644
--- a/packages/PackageInstaller/res/values-lo/strings.xml
+++ b/packages/PackageInstaller/res/values-lo/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"ແອັບນີ້ຈະເລີ່ມດາວໂຫຼດໃນພື້ນຫຼັງ"</string>
<string name="restore" msgid="8460854736328970444">"ກູ້ຄືນ"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"ທ່ານອອບລາຍຢູ່"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"ເພື່ອກູ້ຄືນແອັບນີ້, ກະລຸນາກວດສອບການເຊື່ອມຕໍ່ອິນເຕີເນັດຂອງທ່ານແລ້ວລອງໃໝ່"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"ມີບາງຢ່າງຜິດພາດເກີດຂຶ້ນ"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"ເກີດບັນຫາໃນລະຫວ່າງທີ່ພະຍາຍາມກູ້ຄືນແອັບນີ້"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"ບ່ອນຈັດເກັບຂໍ້ມູນບໍ່ພຽງພໍ"</string>
diff --git a/packages/PackageInstaller/res/values-lt/strings.xml b/packages/PackageInstaller/res/values-lt/strings.xml
index c2608cbf3579..86140b6dc0a3 100644
--- a/packages/PackageInstaller/res/values-lt/strings.xml
+++ b/packages/PackageInstaller/res/values-lt/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Šios programos atsisiuntimas bus pradėtas fone"</string>
<string name="restore" msgid="8460854736328970444">"Atkurti"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Esate neprisijungę"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Jei norite atkurti šią programą, patikrinkite interneto ryšį ir bandykite dar kartą"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Kažkas nepavyko"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Bandant atkurti šią programą iškilo problema"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Trūksta saugyklos vietos"</string>
diff --git a/packages/PackageInstaller/res/values-lv/strings.xml b/packages/PackageInstaller/res/values-lv/strings.xml
index 67ec0f3c2601..96709ef2a3e2 100644
--- a/packages/PackageInstaller/res/values-lv/strings.xml
+++ b/packages/PackageInstaller/res/values-lv/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Fonā tiks sākta šīs lietotnes lejupielāde."</string>
<string name="restore" msgid="8460854736328970444">"Atjaunot"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Jūs esat bezsaistē"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Lai atjaunotu šo lietotni, pārbaudiet interneta savienojumu un mēģiniet vēlreiz."</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Radās problēma"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Mēģinot atjaunot šo lietotni, radās problēma."</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Krātuvē nepietiek vietas"</string>
diff --git a/packages/PackageInstaller/res/values-mk/strings.xml b/packages/PackageInstaller/res/values-mk/strings.xml
index 0479537860f6..032ee0c1a052 100644
--- a/packages/PackageInstaller/res/values-mk/strings.xml
+++ b/packages/PackageInstaller/res/values-mk/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Апликацијава ќе почне да се презема во заднина"</string>
<string name="restore" msgid="8460854736328970444">"Враќање"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Офлајн сте"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"За да ја вратите апликацијава, проверете ја интернет-врската и обидете се повторно"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Нешто тргна наопаку"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Се јави проблем при обидот за враќање на апликацијава"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Нема доволно простор"</string>
diff --git a/packages/PackageInstaller/res/values-ml/strings.xml b/packages/PackageInstaller/res/values-ml/strings.xml
index c38ab575168c..f79662b76b95 100644
--- a/packages/PackageInstaller/res/values-ml/strings.xml
+++ b/packages/PackageInstaller/res/values-ml/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"പശ്ചാത്തലത്തിൽ ഈ ആപ്പ് ഡൗൺലോഡ് ചെയ്ത് തുടങ്ങും"</string>
<string name="restore" msgid="8460854736328970444">"പുനഃസ്ഥാപിക്കുക"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"നിങ്ങൾ ഓഫ്‌ലൈനാണ്"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"ഈ ആപ്പ് പുനഃസ്ഥാപിക്കാൻ, നിങ്ങളുടെ ഇന്റർനെറ്റ് കണക്ഷൻ പരിശോധിച്ച ശേഷം വീണ്ടും ശ്രമിക്കുക"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"എന്തോ കുഴപ്പമുണ്ടായി"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"ഈ ആപ്പ് പുനഃസ്ഥാപിക്കുന്നതിൽ ഒരു പ്രശ്നമുണ്ടായി"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"മതിയായ സ്‌റ്റോറേജ് ഇല്ല"</string>
diff --git a/packages/PackageInstaller/res/values-mn/strings.xml b/packages/PackageInstaller/res/values-mn/strings.xml
index 64eecc585c3c..ab2bb9f0472b 100644
--- a/packages/PackageInstaller/res/values-mn/strings.xml
+++ b/packages/PackageInstaller/res/values-mn/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Энэ аппыг дэвсгэрт татаж эхэлнэ"</string>
<string name="restore" msgid="8460854736328970444">"Сэргээх"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Та офлайн байна"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Энэ аппыг сэргээхийн тулд интернэт холболтоо шалгаад, дахин оролдоно уу"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Ямар нэг алдаа гарлаа"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Энэ аппыг сэргээхээр оролдоход асуудал гарлаа"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Хангалттай хадгалах сан байхгүй"</string>
diff --git a/packages/PackageInstaller/res/values-mr/strings.xml b/packages/PackageInstaller/res/values-mr/strings.xml
index b071b73fdb8d..c64d62fdcf5c 100644
--- a/packages/PackageInstaller/res/values-mr/strings.xml
+++ b/packages/PackageInstaller/res/values-mr/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"हे ॲप बॅकग्राउंडमध्ये डाउनलोड होण्यास सुरुवात होईल"</string>
<string name="restore" msgid="8460854736328970444">"रिस्टोअर करा"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"तुम्ही ऑफलाइन आहात"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"हे ॲप रिस्टोअर करण्यासाठी, तुमचे इंटरनेट कनेक्शन तपासा आणि पुन्हा प्रयत्न करा"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"काहीतरी चुकले"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"हे अ‍ॅप रिस्टोअर करताना समस्या आली होती"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"पुरेसे स्टोरेज नाही"</string>
diff --git a/packages/PackageInstaller/res/values-ms/strings.xml b/packages/PackageInstaller/res/values-ms/strings.xml
index f15f5432b8bc..e4d321ca5142 100644
--- a/packages/PackageInstaller/res/values-ms/strings.xml
+++ b/packages/PackageInstaller/res/values-ms/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Apl ini akan mula dimuat turun dalam latar"</string>
<string name="restore" msgid="8460854736328970444">"Pulihkan"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Anda berstatus luar talian"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Untuk memulihkan apl ini, semak sambungan Internet anda dan cuba lagi"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Ada yang tidak kena"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Terdapat masalah semasa memuatkan apl ini"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Storan tidak mencukupi"</string>
diff --git a/packages/PackageInstaller/res/values-my/strings.xml b/packages/PackageInstaller/res/values-my/strings.xml
index 063068face2d..98eae5abe9a1 100644
--- a/packages/PackageInstaller/res/values-my/strings.xml
+++ b/packages/PackageInstaller/res/values-my/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"ဤအက်ပ်ကို နောက်ခံတွင် စတင်ဒေါင်းလုဒ်လုပ်ပါမည်"</string>
<string name="restore" msgid="8460854736328970444">"ပြန်ယူရန်"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"သင်အော့ဖ်လိုင်း ဖြစ်နေသည်"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"ဤအက်ပ် ပြန်ယူရန် သင့်အင်တာနက်ချိတ်ဆက်မှုကို စစ်ဆေးပြီး ထပ်စမ်းကြည့်ပါ"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"တစ်ခုခုမှားသွားသည်"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"ဤအက်ပ်ကို ပြန်ယူရန် ကြိုးပမ်းရာတွင် ပြဿနာရှိသည်"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"သိုလှောင်ခန်း မလုံလောက်ပါ"</string>
diff --git a/packages/PackageInstaller/res/values-nb/strings.xml b/packages/PackageInstaller/res/values-nb/strings.xml
index 7c7aecfb6ba7..5ba7682b5f49 100644
--- a/packages/PackageInstaller/res/values-nb/strings.xml
+++ b/packages/PackageInstaller/res/values-nb/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Denne appen begynner å laste ned i bakgrunnen"</string>
<string name="restore" msgid="8460854736328970444">"Gjenopprett"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Du er uten nett"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"For å gjenopprette denne appen, sjekk internettilkoblingen din og prøv igjen"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Noe gikk galt"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Det oppsto et problem med å gjenopprette denne appen"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Ikke nok lagringsplass"</string>
diff --git a/packages/PackageInstaller/res/values-ne/strings.xml b/packages/PackageInstaller/res/values-ne/strings.xml
index f7417736226c..0bc4be3e53d6 100644
--- a/packages/PackageInstaller/res/values-ne/strings.xml
+++ b/packages/PackageInstaller/res/values-ne/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"यो एप ब्याकग्राउन्डमा डाउनलोड हुन थाल्ने छ"</string>
<string name="restore" msgid="8460854736328970444">"रिस्टोर गर्नुहोस्"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"तपाईंको डिभाइस अफलाइन छ"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"यो एप रिस्टोर गर्न आफ्नो इन्टरनेट कनेक्सन जाँच्नुहोस् र फेरि प्रयास गर्नुहोस्।"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"कुनै समस्या आयो"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"यो एप रिस्टोर गर्ने क्रममा कुनै समस्या आयो"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"पर्याप्त खाली ठाउँ छैन"</string>
diff --git a/packages/PackageInstaller/res/values-night/themes.xml b/packages/PackageInstaller/res/values-night/themes.xml
index 18320f7626e9..a5b82b39344c 100644
--- a/packages/PackageInstaller/res/values-night/themes.xml
+++ b/packages/PackageInstaller/res/values-night/themes.xml
@@ -20,6 +20,9 @@
<style name="Theme.AlertDialogActivity"
parent="@android:style/Theme.DeviceDefault.Dialog.Alert">
<item name="alertDialogStyle">@style/AlertDialog</item>
+ <item name="android:windowActionBar">false</item>
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowAnimationStyle">@null</item>
</style>
</resources>
diff --git a/packages/PackageInstaller/res/values-nl/strings.xml b/packages/PackageInstaller/res/values-nl/strings.xml
index d7c3480b9767..eaae47d00fa3 100644
--- a/packages/PackageInstaller/res/values-nl/strings.xml
+++ b/packages/PackageInstaller/res/values-nl/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Deze app wordt gedownload op de achtergrond"</string>
<string name="restore" msgid="8460854736328970444">"Herstellen"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Je bent offline"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Check de internetverbinding en probeer het opnieuw als je deze app wilt herstellen"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Er is iets misgegaan"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Er is iets misgegaan bij het herstellen van deze app"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Onvoldoende opslagruimte"</string>
diff --git a/packages/PackageInstaller/res/values-or/strings.xml b/packages/PackageInstaller/res/values-or/strings.xml
index 4712dba0709c..8bf3225e530b 100644
--- a/packages/PackageInstaller/res/values-or/strings.xml
+++ b/packages/PackageInstaller/res/values-or/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"ଏହି ଆପ ପୃଷ୍ଠପଟରେ ଡାଉନଲୋଡ ହେବା ଆରମ୍ଭ କରିବ"</string>
<string name="restore" msgid="8460854736328970444">"ରିଷ୍ଟୋର କରନ୍ତୁ"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"ଆପଣ ଅଫଲାଇନ ଅଛନ୍ତି"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"ଏହି ଆପକୁ ରିଷ୍ଟୋର କରିବା ପାଇଁ ଆପଣଙ୍କ ଇଣ୍ଟରନେଟ କନେକ୍ସନ ଯାଞ୍ଚ କରି ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"କିଛି ତ୍ରୁଟି ହୋଇଛି"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"ଏହି ଆପ ରିଷ୍ଟୋର କରିବାକୁ ଚେଷ୍ଟା କରିବା ସମୟରେ ଏକ ସମସ୍ୟା ହୋଇଛି"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"ଯଥେଷ୍ଟ ଷ୍ଟୋରେଜ ନାହିଁ"</string>
diff --git a/packages/PackageInstaller/res/values-pa/strings.xml b/packages/PackageInstaller/res/values-pa/strings.xml
index d0640f28070d..34a0945e4aa7 100644
--- a/packages/PackageInstaller/res/values-pa/strings.xml
+++ b/packages/PackageInstaller/res/values-pa/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"ਇਹ ਐਪ ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਡਾਊਨਲੋਡ ਹੋਣੀ ਸ਼ੁਰੂ ਹੋ ਜਾਵੇਗੀ"</string>
<string name="restore" msgid="8460854736328970444">"ਮੁੜ-ਬਹਾਲ ਕਰੋ"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"ਤੁਸੀਂ ਆਫ਼ਲਾਈਨ ਹੋ"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"ਇਸ ਐਪ ਨੂੰ ਮੁੜ-ਬਹਾਲ ਕਰਨ ਲਈ, ਆਪਣੇ ਇੰਟਰਨੈੱਟ ਕਨੈਕਸ਼ਨ ਦੀ ਜਾਂਚ ਕਰ ਕੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"ਕੋਈ ਗੜਬੜ ਹੋ ਗਈ"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"ਇਸ ਐਪ ਨੂੰ ਮੁੜ-ਬਹਾਲ ਕਰਨ ਵੇਲੇ ਕੋਈ ਸਮੱਸਿਆ ਆਈ"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"ਲੋੜੀਂਦੀ ਸਟੋਰੇਜ ਨਹੀਂ ਹੈ"</string>
diff --git a/packages/PackageInstaller/res/values-pl/strings.xml b/packages/PackageInstaller/res/values-pl/strings.xml
index 551d4553ba05..22a78e3b46c6 100644
--- a/packages/PackageInstaller/res/values-pl/strings.xml
+++ b/packages/PackageInstaller/res/values-pl/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Ta aplikacja zacznie pobieranie w tle"</string>
<string name="restore" msgid="8460854736328970444">"Przywróć"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Jesteś offline"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Aby przywrócić tę aplikację, sprawdź połączenie internetowe i spróbuj ponownie"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Coś poszło nie tak"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Podczas przywracania tej aplikacji wystąpił błąd"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Za mało miejsca na dane"</string>
diff --git a/packages/PackageInstaller/res/values-pt-rBR/strings.xml b/packages/PackageInstaller/res/values-pt-rBR/strings.xml
index 31e23dbd9d49..332517a775f4 100644
--- a/packages/PackageInstaller/res/values-pt-rBR/strings.xml
+++ b/packages/PackageInstaller/res/values-pt-rBR/strings.xml
@@ -40,7 +40,7 @@
<string name="install_failed_msg" product="tv" msgid="1920009940048975221">"Não foi possível instalar o app <xliff:g id="APP_NAME">%1$s</xliff:g> na sua TV."</string>
<string name="install_failed_msg" product="default" msgid="6484461562647915707">"Não foi possível instalar o app <xliff:g id="APP_NAME">%1$s</xliff:g> no seu smartphone."</string>
<string name="launch" msgid="3952550563999890101">"Abrir"</string>
- <string name="unknown_apps_admin_dlg_text" msgid="4456572224020176095">"Seu administrador não permite a instalação de apps transferidos por download de fontes desconhecidas"</string>
+ <string name="unknown_apps_admin_dlg_text" msgid="4456572224020176095">"Seu administrador não permite a instalação de apps baixados de fontes desconhecidas"</string>
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Apps desconhecidos não podem ser instalados por este usuário"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Este usuário não tem permissão para instalar apps"</string>
<string name="ok" msgid="7871959885003339302">"OK"</string>
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"O download desse app será feito em segundo plano"</string>
<string name="restore" msgid="8460854736328970444">"Restaurar"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Você está off-line"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Para restaurar o app, confira sua conexão de Internet e tente de novo"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Algo deu errado"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Ocorreu um problema ao tentar restaurar este app"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Armazenamento insuficiente"</string>
diff --git a/packages/PackageInstaller/res/values-pt-rPT/strings.xml b/packages/PackageInstaller/res/values-pt-rPT/strings.xml
index 692312716f70..c39956d63286 100644
--- a/packages/PackageInstaller/res/values-pt-rPT/strings.xml
+++ b/packages/PackageInstaller/res/values-pt-rPT/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Esta app vai começar a ser transferida em segundo plano"</string>
<string name="restore" msgid="8460854736328970444">"Restaurar"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Está offline"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Para restaurar esta app, verifique a sua ligação à Internet e tente novamente"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Algo correu mal"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Ocorreu um problema ao tentar repor esta app"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Sem armazenamento suficiente"</string>
diff --git a/packages/PackageInstaller/res/values-pt/strings.xml b/packages/PackageInstaller/res/values-pt/strings.xml
index 31e23dbd9d49..332517a775f4 100644
--- a/packages/PackageInstaller/res/values-pt/strings.xml
+++ b/packages/PackageInstaller/res/values-pt/strings.xml
@@ -40,7 +40,7 @@
<string name="install_failed_msg" product="tv" msgid="1920009940048975221">"Não foi possível instalar o app <xliff:g id="APP_NAME">%1$s</xliff:g> na sua TV."</string>
<string name="install_failed_msg" product="default" msgid="6484461562647915707">"Não foi possível instalar o app <xliff:g id="APP_NAME">%1$s</xliff:g> no seu smartphone."</string>
<string name="launch" msgid="3952550563999890101">"Abrir"</string>
- <string name="unknown_apps_admin_dlg_text" msgid="4456572224020176095">"Seu administrador não permite a instalação de apps transferidos por download de fontes desconhecidas"</string>
+ <string name="unknown_apps_admin_dlg_text" msgid="4456572224020176095">"Seu administrador não permite a instalação de apps baixados de fontes desconhecidas"</string>
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Apps desconhecidos não podem ser instalados por este usuário"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Este usuário não tem permissão para instalar apps"</string>
<string name="ok" msgid="7871959885003339302">"OK"</string>
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"O download desse app será feito em segundo plano"</string>
<string name="restore" msgid="8460854736328970444">"Restaurar"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Você está off-line"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Para restaurar o app, confira sua conexão de Internet e tente de novo"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Algo deu errado"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Ocorreu um problema ao tentar restaurar este app"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Armazenamento insuficiente"</string>
diff --git a/packages/PackageInstaller/res/values-ro/strings.xml b/packages/PackageInstaller/res/values-ro/strings.xml
index 598fbe90633b..a74d94d07992 100644
--- a/packages/PackageInstaller/res/values-ro/strings.xml
+++ b/packages/PackageInstaller/res/values-ro/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Descărcarea aplicației va începe în fundal"</string>
<string name="restore" msgid="8460854736328970444">"Restabilește"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Ești offline"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Ca să restabilești aplicația, verifică-ți conexiunea la internet și încearcă din nou"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"A apărut o eroare"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"A apărut o problemă la restabilirea aplicației"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Nu există suficient spațiu de stocare"</string>
diff --git a/packages/PackageInstaller/res/values-ru/strings.xml b/packages/PackageInstaller/res/values-ru/strings.xml
index f8ca10f28832..c8686a3828ad 100644
--- a/packages/PackageInstaller/res/values-ru/strings.xml
+++ b/packages/PackageInstaller/res/values-ru/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Приложение начнет скачиваться в фоновом режиме."</string>
<string name="restore" msgid="8460854736328970444">"Восстановить"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Нет доступа к Сети"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Чтобы восстановить это приложение, проверьте подключение к интернету и повторите попытку."</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Что-то пошло не так"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Не удалось восстановить приложение."</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Недостаточно места"</string>
diff --git a/packages/PackageInstaller/res/values-si/strings.xml b/packages/PackageInstaller/res/values-si/strings.xml
index 0ea077eb5d59..1abd881ffbe2 100644
--- a/packages/PackageInstaller/res/values-si/strings.xml
+++ b/packages/PackageInstaller/res/values-si/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"මෙම යෙදුම පසුබිමේ බාගැනීම ආරම්භ කරනු ඇත"</string>
<string name="restore" msgid="8460854736328970444">"ප්‍රතිසාධනය කරන්න"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"ඔබ නොබැඳි වේ"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"මෙම යෙදුම ප්‍රතිසාධනය කිරීම සඳහා, ඔබේ අන්තර්ජාල සම්බන්ධතාවය පරීක්ෂා කර නැවත උත්සාහ කරන්න"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"යමක් වැරදී ඇත"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"මෙම යෙදුම ප්‍රතිසාධනය කිරීමට උත්සාහ කිරීමේ ගැටලුවක් ඇති විය"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"ප්‍රමාණවත් ආචයනයක් නොමැත"</string>
diff --git a/packages/PackageInstaller/res/values-sk/strings.xml b/packages/PackageInstaller/res/values-sk/strings.xml
index ade533cb79a8..ce186f247b20 100644
--- a/packages/PackageInstaller/res/values-sk/strings.xml
+++ b/packages/PackageInstaller/res/values-sk/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Táto aplikácia sa začne sťahovať na pozadí"</string>
<string name="restore" msgid="8460854736328970444">"Obnoviť"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Ste offline"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Ak chcete túto aplikáciu obnoviť, skontrolujte internetové pripojenie a skúste to znova"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Niečo sa pokazilo"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Pri pokuse o obnovenie tejto aplikácie sa vyskytol problém"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Úložisko je nedostatočné"</string>
diff --git a/packages/PackageInstaller/res/values-sl/strings.xml b/packages/PackageInstaller/res/values-sl/strings.xml
index e949b69cfe0c..05b826fc2506 100644
--- a/packages/PackageInstaller/res/values-sl/strings.xml
+++ b/packages/PackageInstaller/res/values-sl/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Aplikacija bo začela prenos v ozadju"</string>
<string name="restore" msgid="8460854736328970444">"Obnovi"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Nimate vzpostavljene povezave"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Če želite obnoviti to aplikacijo, preverite internetno povezavo in poskusite znova"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Prišlo je do težave"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Pri obnavljanju te aplikacije je prišlo do težave."</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Ni dovolj prostora za shranjevanje"</string>
diff --git a/packages/PackageInstaller/res/values-sq/strings.xml b/packages/PackageInstaller/res/values-sq/strings.xml
index bbcccd8bf978..b27207d4b36b 100644
--- a/packages/PackageInstaller/res/values-sq/strings.xml
+++ b/packages/PackageInstaller/res/values-sq/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Ky aplikacion do të fillojë të shkarkohet në sfond"</string>
<string name="restore" msgid="8460854736328970444">"Restauro"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Je offline"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Për ta restauruar këtë aplikacion, kontrollo lidhjen e internetit dhe provo përsëri"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Ndodhi një gabim"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Pati një problem gjatë përpjekjes për të restauruar këtë aplikacion"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Hapësira ruajtëse e pamjaftueshme"</string>
diff --git a/packages/PackageInstaller/res/values-sr/strings.xml b/packages/PackageInstaller/res/values-sr/strings.xml
index 0081516b05af..fcf575f9b236 100644
--- a/packages/PackageInstaller/res/values-sr/strings.xml
+++ b/packages/PackageInstaller/res/values-sr/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Апликација ће започети преузимање у позадини."</string>
<string name="restore" msgid="8460854736328970444">"Врати"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Офлајн сте"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Да бисте вратили ову апликацију, проверите интернет везу и пробајте поново."</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Дошло је до грешке"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Дошло је до проблема при враћању ове апликације"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Нема довољно меморијског простора"</string>
diff --git a/packages/PackageInstaller/res/values-sv/strings.xml b/packages/PackageInstaller/res/values-sv/strings.xml
index 76e936122879..8037dfba3521 100644
--- a/packages/PackageInstaller/res/values-sv/strings.xml
+++ b/packages/PackageInstaller/res/values-sv/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Appen börjar ladda ned i bakgrunden"</string>
<string name="restore" msgid="8460854736328970444">"Återställ"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Du är offline"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"För att återställa den här appen kontrollerar du internetanslutningen och försöker igen"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Något gick fel"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Det gick inte att återställa appen"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Inte tillräckligt med lagringsutrymme"</string>
diff --git a/packages/PackageInstaller/res/values-sw/strings.xml b/packages/PackageInstaller/res/values-sw/strings.xml
index 4bcdaaab78d5..7e1e3584f664 100644
--- a/packages/PackageInstaller/res/values-sw/strings.xml
+++ b/packages/PackageInstaller/res/values-sw/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Itaanza kupakua programu hii chinichini"</string>
<string name="restore" msgid="8460854736328970444">"Rejesha"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Uko nje ya mtandao"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Ili urejeshe programu hii, angalia muunganisho wako wa intaneti kisha ujaribu tena"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Hitilafu fulani imetokea"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Hitilafu fulani imetokea wakati wa kujaribu kurejesha programu hii"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Nafasi ya hifadhi haitoshi"</string>
diff --git a/packages/PackageInstaller/res/values-ta/strings.xml b/packages/PackageInstaller/res/values-ta/strings.xml
index 325455ee3cb6..c7a0d88fe059 100644
--- a/packages/PackageInstaller/res/values-ta/strings.xml
+++ b/packages/PackageInstaller/res/values-ta/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"இந்த ஆப்ஸ் பின்னணியில் பதிவிறக்கத் தொடங்கும்"</string>
<string name="restore" msgid="8460854736328970444">"மீட்டெடு"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"ஆஃப்லைனில் உள்ளீர்கள்"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"இந்த ஆப்ஸை மீட்டெடுக்க, உங்கள் இணைய இணைப்பைச் சரிபார்த்துவிட்டு மீண்டும் முயலவும்"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"ஏதோ தவறாகிவிட்டது"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"இந்த ஆப்ஸை மீட்டெடுக்க முயலும்போது சிக்கல் ஏற்பட்டது"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"போதுமான சேமிப்பகம் இல்லை"</string>
diff --git a/packages/PackageInstaller/res/values-te/strings.xml b/packages/PackageInstaller/res/values-te/strings.xml
index ae517919f14f..d91a9c7c2789 100644
--- a/packages/PackageInstaller/res/values-te/strings.xml
+++ b/packages/PackageInstaller/res/values-te/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"ఈ యాప్ బ్యాక్‌గ్రౌండ్‌లో డౌన్‌లోడ్ అవ్వడం ప్రారంభమవుతుంది"</string>
<string name="restore" msgid="8460854736328970444">"రీస్టోర్ చేయండి"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"మీరు ఆఫ్‌లైన్‌లో ఉన్నారు"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"ఈ యాప్‌ను రీస్టోర్ చేయడానికి, మీ ఇంటర్నెట్ కనెక్షన్‌ను చెక్ చేసి, మళ్లీ ట్రై చేయండి"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"ఏదో పొరపాటు జరిగింది"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"ఈ యాప్‌ను రీస్టోర్ చేయడానికి ట్రై చేస్తున్నపుడు సమస్య ఏర్పడింది"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"తగినంత స్టోరేజ్ స్పేస్ లేదు"</string>
diff --git a/packages/PackageInstaller/res/values-th/strings.xml b/packages/PackageInstaller/res/values-th/strings.xml
index 95fb2a3f19dc..7f3708432a19 100644
--- a/packages/PackageInstaller/res/values-th/strings.xml
+++ b/packages/PackageInstaller/res/values-th/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"แอปนี้จะเริ่มดาวน์โหลดในเบื้องหลัง"</string>
<string name="restore" msgid="8460854736328970444">"กู้คืน"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"คุณออฟไลน์อยู่"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"หากต้องการกู้คืนแอปนี้ ให้ตรวจสอบการเชื่อมต่ออินเทอร์เน็ตแล้วลองอีกครั้ง"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"เกิดข้อผิดพลาด"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"เกิดปัญหาขณะพยายามคืนค่าแอปนี้"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"พื้นที่เก็บข้อมูลไม่เพียงพอ"</string>
diff --git a/packages/PackageInstaller/res/values-tl/strings.xml b/packages/PackageInstaller/res/values-tl/strings.xml
index 0377b1bec454..9c2f15ca751e 100644
--- a/packages/PackageInstaller/res/values-tl/strings.xml
+++ b/packages/PackageInstaller/res/values-tl/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Magsisimulang mag-download ang app na ito sa background"</string>
<string name="restore" msgid="8460854736328970444">"I-restore"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Offline ka"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Para i-restore ang app na ito, suriin ang iyong koneksyon sa internet at subukan ulit"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Nagkaproblema"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Nagkaproblema sa pagsubok na i-restore ang app na ito"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Hindi sapat ang storage"</string>
diff --git a/packages/PackageInstaller/res/values-tr/strings.xml b/packages/PackageInstaller/res/values-tr/strings.xml
index d809748d0f9d..0af23520f870 100644
--- a/packages/PackageInstaller/res/values-tr/strings.xml
+++ b/packages/PackageInstaller/res/values-tr/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Bu uygulama arka planda indirilmeye başlanacak"</string>
<string name="restore" msgid="8460854736328970444">"Geri yükle"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"İnternete bağlı değilsiniz"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Bu uygulamayı geri yüklemek için internet bağlantınızı kontrol edip tekrar deneyin"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Bir hata oluştu"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Bu uygulamayı geri yüklemeye çalışırken bir sorun oluştu"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Yeterli depolama alanı yok"</string>
diff --git a/packages/PackageInstaller/res/values-uk/strings.xml b/packages/PackageInstaller/res/values-uk/strings.xml
index 1dbcb20d942b..15890f5de247 100644
--- a/packages/PackageInstaller/res/values-uk/strings.xml
+++ b/packages/PackageInstaller/res/values-uk/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Цей додаток почне завантажуватись у фоновому режимі"</string>
<string name="restore" msgid="8460854736328970444">"Відновити"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Пристрій не в мережі"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Щоб відновити цей додаток, перевірте інтернет-з\'єднання й повторіть спробу"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Помилка"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Під час спроби відновити цей додаток сталася помилка"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Недостатньо пам’яті"</string>
diff --git a/packages/PackageInstaller/res/values-ur/strings.xml b/packages/PackageInstaller/res/values-ur/strings.xml
index 4b0aa1f750f3..444fdd701e17 100644
--- a/packages/PackageInstaller/res/values-ur/strings.xml
+++ b/packages/PackageInstaller/res/values-ur/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"یہ ایپ پس منظر میں ڈاؤن لوڈ ہونا شروع ہو جائے گی"</string>
<string name="restore" msgid="8460854736328970444">"بحال کریں"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"آپ آف لائن ہیں"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"اس ایپ کو بحال کرنے کے لیے، اپنا انٹرنیٹ کنکشن چیک کریں اور دوبارہ کوشش کریں"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"کچھ غلط ہو گیا"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"اس ایپ کو بحال کرنے کی کوشش میں ایک مسئلہ تھا"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"کافی اسٹوریج نہیں ہے"</string>
diff --git a/packages/PackageInstaller/res/values-uz/strings.xml b/packages/PackageInstaller/res/values-uz/strings.xml
index 18f1d74d644f..24f8f55e53df 100644
--- a/packages/PackageInstaller/res/values-uz/strings.xml
+++ b/packages/PackageInstaller/res/values-uz/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Bu ilova orqa fonda yuklab olinishi boshlanadi"</string>
<string name="restore" msgid="8460854736328970444">"Tiklash"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Siz internetga ulanmagansiz"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Bu ilovani tiklash uchun internetga aloqasini tekshiring va qayta urining"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Nimadir xato ketdi"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Bu ilovani tiklashda muammo chiqdi"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Xotirada yetarli boʻsh joy yoʻq"</string>
diff --git a/packages/PackageInstaller/res/values-vi/strings.xml b/packages/PackageInstaller/res/values-vi/strings.xml
index ef71ea21c594..09d6c71a71dc 100644
--- a/packages/PackageInstaller/res/values-vi/strings.xml
+++ b/packages/PackageInstaller/res/values-vi/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Ứng dụng này sẽ bắt đầu tải xuống ở chế độ nền"</string>
<string name="restore" msgid="8460854736328970444">"Khôi phục"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Không có kết nối mạng"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Để khôi phục ứng dụng này, hãy kiểm tra kết nối Internet rồi thử lại"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Đã xảy ra lỗi"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Đã xảy ra vấn đề khi tìm cách khôi phục ứng dụng này"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Không đủ dung lượng lưu trữ"</string>
diff --git a/packages/PackageInstaller/res/values-zh-rCN/strings.xml b/packages/PackageInstaller/res/values-zh-rCN/strings.xml
index 98c717253405..f4bad7e7ab0e 100644
--- a/packages/PackageInstaller/res/values-zh-rCN/strings.xml
+++ b/packages/PackageInstaller/res/values-zh-rCN/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"系统将开始在后台下载此应用"</string>
<string name="restore" msgid="8460854736328970444">"恢复"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"您没有联网"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"若要恢复此应用,请检查互联网连接,然后重试"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"出了点问题"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"尝试恢复此应用时出现问题"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"存储空间不足"</string>
diff --git a/packages/PackageInstaller/res/values-zh-rHK/strings.xml b/packages/PackageInstaller/res/values-zh-rHK/strings.xml
index 22359df8c9b9..3c07151f318f 100644
--- a/packages/PackageInstaller/res/values-zh-rHK/strings.xml
+++ b/packages/PackageInstaller/res/values-zh-rHK/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"此應用程式將開始在背景下載"</string>
<string name="restore" msgid="8460854736328970444">"還原"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"你已離線"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"如要還原此應用程式,請檢查你的互聯網連線,然後再試一次"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"發生問題"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"嘗試還原此應用程式時發生問題"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"儲存空間不足"</string>
diff --git a/packages/PackageInstaller/res/values-zh-rTW/strings.xml b/packages/PackageInstaller/res/values-zh-rTW/strings.xml
index ed4fd39032ee..11046757c1e5 100644
--- a/packages/PackageInstaller/res/values-zh-rTW/strings.xml
+++ b/packages/PackageInstaller/res/values-zh-rTW/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"系統將開始在背景下載這個應用程式"</string>
<string name="restore" msgid="8460854736328970444">"還原"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"裝置目前離線"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"如要還原這個應用程式,請檢查網際網路連線,然後再試一次"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"發生錯誤"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"嘗試還原這個應用程式時發生問題"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"儲存空間不足"</string>
diff --git a/packages/PackageInstaller/res/values-zu/strings.xml b/packages/PackageInstaller/res/values-zu/strings.xml
index eff6e600a336..679a5af0c7e0 100644
--- a/packages/PackageInstaller/res/values-zu/strings.xml
+++ b/packages/PackageInstaller/res/values-zu/strings.xml
@@ -113,8 +113,7 @@
<string name="unarchive_body_text" msgid="8244155079861708964">"Le app izoqala ukudawuniloda ingemuva"</string>
<string name="restore" msgid="8460854736328970444">"Buyisela"</string>
<string name="unarchive_error_offline_title" msgid="4021785324565678605">"Awuxhunyiwe ku-inthanethi"</string>
- <!-- no translation found for unarchive_error_offline_body (2256042209364094099) -->
- <skip />
+ <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Ukuze ubuyisele le app, hlola ukuxhumeka kwakho kwe-inthanethi uphinde uzame futhi"</string>
<string name="unarchive_error_generic_title" msgid="7123457671482449992">"Kukhona okungahambanga kahle"</string>
<string name="unarchive_error_generic_body" msgid="4486803312463813079">"Kube nenkinga ekuzameni ukubuyisa le app"</string>
<string name="unarchive_error_storage_title" msgid="5080723357273852630">"Indawo yokubeka ayanele"</string>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java
index cf2f85ed5356..634e067a12d4 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java
@@ -20,6 +20,7 @@ import static android.content.res.AssetFileDescriptor.UNKNOWN_LENGTH;
import static com.android.packageinstaller.PackageInstallerActivity.EXTRA_STAGED_SESSION_ID;
+import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
@@ -27,10 +28,10 @@ import android.app.DialogFragment;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.pm.Flags;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.res.AssetFileDescriptor;
-import android.Manifest;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
@@ -200,7 +201,7 @@ public class InstallStaging extends Activity {
params.setPermissionState(Manifest.permission.USE_FULL_SCREEN_INTENT,
PackageInstaller.SessionParams.PERMISSION_STATE_DENIED);
- if (pfd != null) {
+ if (pfd != null && Flags.readInstallInfo()) {
try {
final PackageInstaller.InstallInfo result = installer.readInstallInfo(pfd,
debugPathName, 0);
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index 7240fb97611a..cf6aab641fc9 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -31,6 +31,7 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
+import android.content.pm.Flags;
import android.content.pm.InstallSourceInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
@@ -397,9 +398,12 @@ public class PackageInstallerActivity extends Activity {
final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID,
-1 /* defaultValue */);
final SessionInfo info = mInstaller.getSessionInfo(sessionId);
- String resolvedPath = info != null ? info.getResolvedBaseApkPath() : null;
+ String resolvedPath = null;
+ if (info != null && Flags.getResolvedApkPath()) {
+ resolvedPath = info.getResolvedBaseApkPath();
+ }
if (info == null || !info.isSealed() || resolvedPath == null) {
- Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring");
+ Log.w(TAG, "Session " + sessionId + " in funky state; ignoring");
finish();
return;
}
@@ -414,7 +418,7 @@ public class PackageInstallerActivity extends Activity {
-1 /* defaultValue */);
final SessionInfo info = mInstaller.getSessionInfo(sessionId);
if (info == null || !info.isPreApprovalRequested()) {
- Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring");
+ Log.w(TAG, "Session " + sessionId + " in funky state; ignoring");
finish();
return;
}
@@ -835,7 +839,9 @@ public class PackageInstallerActivity extends Activity {
// work for the multiple user case, i.e. the caller task user and started
// Activity user are not the same. To avoid having multiple PIAs in the task,
// finish the current PackageInstallerActivity
- finish();
+ // Because finish() is overridden to set the installation result, we must use
+ // the original finish() method, or the confirmation dialog fails to appear.
+ PackageInstallerActivity.super.finish();
}
}, 500);
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt
index aeabbd53d177..22caabdabbf0 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt
@@ -25,6 +25,7 @@ import android.content.ContentResolver
import android.content.Context
import android.content.Intent
import android.content.pm.ApplicationInfo
+import android.content.pm.Flags
import android.content.pm.PackageInfo
import android.content.pm.PackageInstaller
import android.content.pm.PackageInstaller.SessionInfo
@@ -362,7 +363,7 @@ class InstallRepository(private val context: Context) {
params.setPermissionState(
Manifest.permission.USE_FULL_SCREEN_INTENT, SessionParams.PERMISSION_STATE_DENIED
)
- if (pfd != null) {
+ if (pfd != null && Flags.readInstallInfo()) {
try {
val installInfo = packageInstaller.readInstallInfo(pfd, debugPathName, 0)
params.setAppPackageName(installInfo.packageName)
@@ -425,7 +426,8 @@ class InstallRepository(private val context: Context) {
if (PackageInstaller.ACTION_CONFIRM_INSTALL == intent.action) {
val info = packageInstaller.getSessionInfo(sessionId)
- val resolvedPath = info?.resolvedBaseApkPath
+ val resolvedPath =
+ if (Flags.getResolvedApkPath()) info?.resolvedBaseApkPath else null
if (info == null || !info.isSealed || resolvedPath == null) {
Log.w(LOG_TAG, "Session $sessionId in funky state; ignoring")
return InstallAborted(ABORT_REASON_INTERNAL_ERROR)
diff --git a/packages/PrintRecommendationService/res/values/strings.xml b/packages/PrintRecommendationService/res/values/strings.xml
index 2bab1b65529b..b6c45b7a23c8 100644
--- a/packages/PrintRecommendationService/res/values/strings.xml
+++ b/packages/PrintRecommendationService/res/values/strings.xml
@@ -18,7 +18,6 @@
-->
<resources>
- <string name="plugin_vendor_google_cloud_print">Cloud Print</string>
<string name="plugin_vendor_hp">HP</string>
<string name="plugin_vendor_lexmark">Lexmark</string>
<string name="plugin_vendor_brother">Brother</string>
diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java
index 5a756fe50209..4ec88830386b 100644
--- a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java
+++ b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/RecommendationServiceImpl.java
@@ -23,7 +23,6 @@ import android.printservice.recommendation.RecommendationInfo;
import android.printservice.recommendation.RecommendationService;
import android.util.Log;
-import com.android.printservice.recommendation.plugin.google.CloudPrintPlugin;
import com.android.printservice.recommendation.plugin.hp.HPRecommendationPlugin;
import com.android.printservice.recommendation.plugin.mdnsFilter.MDNSFilterPlugin;
import com.android.printservice.recommendation.plugin.mdnsFilter.VendorConfig;
@@ -77,14 +76,6 @@ public class RecommendationServiceImpl extends RecommendationService
}
try {
- mPlugins.add(new RemotePrintServicePlugin(new CloudPrintPlugin(this), this,
- true));
- } catch (Exception e) {
- Log.e(LOG_TAG, "Could not initiate "
- + getString(R.string.plugin_vendor_google_cloud_print) + " plugin", e);
- }
-
- try {
mPlugins.add(new RemotePrintServicePlugin(new HPRecommendationPlugin(this), this,
false));
} catch (Exception e) {
diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/google/CloudPrintPlugin.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/google/CloudPrintPlugin.java
deleted file mode 100644
index 3029d10d4cf3..000000000000
--- a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/google/CloudPrintPlugin.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.printservice.recommendation.plugin.google;
-
-import static com.android.printservice.recommendation.util.MDNSUtils.ATTRIBUTE_TY;
-
-import android.content.Context;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.StringRes;
-
-import com.android.printservice.recommendation.PrintServicePlugin;
-import com.android.printservice.recommendation.R;
-import com.android.printservice.recommendation.util.MDNSFilteredDiscovery;
-
-import java.net.Inet4Address;
-import java.net.InetAddress;
-import java.nio.charset.StandardCharsets;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Plugin detecting <a href="https://developers.google.com/cloud-print/docs/privet">Google Cloud
- * Print</a> printers.
- */
-public class CloudPrintPlugin implements PrintServicePlugin {
- private static final String LOG_TAG = CloudPrintPlugin.class.getSimpleName();
- private static final boolean DEBUG = false;
-
- private static final String ATTRIBUTE_TXTVERS = "txtvers";
- private static final String ATTRIBUTE_URL = "url";
- private static final String ATTRIBUTE_TYPE = "type";
- private static final String ATTRIBUTE_ID = "id";
- private static final String ATTRIBUTE_CS = "cs";
-
- private static final String TYPE = "printer";
-
- private static final String PRIVET_SERVICE = "_privet._tcp";
-
- /** The required mDNS service types */
- private static final Set<String> PRINTER_SERVICE_TYPE = Set.of(
- PRIVET_SERVICE); // Not checking _printer_._sub
-
- /** All possible connection states */
- private static final Set<String> POSSIBLE_CONNECTION_STATES = Set.of(
- "online",
- "offline",
- "connecting",
- "not-configured");
-
- private static final byte SUPPORTED_TXTVERS = '1';
-
- /** The mDNS filtered discovery */
- private final MDNSFilteredDiscovery mMDNSFilteredDiscovery;
-
- /**
- * Create a plugin detecting Google Cloud Print printers.
- *
- * @param context The context the plugin runs in
- */
- public CloudPrintPlugin(@NonNull Context context) {
- mMDNSFilteredDiscovery = new MDNSFilteredDiscovery(context, PRINTER_SERVICE_TYPE,
- nsdServiceInfo -> {
- // The attributes are case insensitive. For faster searching create a clone of
- // the map with the attribute-keys all in lower case.
- ArrayMap<String, byte[]> caseInsensitiveAttributes =
- new ArrayMap<>(nsdServiceInfo.getAttributes().size());
- for (Map.Entry<String, byte[]> entry : nsdServiceInfo.getAttributes()
- .entrySet()) {
- caseInsensitiveAttributes.put(entry.getKey().toLowerCase(),
- entry.getValue());
- }
-
- if (DEBUG) {
- Log.i(LOG_TAG, nsdServiceInfo.getServiceName() + ":");
- Log.i(LOG_TAG, "type: " + nsdServiceInfo.getServiceType());
- Log.i(LOG_TAG, "host: " + nsdServiceInfo.getHost());
- for (Map.Entry<String, byte[]> entry : caseInsensitiveAttributes.entrySet()) {
- if (entry.getValue() == null) {
- Log.i(LOG_TAG, entry.getKey() + "= null");
- } else {
- Log.i(LOG_TAG, entry.getKey() + "=" + new String(entry.getValue(),
- StandardCharsets.UTF_8));
- }
- }
- }
-
- byte[] txtvers = caseInsensitiveAttributes.get(ATTRIBUTE_TXTVERS);
- if (txtvers == null || txtvers.length != 1 || txtvers[0] != SUPPORTED_TXTVERS) {
- // The spec requires this to be the first attribute, but at this time we
- // lost the order of the attributes
- return false;
- }
-
- if (caseInsensitiveAttributes.get(ATTRIBUTE_TY) == null) {
- return false;
- }
-
- byte[] url = caseInsensitiveAttributes.get(ATTRIBUTE_URL);
- if (url == null || url.length == 0) {
- return false;
- }
-
- byte[] type = caseInsensitiveAttributes.get(ATTRIBUTE_TYPE);
- if (type == null || !TYPE.equals(
- new String(type, StandardCharsets.UTF_8).toLowerCase())) {
- return false;
- }
-
- if (caseInsensitiveAttributes.get(ATTRIBUTE_ID) == null) {
- return false;
- }
-
- byte[] cs = caseInsensitiveAttributes.get(ATTRIBUTE_CS);
- if (cs == null || !POSSIBLE_CONNECTION_STATES.contains(
- new String(cs, StandardCharsets.UTF_8).toLowerCase())) {
- return false;
- }
-
- InetAddress address = nsdServiceInfo.getHost();
- if (!(address instanceof Inet4Address)) {
- // Not checking for link local address
- return false;
- }
-
- return true;
- });
- }
-
- @Override
- @NonNull public CharSequence getPackageName() {
- return "com.google.android.apps.cloudprint";
- }
-
- @Override
- public void start(@NonNull PrinterDiscoveryCallback callback) throws Exception {
- mMDNSFilteredDiscovery.start(callback);
- }
-
- @Override
- @StringRes public int getName() {
- return R.string.plugin_vendor_google_cloud_print;
- }
-
- @Override
- public void stop() throws Exception {
- mMDNSFilteredDiscovery.stop();
- }
-}
diff --git a/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java
index 5b39f4ee1541..18e8fc38ddb0 100644
--- a/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java
+++ b/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java
@@ -219,7 +219,6 @@ public class RestrictedLockUtils {
}
}
-
/**
* Shows restricted setting dialog.
*
diff --git a/packages/SettingsLib/Spa/OWNERS b/packages/SettingsLib/Spa/OWNERS
index 464328e816f2..67386d170a0d 100644
--- a/packages/SettingsLib/Spa/OWNERS
+++ b/packages/SettingsLib/Spa/OWNERS
@@ -1,4 +1,4 @@
-set noparent
+include platform/frameworks/base:/packages/SettingsLib/OWNERS
chaohuiw@google.com
hanxu@google.com
diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
index ec519ca61021..463e9be391b6 100644
--- a/packages/SettingsLib/Spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/build.gradle.kts
@@ -29,7 +29,7 @@ val androidTop: String = File(rootDir, "../../../../..").canonicalPath
allprojects {
extra["androidTop"] = androidTop
- extra["jetpackComposeVersion"] = "1.7.0-alpha01"
+ extra["jetpackComposeVersion"] = "1.7.0-alpha02"
}
subprojects {
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 460a6f7261ae..761bb7918afd 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
@@ -27,8 +27,8 @@ import com.android.settingslib.spa.gallery.chart.ChartPageProvider
import com.android.settingslib.spa.gallery.dialog.DialogMainPageProvider
import com.android.settingslib.spa.gallery.dialog.NavDialogProvider
import com.android.settingslib.spa.gallery.editor.EditorMainPageProvider
-import com.android.settingslib.spa.gallery.editor.SettingsExposedDropdownMenuBoxPageProvider
-import com.android.settingslib.spa.gallery.editor.SettingsExposedDropdownMenuCheckBoxProvider
+import com.android.settingslib.spa.gallery.editor.SettingsDropdownBoxPageProvider
+import com.android.settingslib.spa.gallery.editor.SettingsDropdownCheckBoxProvider
import com.android.settingslib.spa.gallery.home.HomePageProvider
import com.android.settingslib.spa.gallery.itemList.ItemListPageProvider
import com.android.settingslib.spa.gallery.itemList.ItemOperatePageProvider
@@ -99,8 +99,8 @@ class GallerySpaEnvironment(context: Context) : SpaEnvironment(context) {
OperateListPageProvider,
EditorMainPageProvider,
SettingsOutlinedTextFieldPageProvider,
- SettingsExposedDropdownMenuBoxPageProvider,
- SettingsExposedDropdownMenuCheckBoxProvider,
+ SettingsDropdownBoxPageProvider,
+ SettingsDropdownCheckBoxProvider,
SettingsTextFieldPasswordPageProvider,
SearchScaffoldPageProvider,
SuwScaffoldPageProvider,
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt
index d5cf1a35b4df..79c5ebbce5fa 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt
@@ -88,11 +88,13 @@ object CardPageProvider : SettingsPageProvider {
@Composable
private fun SettingsCardWithoutIcon() {
+ val sampleTitle = stringResource(R.string.sample_title)
+ var title by remember { mutableStateOf(sampleTitle) }
SettingsCard(
CardModel(
- title = stringResource(R.string.sample_title),
+ title = title,
text = stringResource(R.string.sample_text),
- )
+ ) { title = "Clicked" }
)
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/EditorMainPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/EditorMainPageProvider.kt
index 4875ea99d4d6..c511542f265a 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/EditorMainPageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/EditorMainPageProvider.kt
@@ -35,9 +35,9 @@ object EditorMainPageProvider : SettingsPageProvider {
return listOf(
SettingsOutlinedTextFieldPageProvider.buildInjectEntry().setLink(fromPage = owner)
.build(),
- SettingsExposedDropdownMenuBoxPageProvider.buildInjectEntry().setLink(fromPage = owner)
+ SettingsDropdownBoxPageProvider.buildInjectEntry().setLink(fromPage = owner)
.build(),
- SettingsExposedDropdownMenuCheckBoxProvider.buildInjectEntry().setLink(fromPage = owner)
+ SettingsDropdownCheckBoxProvider.buildInjectEntry().setLink(fromPage = owner)
.build(),
SettingsTextFieldPasswordPageProvider.buildInjectEntry().setLink(fromPage = owner)
.build(),
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuBoxPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsDropdownBoxPageProvider.kt
index 5ffbe8ba8a26..2ebb5f5eba27 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuBoxPageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsDropdownBoxPageProvider.kt
@@ -28,16 +28,15 @@ 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.editor.SettingsExposedDropdownMenuBox
+import com.android.settingslib.spa.widget.editor.SettingsDropdownBox
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
-private const val TITLE = "Sample SettingsExposedDropdownMenuBox"
+private const val TITLE = "Sample SettingsDropdownBox"
-object SettingsExposedDropdownMenuBoxPageProvider : SettingsPageProvider {
- override val name = "SettingsExposedDropdownMenuBox"
- private const val exposedDropdownMenuBoxLabel = "ExposedDropdownMenuBoxLabel"
+object SettingsDropdownBoxPageProvider : SettingsPageProvider {
+ override val name = "SettingsDropdownBox"
override fun getTitle(arguments: Bundle?): String {
return TITLE
@@ -45,18 +44,44 @@ object SettingsExposedDropdownMenuBoxPageProvider : SettingsPageProvider {
@Composable
override fun Page(arguments: Bundle?) {
- var selectedItem by remember { mutableIntStateOf(-1) }
- val options = listOf("item1", "item2", "item3")
RegularScaffold(title = TITLE) {
- SettingsExposedDropdownMenuBox(
- label = exposedDropdownMenuBoxLabel,
- options = options,
- selectedOptionIndex = selectedItem,
- enabled = true,
- onselectedOptionTextChange = { selectedItem = it })
+ Regular()
+ NotEnabled()
+ Empty()
}
}
+ @Composable
+ private fun Regular() {
+ var selectedItem by remember { mutableIntStateOf(-1) }
+ SettingsDropdownBox(
+ label = "SettingsDropdownBox",
+ options = listOf("item1", "item2", "item3"),
+ selectedOptionIndex = selectedItem,
+ ) { selectedItem = it }
+ }
+
+ @Composable
+ private fun NotEnabled() {
+ var selectedItem by remember { mutableIntStateOf(0) }
+ SettingsDropdownBox(
+ label = "Not enabled",
+ options = listOf("item1", "item2", "item3"),
+ enabled = false,
+ selectedOptionIndex = selectedItem,
+ ) { selectedItem = it }
+ }
+
+ @Composable
+ private fun Empty() {
+ var selectedItem by remember { mutableIntStateOf(-1) }
+ SettingsDropdownBox(
+ label = "Empty",
+ options = emptyList(),
+ selectedOptionIndex = selectedItem,
+ ) { selectedItem = it }
+ }
+
fun buildInjectEntry(): SettingsEntryBuilder {
return SettingsEntryBuilder.createInject(owner = createSettingsPage())
.setUiLayoutFn {
@@ -70,8 +95,8 @@ object SettingsExposedDropdownMenuBoxPageProvider : SettingsPageProvider {
@Preview(showBackground = true)
@Composable
-private fun SettingsExposedDropdownMenuBoxPagePreview() {
+private fun SettingsDropdownBoxPagePreview() {
SettingsTheme {
- SettingsExposedDropdownMenuBoxPageProvider.Page(null)
+ SettingsDropdownBoxPageProvider.Page(null)
}
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsDropdownCheckBoxProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsDropdownCheckBoxProvider.kt
new file mode 100644
index 000000000000..33ab75d6189d
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsDropdownCheckBoxProvider.kt
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2024 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.editor
+
+import android.os.Bundle
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.tooling.preview.Preview
+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.editor.SettingsDropdownCheckBox
+import com.android.settingslib.spa.widget.editor.SettingsDropdownCheckOption
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
+
+private const val TITLE = "Sample SettingsDropdownCheckBox"
+
+object SettingsDropdownCheckBoxProvider : SettingsPageProvider {
+ override val name = "SettingsDropdownCheckBox"
+
+ override fun getTitle(arguments: Bundle?): String {
+ return TITLE
+ }
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ RegularScaffold(title = TITLE) {
+ SettingsDropdownCheckBox(
+ label = "SettingsDropdownCheckBox",
+ options = remember {
+ listOf(
+ SettingsDropdownCheckOption("Item 1"),
+ SettingsDropdownCheckOption("Item 2"),
+ SettingsDropdownCheckOption("Item 3"),
+ )
+ },
+ )
+ SettingsDropdownCheckBox(
+ label = "Empty list",
+ options = emptyList(),
+ )
+ SettingsDropdownCheckBox(
+ label = "Disabled",
+ options = remember {
+ listOf(
+ SettingsDropdownCheckOption("Item 1", selected = mutableStateOf(true)),
+ SettingsDropdownCheckOption("Item 2"),
+ SettingsDropdownCheckOption("Item 3"),
+ )
+ },
+ enabled = false,
+ )
+ SettingsDropdownCheckBox(
+ label = "With disabled item",
+ options = remember {
+ listOf(
+ SettingsDropdownCheckOption("Item 1"),
+ SettingsDropdownCheckOption("Item 2"),
+ SettingsDropdownCheckOption(
+ text = "Disabled item 1",
+ changeable = false,
+ selected = mutableStateOf(true),
+ ),
+ SettingsDropdownCheckOption("Disabled item 2", changeable = false),
+ )
+ },
+ )
+ SettingsDropdownCheckBox(
+ label = "With select all",
+ options = remember {
+ listOf(
+ SettingsDropdownCheckOption("All", isSelectAll = true),
+ SettingsDropdownCheckOption("Item 1"),
+ SettingsDropdownCheckOption("Item 2"),
+ SettingsDropdownCheckOption("Item 3"),
+ )
+ },
+ )
+ SettingsDropdownCheckBox(
+ label = "With disabled item and select all",
+ options =
+ remember {
+ listOf(
+ SettingsDropdownCheckOption("All", isSelectAll = true, changeable = false),
+ SettingsDropdownCheckOption("Item 1"),
+ SettingsDropdownCheckOption("Item 2"),
+ SettingsDropdownCheckOption(
+ text = "Disabled item 1",
+ changeable = false,
+ selected = mutableStateOf(true),
+ ),
+ SettingsDropdownCheckOption("Disabled item 2", changeable = false),
+ )
+ },
+ )
+ }
+ }
+
+ fun buildInjectEntry(): SettingsEntryBuilder {
+ return SettingsEntryBuilder.createInject(owner = createSettingsPage()).setUiLayoutFn {
+ Preference(object : PreferenceModel {
+ override val title = TITLE
+ override val onClick = navigator(name)
+ })
+ }
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun SettingsDropdownCheckBoxPagePreview() {
+ SettingsTheme {
+ SettingsDropdownCheckBoxProvider.Page(null)
+ }
+}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuCheckBoxProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuCheckBoxProvider.kt
deleted file mode 100644
index d28964676bdd..000000000000
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuCheckBoxProvider.kt
+++ /dev/null
@@ -1,75 +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.settingslib.spa.gallery.editor
-
-import android.os.Bundle
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.mutableStateListOf
-import androidx.compose.runtime.remember
-import androidx.compose.ui.tooling.preview.Preview
-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.editor.SettingsExposedDropdownMenuCheckBox
-import com.android.settingslib.spa.widget.preference.Preference
-import com.android.settingslib.spa.widget.preference.PreferenceModel
-import com.android.settingslib.spa.widget.scaffold.RegularScaffold
-
-private const val TITLE = "Sample SettingsExposedDropdownMenuCheckBox"
-
-object SettingsExposedDropdownMenuCheckBoxProvider : SettingsPageProvider {
- override val name = "SettingsExposedDropdownMenuCheckBox"
- private const val exposedDropdownMenuCheckBoxLabel = "ExposedDropdownMenuCheckBoxLabel"
- private val options = listOf("item1", "item2", "item3")
- private val selectedOptionsState1 = mutableStateListOf(0, 1)
-
- override fun getTitle(arguments: Bundle?): String {
- return TITLE
- }
-
- @Composable
- override fun Page(arguments: Bundle?) {
- RegularScaffold(title = TITLE) {
- SettingsExposedDropdownMenuCheckBox(
- label = exposedDropdownMenuCheckBoxLabel,
- options = options,
- selectedOptionsState = remember { selectedOptionsState1 },
- enabled = true,
- onSelectedOptionStateChange = {},
- )
- }
- }
-
- fun buildInjectEntry(): SettingsEntryBuilder {
- return SettingsEntryBuilder.createInject(owner = createSettingsPage()).setUiLayoutFn {
- Preference(object : PreferenceModel {
- override val title = TITLE
- override val onClick = navigator(name)
- })
- }
- }
-}
-
-@Preview(showBackground = true)
-@Composable
-private fun SettingsExposedDropdownMenuCheckBoxPagePreview() {
- SettingsTheme {
- SettingsExposedDropdownMenuCheckBoxProvider.Page(null)
- }
-} \ No newline at end of file
diff --git a/packages/SettingsLib/Spa/gradle/libs.versions.toml b/packages/SettingsLib/Spa/gradle/libs.versions.toml
index f6fbc02de3b0..fe378c27523c 100644
--- a/packages/SettingsLib/Spa/gradle/libs.versions.toml
+++ b/packages/SettingsLib/Spa/gradle/libs.versions.toml
@@ -16,7 +16,7 @@
[versions]
agp = "8.2.2"
-compose-compiler = "1.5.8"
+compose-compiler = "1.5.9"
dexmaker-mockito = "2.28.3"
jvm = "17"
kotlin = "1.9.22"
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index 08a87973468b..2259bd74d56e 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -57,13 +57,13 @@ dependencies {
api("androidx.slice:slice-builders:1.1.0-alpha02")
api("androidx.slice:slice-core:1.1.0-alpha02")
api("androidx.slice:slice-view:1.1.0-alpha02")
- api("androidx.compose.material3:material3:1.2.0-rc01")
+ api("androidx.compose.material3:material3:1.2.0")
api("androidx.compose.material:material-icons-extended:$jetpackComposeVersion")
api("androidx.compose.runtime:runtime-livedata:$jetpackComposeVersion")
api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion")
api("androidx.lifecycle:lifecycle-livedata-ktx")
api("androidx.lifecycle:lifecycle-runtime-compose")
- api("androidx.navigation:navigation-compose:2.8.0-alpha01")
+ api("androidx.navigation:navigation-compose:2.8.0-alpha02")
api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha")
api("com.google.android.material:material:1.7.0-alpha03")
debugApi("androidx.compose.ui:ui-tooling:$jetpackComposeVersion")
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt
index 960ebccf6c25..8100fd58f5a0 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt
@@ -45,4 +45,6 @@ data class CardModel(
/** If specified, this color will be used to tint the icon and the buttons. */
val containerColor: Color = Color.Unspecified,
+
+ val onClick: (() -> Unit)? = null,
)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt
index 700fa487ed73..621825a82c1e 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt
@@ -17,6 +17,7 @@
package com.android.settingslib.spa.widget.card
import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
@@ -102,10 +103,11 @@ internal fun SettingsCardImpl(model: CardModel) {
AnimatedVisibility(visible = model.isVisible()) {
SettingsCardContent(containerColor = model.containerColor) {
Column(
- modifier = Modifier.padding(
- horizontal = SettingsDimension.dialogItemPaddingHorizontal,
- vertical = SettingsDimension.itemPaddingAround,
- ),
+ modifier = (model.onClick?.let { Modifier.clickable(onClick = it) } ?: Modifier)
+ .padding(
+ horizontal = SettingsDimension.dialogItemPaddingHorizontal,
+ vertical = SettingsDimension.itemPaddingAround,
+ ),
verticalArrangement = Arrangement.spacedBy(SettingsDimension.itemPaddingAround)
) {
CardHeader(model.imageVector, model.tintColor, model.onDismiss)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBox.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/DropdownTextBox.kt
index f6692a356899..679c562ac92d 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBox.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/DropdownTextBox.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -19,7 +19,6 @@ package com.android.settingslib.spa.widget.editor
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
-import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
@@ -31,80 +30,58 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
-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.SettingsTheme
-@Composable
+internal interface DropdownTextBoxScope {
+ fun dismiss()
+}
+
@OptIn(ExperimentalMaterial3Api::class)
-fun SettingsExposedDropdownMenuBox(
+@Composable
+internal fun DropdownTextBox(
label: String,
- options: List<String>,
- selectedOptionIndex: Int,
- enabled: Boolean,
- onselectedOptionTextChange: (Int) -> Unit,
+ text: String,
+ enabled: Boolean = true,
+ errorMessage: String? = null,
+ content: @Composable DropdownTextBoxScope.() -> Unit,
) {
var expanded by remember { mutableStateOf(false) }
+ val scope = remember {
+ object : DropdownTextBoxScope {
+ override fun dismiss() {
+ expanded = false
+ }
+ }
+ }
ExposedDropdownMenuBox(
expanded = expanded,
- onExpandedChange = { expanded = it },
+ onExpandedChange = { expanded = enabled && it },
modifier = Modifier
- .width(350.dp)
- .padding(SettingsDimension.menuFieldPadding),
+ .padding(SettingsDimension.menuFieldPadding)
+ .width(Width),
) {
OutlinedTextField(
// The `menuAnchor` modifier must be passed to the text field for correctness.
modifier = Modifier
.menuAnchor()
.fillMaxWidth(),
- value = options.getOrElse(selectedOptionIndex) { "" },
+ value = text,
onValueChange = { },
label = { Text(text = label) },
- trailingIcon = {
- ExposedDropdownMenuDefaults.TrailingIcon(
- expanded = expanded
- )
- },
+ trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
singleLine = true,
readOnly = true,
- enabled = enabled
+ enabled = enabled,
+ isError = errorMessage != null,
+ supportingText = errorMessage?.let { { Text(text = it) } },
)
- if (options.isNotEmpty()) {
- ExposedDropdownMenu(
- expanded = expanded,
- modifier = Modifier
- .fillMaxWidth(),
- onDismissRequest = { expanded = false },
- ) {
- options.forEach { option ->
- DropdownMenuItem(
- text = { Text(option) },
- onClick = {
- onselectedOptionTextChange(options.indexOf(option))
- expanded = false
- },
- contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding,
- )
- }
- }
- }
+ ExposedDropdownMenu(
+ expanded = expanded,
+ modifier = Modifier.width(Width),
+ onDismissRequest = { expanded = false },
+ ) { scope.content() }
}
}
-@Preview
-@Composable
-private fun SettingsExposedDropdownMenuBoxsPreview() {
- val item1 = "item1"
- val item2 = "item2"
- val item3 = "item3"
- val options = listOf(item1, item2, item3)
- SettingsTheme {
- SettingsExposedDropdownMenuBox(
- label = "ExposedDropdownMenuBoxLabel",
- options = options,
- selectedOptionIndex = 0,
- enabled = true,
- onselectedOptionTextChange = {})
- }
-} \ No newline at end of file
+private val Width = 310.dp
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsDropdownBox.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsDropdownBox.kt
new file mode 100644
index 000000000000..ff141c2b383c
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsDropdownBox.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2024 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.editor
+
+import androidx.compose.material3.DropdownMenuItem
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.ExposedDropdownMenuDefaults
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+
+@Composable
+@OptIn(ExperimentalMaterial3Api::class)
+fun SettingsDropdownBox(
+ label: String,
+ options: List<String>,
+ selectedOptionIndex: Int,
+ enabled: Boolean = true,
+ onSelectedOptionChange: (Int) -> Unit,
+) {
+ DropdownTextBox(
+ label = label,
+ text = options.getOrElse(selectedOptionIndex) { "" },
+ enabled = enabled && options.isNotEmpty(),
+ ) {
+ options.forEachIndexed { index, option ->
+ DropdownMenuItem(
+ text = { Text(option) },
+ onClick = {
+ dismiss()
+ onSelectedOptionChange(index)
+ },
+ contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding,
+ )
+ }
+ }
+}
+
+@Preview
+@Composable
+private fun SettingsDropdownBoxPreview() {
+ val item1 = "item1"
+ val item2 = "item2"
+ val item3 = "item3"
+ val options = listOf(item1, item2, item3)
+ SettingsTheme {
+ SettingsDropdownBox(
+ label = "ExposedDropdownMenuBoxLabel",
+ options = options,
+ selectedOptionIndex = 0,
+ enabled = true,
+ ) {}
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsDropdownCheckBox.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsDropdownCheckBox.kt
new file mode 100644
index 000000000000..0e7e49960be1
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsDropdownCheckBox.kt
@@ -0,0 +1,145 @@
+/*
+ * 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.editor
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.material3.Checkbox
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+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.widget.editor.SettingsDropdownCheckOption.Companion.changeable
+
+data class SettingsDropdownCheckOption(
+ /** The displayed text of this option. */
+ val text: String,
+
+ /** If true, check / uncheck this item will check / uncheck all enabled options. */
+ val isSelectAll: Boolean = false,
+
+ /** If not changeable, cannot check or uncheck this option. */
+ val changeable: Boolean = true,
+
+ /** The selected state of this option. */
+ val selected: MutableState<Boolean> = mutableStateOf(false),
+
+ /** Get called when the option is clicked, no matter if it's changeable. */
+ val onClick: () -> Unit = {},
+) {
+ companion object {
+ val List<SettingsDropdownCheckOption>.changeable: Boolean
+ get() = filter { !it.isSelectAll }.any { it.changeable }
+ }
+}
+
+@Composable
+fun SettingsDropdownCheckBox(
+ label: String,
+ options: List<SettingsDropdownCheckOption>,
+ emptyText: String = "",
+ enabled: Boolean = true,
+ errorMessage: String? = null,
+ onSelectedStateChange: () -> Unit = {},
+) {
+ DropdownTextBox(
+ label = label,
+ text = getDisplayText(options) ?: emptyText,
+ enabled = enabled && options.changeable,
+ errorMessage = errorMessage,
+ ) {
+ for (option in options) {
+ CheckboxItem(option) {
+ option.onClick()
+ if (option.changeable) {
+ checkboxItemOnClick(options, option)
+ onSelectedStateChange()
+ }
+ }
+ }
+ }
+}
+
+private fun getDisplayText(options: List<SettingsDropdownCheckOption>): String? {
+ val selectedOptions = options.filter { it.selected.value }
+ if (selectedOptions.isEmpty()) return null
+ return selectedOptions.filter { it.isSelectAll }.ifEmpty { selectedOptions }
+ .joinToString { it.text }
+}
+
+private fun checkboxItemOnClick(
+ options: List<SettingsDropdownCheckOption>,
+ clickedOption: SettingsDropdownCheckOption,
+) {
+ if (!clickedOption.changeable) return
+ val newChecked = !clickedOption.selected.value
+ if (clickedOption.isSelectAll) {
+ for (option in options.filter { it.changeable }) option.selected.value = newChecked
+ } else {
+ clickedOption.selected.value = newChecked
+ }
+ val (selectAllOptions, regularOptions) = options.partition { it.isSelectAll }
+ val isAllRegularOptionsChecked = regularOptions.all { it.selected.value }
+ selectAllOptions.forEach { it.selected.value = isAllRegularOptionsChecked }
+}
+
+@Composable
+private fun CheckboxItem(
+ option: SettingsDropdownCheckOption,
+ onClick: (SettingsDropdownCheckOption) -> Unit,
+) {
+ TextButton(
+ onClick = { onClick(option) },
+ modifier = Modifier.fillMaxWidth(),
+ ) {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.spacedBy(SettingsDimension.itemPaddingAround),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Checkbox(
+ checked = option.selected.value,
+ onCheckedChange = null,
+ enabled = option.changeable,
+ )
+ Text(text = option.text, modifier = Modifier.alphaForEnabled(option.changeable))
+ }
+ }
+}
+
+@Preview
+@Composable
+private fun ActionButtonsPreview() {
+ val item1 = SettingsDropdownCheckOption("item1")
+ val item2 = SettingsDropdownCheckOption("item2")
+ val item3 = SettingsDropdownCheckOption("item3")
+ val options = listOf(item1, item2, item3)
+ SettingsTheme {
+ SettingsDropdownCheckBox(
+ label = "label",
+ options = options,
+ )
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt
deleted file mode 100644
index e704505117cf..000000000000
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt
+++ /dev/null
@@ -1,164 +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.settingslib.spa.widget.editor
-
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.width
-import androidx.compose.material3.Checkbox
-import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.ExposedDropdownMenuBox
-import androidx.compose.material3.ExposedDropdownMenuDefaults
-import androidx.compose.material3.OutlinedTextField
-import androidx.compose.material3.Text
-import androidx.compose.material3.TextButton
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableIntStateOf
-import androidx.compose.runtime.mutableStateListOf
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.runtime.snapshots.SnapshotStateList
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.layout.onSizeChanged
-import androidx.compose.ui.platform.LocalDensity
-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.SettingsTheme
-
-@OptIn(ExperimentalMaterial3Api::class)
-@Composable
-fun SettingsExposedDropdownMenuCheckBox(
- label: String,
- options: List<String>,
- selectedOptionsState: SnapshotStateList<Int>,
- emptyVal: String = "",
- enabled: Boolean,
- errorMessage: String? = null,
- onSelectedOptionStateChange: () -> Unit,
-) {
- var dropDownWidth by remember { mutableIntStateOf(0) }
- var expanded by remember { mutableStateOf(false) }
- val allIndex = options.indexOf("*")
- ExposedDropdownMenuBox(
- expanded = expanded,
- onExpandedChange = { expanded = it },
- modifier = Modifier
- .width(350.dp)
- .padding(SettingsDimension.textFieldPadding)
- .onSizeChanged { dropDownWidth = it.width },
- ) {
- OutlinedTextField(
- // The `menuAnchor` modifier must be passed to the text field for correctness.
- modifier = Modifier
- .menuAnchor()
- .fillMaxWidth(),
- value = if (selectedOptionsState.size == 0) emptyVal
- else if (selectedOptionsState.contains(allIndex)) "*"
- else selectedOptionsState.joinToString { options[it] },
- onValueChange = {},
- label = { Text(text = label) },
- trailingIcon = {
- ExposedDropdownMenuDefaults.TrailingIcon(
- expanded = expanded
- )
- },
- readOnly = true,
- enabled = enabled,
- isError = errorMessage != null,
- supportingText = {
- if (errorMessage != null) {
- Text(text = errorMessage)
- }
- }
- )
- if (options.isNotEmpty()) {
- ExposedDropdownMenu(
- expanded = expanded,
- modifier = Modifier.width(with(LocalDensity.current) { dropDownWidth.toDp() }),
- onDismissRequest = { expanded = false },
- ) {
- options.forEachIndexed { index, option ->
- CheckboxItem(
- selectedOptionsState,
- index,
- allIndex,
- onSelectedOptionStateChange,
- option,
- )
- }
- }
- }
- }
-}
-
-@Composable
-private fun CheckboxItem(
- selectedOptionsState: SnapshotStateList<Int>,
- index: Int,
- allIndex: Int,
- onSelectedOptionStateChange: () -> Unit,
- option: String
-) {
- TextButton(
- modifier = Modifier.fillMaxWidth(),
- onClick = {
- if (selectedOptionsState.contains(index)) {
- if (index == allIndex) {
- selectedOptionsState.clear()
- } else {
- selectedOptionsState.remove(index)
- selectedOptionsState.remove(allIndex)
- }
- } else {
- selectedOptionsState.add(index)
- }
- onSelectedOptionStateChange()
- }) {
- Row(
- modifier = Modifier.fillMaxWidth(),
- horizontalArrangement = Arrangement.spacedBy(SettingsDimension.itemPaddingAround),
- verticalAlignment = Alignment.CenterVertically
- ) {
- Checkbox(
- checked = selectedOptionsState.contains(index),
- onCheckedChange = null,
- )
- Text(text = option)
- }
- }
-}
-
-@Preview
-@Composable
-private fun ActionButtonsPreview() {
- val options = listOf("item1", "item2", "item3")
- val selectedOptionsState = remember { mutableStateListOf(0, 1) }
- SettingsTheme {
- SettingsExposedDropdownMenuCheckBox(
- label = "label",
- options = options,
- selectedOptionsState = selectedOptionsState,
- enabled = true,
- onSelectedOptionStateChange = {})
- }
-}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextField.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextField.kt
index 2ce3c66796db..bdc6a6890cbd 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextField.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextField.kt
@@ -19,6 +19,7 @@ package com.android.settingslib.spa.widget.editor
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@@ -26,6 +27,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.framework.theme.SettingsTheme
@@ -37,7 +39,8 @@ fun SettingsOutlinedTextField(
errorMessage: String? = null,
singleLine: Boolean = true,
enabled: Boolean = true,
- onTextChange: (String) -> Unit,
+ shape: Shape = OutlinedTextFieldDefaults.shape,
+ onTextChange: (String) -> Unit
) {
OutlinedTextField(
modifier = Modifier
@@ -55,7 +58,8 @@ fun SettingsOutlinedTextField(
if (errorMessage != null) {
Text(text = errorMessage)
}
- }
+ },
+ shape = shape
)
}
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
index a0149da8f4b8..1a04bb8351db 100644
--- 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
@@ -37,11 +37,13 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.Role
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.widget.dialog.SettingsDialog
+import com.android.settingslib.spa.widget.ui.SettingsBody
import com.android.settingslib.spa.widget.ui.SettingsDialogItem
data class ListPreferenceOption(
val id: Int,
val text: String,
+ val summary: String = String()
)
/**
@@ -129,6 +131,14 @@ private fun Radio(
) {
RadioButton(selected = selected, onClick = null, enabled = enabled)
Spacer(modifier = Modifier.width(SettingsDimension.itemPaddingEnd))
- SettingsDialogItem(text = option.text, enabled = enabled)
+ Column {
+ SettingsDialogItem(text = option.text, enabled = enabled)
+ if (option.summary != String()) {
+ SettingsBody(
+ body = option.summary,
+ maxLines = 1
+ )
+ }
+ }
}
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt
index e36572fdff65..3216e37b5db4 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt
@@ -24,7 +24,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@@ -46,14 +45,14 @@ internal fun TwoTargetPreference(
verticalAlignment = Alignment.CenterVertically,
) {
Box(modifier = Modifier.weight(1f)) {
- Preference(remember {
+ Preference(
object : PreferenceModel {
override val title = title
override val summary = summary
override val icon = icon
override val onClick = onClick
}
- })
+ )
}
PreferenceDivider()
widget()
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCardTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCardTest.kt
index b5b2525bffdd..ffc7e86665dd 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCardTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCardTest.kt
@@ -141,6 +141,23 @@ class SettingsCardTest {
composeTestRule.onNodeWithText(TEXT).isNotDisplayed()
}
+ @Test
+ fun settingsCard_clickable() {
+ var clicked by mutableStateOf(false)
+ composeTestRule.setContent {
+ SettingsCard(
+ CardModel(
+ title = TITLE,
+ text = "",
+ ) { clicked = true }
+ )
+ }
+
+ composeTestRule.onNodeWithText(TITLE).performClick()
+
+ assertThat(clicked).isTrue()
+ }
+
private companion object {
const val TITLE = "Title"
const val TEXT = "Text"
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsDropdownBoxTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsDropdownBoxTest.kt
new file mode 100644
index 000000000000..c34742461774
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsDropdownBoxTest.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2024 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.editor
+
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.test.assertIsDisplayed
+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 org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SettingsDropdownBoxTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ @Test
+ fun dropdownMenuBox_displayed() {
+ composeTestRule.setContent {
+ var selectedItem by remember { mutableStateOf(0) }
+ SettingsDropdownBox(
+ label = LABEL,
+ options = options,
+ selectedOptionIndex = selectedItem,
+ ) { selectedItem = it }
+ }
+
+ composeTestRule.onNodeWithText(LABEL).assertIsDisplayed()
+ }
+
+ @Test
+ fun dropdownMenuBox_enabled_expanded() {
+ composeTestRule.setContent {
+ var selectedItem by remember { mutableIntStateOf(0) }
+ SettingsDropdownBox(
+ label = LABEL,
+ options = options,
+ selectedOptionIndex = selectedItem
+ ) { selectedItem = it }
+ }
+ composeTestRule.onNodeWithText(ITEM2).assertDoesNotExist()
+
+ composeTestRule.onNodeWithText(LABEL).performClick()
+
+ composeTestRule.onNodeWithText(ITEM2).assertIsDisplayed()
+ }
+
+ @Test
+ fun dropdownMenuBox_notEnabled_notExpanded() {
+ composeTestRule.setContent {
+ var selectedItem by remember { mutableIntStateOf(0) }
+ SettingsDropdownBox(
+ label = LABEL,
+ options = options,
+ enabled = false,
+ selectedOptionIndex = selectedItem
+ ) { selectedItem = it }
+ }
+ composeTestRule.onNodeWithText(ITEM2).assertDoesNotExist()
+
+ composeTestRule.onNodeWithText(LABEL).performClick()
+
+ composeTestRule.onNodeWithText(ITEM2).assertDoesNotExist()
+ }
+
+ @Test
+ fun dropdownMenuBox_valueChanged() {
+ composeTestRule.setContent {
+ var selectedItem by remember { mutableIntStateOf(0) }
+ SettingsDropdownBox(
+ label = LABEL,
+ options = options,
+ selectedOptionIndex = selectedItem
+ ) { selectedItem = it }
+ }
+ composeTestRule.onNodeWithText(ITEM2).assertDoesNotExist()
+
+ composeTestRule.onNodeWithText(LABEL).performClick()
+ composeTestRule.onNodeWithText(ITEM2).performClick()
+
+ composeTestRule.onNodeWithText(ITEM2).assertIsDisplayed()
+ }
+ private companion object {
+ const val LABEL = "Label"
+ const val ITEM2 = "item2"
+ val options = listOf("item1", ITEM2, "item3")
+ }
+}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsDropdownCheckBoxTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsDropdownCheckBoxTest.kt
new file mode 100644
index 000000000000..72b7b98c2a94
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsDropdownCheckBoxTest.kt
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2024 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.editor
+
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.hasAnyAncestor
+import androidx.compose.ui.test.hasText
+import androidx.compose.ui.test.isPopup
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
+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.testutils.hasRole
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SettingsDropdownCheckBoxTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ @Test
+ fun dropdownCheckBox_displayed() {
+ val item1 = SettingsDropdownCheckOption("item1")
+ val item2 = SettingsDropdownCheckOption("item2")
+ val item3 = SettingsDropdownCheckOption("item3")
+ composeTestRule.setContent {
+ SettingsDropdownCheckBox(
+ label = LABEL,
+ options = listOf(item1, item2, item3),
+ )
+ }
+
+ composeTestRule.onNodeWithText(LABEL).assertIsDisplayed()
+ }
+
+ @Test
+ fun dropdownCheckBox_expanded() {
+ val item1 = SettingsDropdownCheckOption("item1")
+ val item2 = SettingsDropdownCheckOption("item2")
+ val item3 = SettingsDropdownCheckOption("item3")
+ composeTestRule.setContent {
+ SettingsDropdownCheckBox(
+ label = LABEL,
+ options = listOf(item1, item2, item3),
+ )
+ }
+ composeTestRule.onOption(item3).assertDoesNotExist()
+
+ composeTestRule.onNodeWithText(LABEL).performClick()
+
+ composeTestRule.onOption(item3).assertIsDisplayed()
+ }
+
+ @Test
+ fun dropdownCheckBox_valueAdded() {
+ val item1 = SettingsDropdownCheckOption("item1")
+ val item2 = SettingsDropdownCheckOption("item2")
+ val item3 = SettingsDropdownCheckOption("item3")
+ composeTestRule.setContent {
+ SettingsDropdownCheckBox(
+ label = LABEL,
+ options = listOf(item1, item2, item3),
+ )
+ }
+ composeTestRule.onDropdownBox(item3.text).assertDoesNotExist()
+
+ composeTestRule.onNodeWithText(LABEL).performClick()
+ composeTestRule.onOption(item3).performClick()
+
+ composeTestRule.onDropdownBox(item3.text).assertIsDisplayed()
+ assertThat(item3.selected.value).isTrue()
+ }
+
+ @Test
+ fun dropdownCheckBox_valueDeleted() {
+ val item1 = SettingsDropdownCheckOption("item1")
+ val item2 = SettingsDropdownCheckOption("item2", selected = mutableStateOf(true))
+ val item3 = SettingsDropdownCheckOption("item3")
+ composeTestRule.setContent {
+ SettingsDropdownCheckBox(
+ label = LABEL,
+ options = listOf(item1, item2, item3),
+ )
+ }
+ composeTestRule.onDropdownBox(item2.text).assertIsDisplayed()
+
+ composeTestRule.onNodeWithText(LABEL).performClick()
+ composeTestRule.onOption(item2).performClick()
+
+ composeTestRule.onDropdownBox(item2.text).assertDoesNotExist()
+ assertThat(item2.selected.value).isFalse()
+ }
+
+ @Test
+ fun dropdownCheckBox_withSelectAll() {
+ val selectAll = SettingsDropdownCheckOption("All", isSelectAll = true)
+ val item1 = SettingsDropdownCheckOption("item1")
+ val item2 = SettingsDropdownCheckOption("item2")
+ composeTestRule.setContent {
+ SettingsDropdownCheckBox(
+ label = LABEL,
+ options = listOf(selectAll, item1, item2),
+ )
+ }
+
+ composeTestRule.onNodeWithText(LABEL).performClick()
+ composeTestRule.onOption(selectAll).performClick()
+
+ composeTestRule.onDropdownBox(selectAll.text).assertIsDisplayed()
+ composeTestRule.onDropdownBox(item1.text).assertDoesNotExist()
+ composeTestRule.onDropdownBox(item2.text).assertDoesNotExist()
+ assertThat(item1.selected.value).isTrue()
+ assertThat(item2.selected.value).isTrue()
+ }
+
+ private companion object {
+ const val LABEL = "Label"
+ }
+}
+
+private fun ComposeContentTestRule.onDropdownBox(text: String) =
+ onNode(hasRole(Role.DropdownList) and hasText(text))
+
+private fun ComposeContentTestRule.onOption(option: SettingsDropdownCheckOption) =
+ onNode(hasAnyAncestor(isPopup()) and hasText(option.text))
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBoxTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBoxTest.kt
deleted file mode 100644
index bc67e4c61ea5..000000000000
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBoxTest.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.settingslib.spa.widget.editor
-
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableIntStateOf
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.test.assertIsDisplayed
-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 org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
-class SettingsExposedDropdownMenuBoxTest {
- @get:Rule
- val composeTestRule = createComposeRule()
- private val options = listOf("item1", "item2", "item3")
- private val item2 = "item2"
- private val exposedDropdownMenuBoxLabel = "ExposedDropdownMenuBoxLabel"
-
- @Test
- fun exposedDropdownMenuBoxs_displayed() {
- composeTestRule.setContent {
- var selectedItem by remember { mutableStateOf(0) }
- SettingsExposedDropdownMenuBox(
- label = exposedDropdownMenuBoxLabel,
- options = options,
- selectedOptionIndex = selectedItem,
- enabled = true,
- onselectedOptionTextChange = { selectedItem = it })
- }
- composeTestRule.onNodeWithText(exposedDropdownMenuBoxLabel, substring = true)
- .assertIsDisplayed()
- }
-
- @Test
- fun exposedDropdownMenuBoxs_expanded() {
- composeTestRule.setContent {
- var selectedItem by remember { mutableIntStateOf(0) }
- SettingsExposedDropdownMenuBox(
- label = exposedDropdownMenuBoxLabel,
- options = options,
- selectedOptionIndex = selectedItem,
- enabled = true,
- onselectedOptionTextChange = { selectedItem = it })
- }
- composeTestRule.onNodeWithText(item2, substring = true)
- .assertDoesNotExist()
- composeTestRule.onNodeWithText(exposedDropdownMenuBoxLabel, substring = true)
- .performClick()
- composeTestRule.onNodeWithText(item2, substring = true)
- .assertIsDisplayed()
- }
-
- @Test
- fun exposedDropdownMenuBoxs_valueChanged() {
- composeTestRule.setContent {
- var selectedItem by remember { mutableIntStateOf(0) }
- SettingsExposedDropdownMenuBox(
- label = exposedDropdownMenuBoxLabel,
- options = options,
- selectedOptionIndex = selectedItem,
- enabled = true,
- onselectedOptionTextChange = { selectedItem = it })
- }
- composeTestRule.onNodeWithText(item2, substring = true)
- .assertDoesNotExist()
- composeTestRule.onNodeWithText(exposedDropdownMenuBoxLabel, substring = true)
- .performClick()
- composeTestRule.onNodeWithText(item2, substring = true)
- .performClick()
- composeTestRule.onNodeWithText(item2, substring = true)
- .assertIsDisplayed()
- }
-} \ No newline at end of file
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBoxTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBoxTest.kt
deleted file mode 100644
index 2b78ed7d6fa2..000000000000
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBoxTest.kt
+++ /dev/null
@@ -1,115 +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.settingslib.spa.widget.editor
-
-import androidx.compose.runtime.mutableStateListOf
-import androidx.compose.runtime.remember
-import androidx.compose.ui.test.SemanticsNodeInteraction
-import androidx.compose.ui.test.assertIsDisplayed
-import androidx.compose.ui.test.hasText
-import androidx.compose.ui.test.isFocused
-import androidx.compose.ui.test.junit4.ComposeContentTestRule
-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 org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
-class SettingsExposedDropdownMenuCheckBoxTest {
- @get:Rule
- val composeTestRule = createComposeRule()
- private val item1 = "item1"
- private val item2 = "item2"
- private val item3 = "item3"
- private val options = listOf(item1, item2, item3)
- private val selectedOptionsState1 = mutableStateListOf(0, 1)
- private val exposedDropdownMenuCheckBoxLabel = "ExposedDropdownMenuCheckBoxLabel"
-
- @Test
- fun exposedDropdownMenuCheckBox_displayed() {
- composeTestRule.setContent {
- SettingsExposedDropdownMenuCheckBox(
- label = exposedDropdownMenuCheckBoxLabel,
- options = options,
- selectedOptionsState = remember { selectedOptionsState1 },
- enabled = true,
- ) {}
- }
- composeTestRule.onNodeWithText(
- exposedDropdownMenuCheckBoxLabel, substring = true
- ).assertIsDisplayed()
- }
-
- @Test
- fun exposedDropdownMenuCheckBox_expanded() {
- composeTestRule.setContent {
- SettingsExposedDropdownMenuCheckBox(
- label = exposedDropdownMenuCheckBoxLabel,
- options = options,
- selectedOptionsState = remember { selectedOptionsState1 },
- enabled = true,
- ) {}
- }
- composeTestRule.onNodeWithText(item3, substring = true).assertDoesNotExist()
- composeTestRule.onNodeWithText(exposedDropdownMenuCheckBoxLabel, substring = true)
- .performClick()
- composeTestRule.onNodeWithText(item3, substring = true).assertIsDisplayed()
- }
-
- @Test
- fun exposedDropdownMenuCheckBox_valueAdded() {
- composeTestRule.setContent {
- SettingsExposedDropdownMenuCheckBox(
- label = exposedDropdownMenuCheckBoxLabel,
- options = options,
- selectedOptionsState = remember { selectedOptionsState1 },
- enabled = true,
- ) {}
- }
- composeTestRule.onNodeWithText(item3, substring = true).assertDoesNotExist()
- composeTestRule.onNodeWithText(exposedDropdownMenuCheckBoxLabel, substring = true)
- .performClick()
- composeTestRule.onNodeWithText(item3, substring = true).performClick()
- composeTestRule.onFocusedText(item3).assertIsDisplayed()
- }
-
- @Test
- fun exposedDropdownMenuCheckBox_valueDeleted() {
- composeTestRule.setContent {
- SettingsExposedDropdownMenuCheckBox(
- label = exposedDropdownMenuCheckBoxLabel,
- options = options,
- selectedOptionsState = remember { selectedOptionsState1 },
- enabled = true,
- ) {}
- }
- composeTestRule.onNodeWithText(item2, substring = true).assertIsDisplayed()
- composeTestRule.onNodeWithText(exposedDropdownMenuCheckBoxLabel, substring = true)
- .performClick()
- composeTestRule.onNotFocusedText(item2).performClick()
- composeTestRule.onFocusedText(item2).assertDoesNotExist()
- }
-}
-
-fun ComposeContentTestRule.onFocusedText(text: String): SemanticsNodeInteraction =
- onNode(isFocused() and hasText(text, substring = true))
-
-fun ComposeContentTestRule.onNotFocusedText(text: String): SemanticsNodeInteraction =
- onNode(!isFocused() and hasText(text, substring = true)) \ No newline at end of file
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
index 796ac4851cb5..417ce6ed830b 100644
--- 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
@@ -123,6 +123,26 @@ class ListPreferenceTest {
}
@Test
+ fun click_optionsNotEmptyAndItemHasSummary_itemShowSummary() {
+ composeTestRule.setContent {
+ ListPreference(remember {
+ object : ListPreferenceModel {
+ override val title = TITLE
+ override val options =
+ listOf(ListPreferenceOption(id = 1, text = "A", summary = "A_Summary"))
+ override val selectedId = mutableIntStateOf(1)
+ override val onIdSelected: (Int) -> Unit = {}
+ }
+ })
+ }
+
+ composeTestRule.onNodeWithText(TITLE).performClick()
+
+ composeTestRule.onDialogText(TITLE).assertIsDisplayed()
+ composeTestRule.onNodeWithText("A_Summary").assertIsDisplayed()
+ }
+
+ @Test
fun select() {
val selectedId = mutableIntStateOf(1)
composeTestRule.setContent {
diff --git a/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/SemanticsMatcher.kt b/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/SemanticsMatcher.kt
new file mode 100644
index 000000000000..856bed603354
--- /dev/null
+++ b/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/SemanticsMatcher.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 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.testutils
+
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.SemanticsProperties
+import androidx.compose.ui.semantics.getOrNull
+import androidx.compose.ui.test.SemanticsMatcher
+
+fun hasRole(role: Role) = SemanticsMatcher("${SemanticsProperties.Role.name} has $role") {
+ it.config.getOrNull(SemanticsProperties.Role) == role
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
index 9432d5995151..6b1893c73b3f 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
@@ -31,7 +31,6 @@ import kotlinx.coroutines.flow.flowOn
data class EnhancedConfirmation(
val key: String,
- val uid: Int,
val packageName: String,
)
data class Restrictions(
@@ -91,7 +90,7 @@ internal class RestrictionsProviderImpl(
restrictions.enhancedConfirmation?.let { ec ->
RestrictedLockUtilsInternal
.checkIfRequiresEnhancedConfirmation(context, ec.key,
- ec.uid, ec.packageName)
+ ec.packageName)
?.let { intent -> return BlockedByEcmImpl(context = context, intent = intent) }
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
index 74b556ea106e..27e00c05c33f 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
@@ -159,7 +159,6 @@ internal fun <T : AppRecord> TogglePermissionAppListModel<T>.TogglePermissionApp
keys = switchRestrictionKeys,
enhancedConfirmation = enhancedConfirmationKey?.let { EnhancedConfirmation(
key = it,
- uid = checkNotNull(applicationInfo).uid,
packageName = packageName) })
RestrictedSwitchPreference(switchModel, restrictions, restrictionsProviderFactory)
InfoPageAdditionalContent(record, isAllowed)
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
index 4b474379c54b..2e8b76a03722 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
@@ -150,15 +150,13 @@ internal class TogglePermissionInternalAppListModel<T : AppRecord>(
@Composable
fun getSummary(record: T): () -> String {
- val restrictions = remember(record.app.userId,
- record.app.uid, record.app.packageName) {
+ val restrictions = remember(record.app.userId, record.app.packageName) {
Restrictions(
userId = record.app.userId,
keys = listModel.switchRestrictionKeys,
enhancedConfirmation = listModel.enhancedConfirmationKey?.let {
EnhancedConfirmation(
key = it,
- uid = record.app.uid,
packageName = record.app.packageName)
})
}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceTest.kt
index 00ba9b4d9836..b88d1c5760a6 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceTest.kt
@@ -32,6 +32,7 @@ import com.android.settingslib.spaprivileged.model.enterprise.BaseUserRestricted
import com.android.settingslib.spaprivileged.model.enterprise.NoRestricted
import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
import com.android.settingslib.spaprivileged.tests.testutils.FakeBlockedByAdmin
+import com.android.settingslib.spaprivileged.tests.testutils.FakeBlockedByEcm
import com.android.settingslib.spaprivileged.tests.testutils.FakeRestrictionsProvider
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
@@ -44,6 +45,7 @@ class RestrictedSwitchPreferenceTest {
val composeTestRule = createComposeRule()
private val fakeBlockedByAdmin = FakeBlockedByAdmin()
+ private val fakeBlockedByEcm = FakeBlockedByEcm()
private val fakeRestrictionsProvider = FakeRestrictionsProvider()
@@ -141,6 +143,29 @@ class RestrictedSwitchPreferenceTest {
assertThat(fakeBlockedByAdmin.sendShowAdminSupportDetailsIntentIsCalled).isTrue()
}
+ @Test
+ fun whenBlockedByEcm_disabled() {
+ val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
+ fakeRestrictionsProvider.restrictedMode = fakeBlockedByEcm
+
+ setContent(restrictions)
+
+ composeTestRule.onNodeWithText(TITLE).assertIsDisplayed().assertIsEnabled()
+ composeTestRule.onNodeWithText(FakeBlockedByEcm.SUMMARY).assertIsDisplayed()
+ composeTestRule.onNode(isOn()).assertIsDisplayed()
+ }
+
+ @Test
+ fun whenBlockedByEcm_click() {
+ val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
+ fakeRestrictionsProvider.restrictedMode = fakeBlockedByEcm
+
+ setContent(restrictions)
+ composeTestRule.onRoot().performClick()
+
+ assertThat(fakeBlockedByEcm.showRestrictedSettingsDetailsIsCalled).isTrue()
+ }
+
private fun setContent(restrictions: Restrictions) {
composeTestRule.setContent {
RestrictedSwitchPreference(switchPreferenceModel, restrictions) { _, _ ->
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItemTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItemTest.kt
index 2ccf323de2a3..556adc750763 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItemTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItemTest.kt
@@ -29,6 +29,7 @@ import com.android.settingslib.spaprivileged.model.enterprise.BaseUserRestricted
import com.android.settingslib.spaprivileged.model.enterprise.NoRestricted
import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
import com.android.settingslib.spaprivileged.tests.testutils.FakeBlockedByAdmin
+import com.android.settingslib.spaprivileged.tests.testutils.FakeBlockedByEcm
import com.android.settingslib.spaprivileged.tests.testutils.FakeRestrictionsProvider
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
@@ -41,6 +42,7 @@ class RestrictedMenuItemTest {
val composeTestRule = createComposeRule()
private val fakeBlockedByAdmin = FakeBlockedByAdmin()
+ private val fakeBlockedByEcm = FakeBlockedByEcm()
private val fakeRestrictionsProvider = FakeRestrictionsProvider()
@@ -129,6 +131,28 @@ class RestrictedMenuItemTest {
assertThat(menuItemOnClickIsCalled).isFalse()
}
+ @Test
+ fun whenBlockedByEcm_disabled() {
+ val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
+ fakeRestrictionsProvider.restrictedMode = fakeBlockedByEcm
+
+ setContent(restrictions)
+
+ composeTestRule.onNodeWithText(TEXT).assertIsDisplayed().assertIsEnabled()
+ }
+
+ @Test
+ fun whenBlockedByEcm_onClick_showEcmDetails() {
+ val restrictions = Restrictions(userId = USER_ID, keys = listOf(RESTRICTION_KEY))
+ fakeRestrictionsProvider.restrictedMode = fakeBlockedByEcm
+
+ setContent(restrictions)
+ composeTestRule.onRoot().performClick()
+
+ assertThat(fakeBlockedByEcm.showRestrictedSettingsDetailsIsCalled).isTrue()
+ assertThat(menuItemOnClickIsCalled).isFalse()
+ }
+
private fun setContent(restrictions: Restrictions) {
val fakeMoreOptionsScope = object : MoreOptionsScope() {
override fun dismiss() {}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/RestrictedTestUtils.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/RestrictedTestUtils.kt
index 93fa17d1eeca..f8ca2a084f14 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/RestrictedTestUtils.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/RestrictedTestUtils.kt
@@ -19,6 +19,7 @@ package com.android.settingslib.spaprivileged.tests.testutils
import androidx.compose.runtime.Composable
import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spaprivileged.model.enterprise.BlockedByAdmin
+import com.android.settingslib.spaprivileged.model.enterprise.BlockedByEcm
import com.android.settingslib.spaprivileged.model.enterprise.RestrictedMode
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProvider
@@ -36,6 +37,18 @@ class FakeBlockedByAdmin : BlockedByAdmin {
}
}
+class FakeBlockedByEcm : BlockedByEcm {
+ var showRestrictedSettingsDetailsIsCalled = false
+
+ override fun showRestrictedSettingsDetails() {
+ showRestrictedSettingsDetailsIsCalled = true
+ }
+
+ companion object {
+ const val SUMMARY = "Disabled"
+ }
+}
+
class FakeRestrictionsProvider : RestrictionsProvider {
var restrictedMode: RestrictedMode? = null
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index d622eb859cd1..6a1ee3a3c623 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -23,3 +23,24 @@ flag {
description: "Displays the auto on toggle in the bluetooth QS tile dialog"
bug: "316985153"
}
+
+flag {
+ name: "legacy_le_audio_sharing"
+ namespace: "pixel_cross_device_control"
+ description: "Gates the legacy le audio sharing UI."
+ bug: "322295262"
+}
+
+flag {
+ name: "enable_le_audio_sharing"
+ namespace: "pixel_cross_device_control"
+ description: "Gates whether to enable LE audio sharing"
+ bug: "305620450"
+}
+
+flag {
+ name: "enable_le_audio_qr_code_private_broadcast_sharing"
+ namespace: "pixel_cross_device_control"
+ description: "Gates whether to enable LE audio private broadcast sharing via QR code"
+ bug: "308368124"
+} \ No newline at end of file
diff --git a/packages/SettingsLib/res/drawable/ic_bt_le_audio_sharing.xml b/packages/SettingsLib/res/drawable/ic_bt_le_audio_sharing.xml
new file mode 100644
index 000000000000..618677389ce1
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_bt_le_audio_sharing.xml
@@ -0,0 +1,87 @@
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:autoMirrored="true"
+ android:height="24dp"
+ android:width="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="#000000"
+ android:pathData="M16.984,24H7.279L12.131,15.508L16.984,24ZM10.481,22.144H13.781L12.131,19.257L10.481,22.144Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M12.131,14.295C13.471,14.295 14.558,13.209 14.558,11.869C14.558,10.529 13.471,9.442 12.131,9.442C10.791,9.442 9.705,10.529 9.705,11.869C9.705,13.209 10.791,14.295 12.131,14.295Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M4.573,21.368C4.052,20.943 3.967,20.179 4.379,19.657C4.804,19.136 5.568,19.051 6.09,19.463C6.611,19.876 6.696,20.64 6.284,21.174C6.041,21.465 5.689,21.623 5.338,21.623C5.071,21.623 4.804,21.538 4.573,21.368Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M17.991,21.162C17.579,20.628 17.663,19.876 18.185,19.451C18.707,19.039 19.471,19.124 19.896,19.646C20.308,20.167 20.223,20.931 19.702,21.344C19.471,21.526 19.204,21.611 18.949,21.611C18.586,21.611 18.234,21.453 17.991,21.162Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M1.213,17.145C0.91,16.551 1.165,15.823 1.771,15.532C2.378,15.241 3.093,15.495 3.397,16.09C3.688,16.697 3.433,17.424 2.827,17.715C2.657,17.8 2.475,17.837 2.305,17.837C1.844,17.837 1.419,17.582 1.213,17.145Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M21.449,17.691C20.842,17.4 20.588,16.684 20.879,16.077C21.17,15.471 21.898,15.216 22.504,15.507C23.099,15.798 23.354,16.526 23.062,17.133C22.856,17.557 22.419,17.812 21.971,17.812C21.789,17.812 21.619,17.776 21.449,17.691Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M0,11.892C0,11.225 0.546,10.679 1.213,10.679C1.88,10.679 2.426,11.212 2.426,11.892C2.426,12.559 1.88,13.105 1.213,13.105C0.546,13.105 0,12.559 0,11.892Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M21.837,11.869C21.837,11.857 21.837,11.845 21.837,11.833C21.824,11.153 22.37,10.62 23.05,10.607C23.717,10.607 24.251,11.153 24.263,11.821C24.263,11.833 24.263,11.845 24.263,11.845C24.263,11.857 24.263,11.869 24.263,11.869C24.263,12.536 23.717,13.082 23.05,13.082C22.382,13.082 21.837,12.536 21.837,11.869Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M1.759,8.242C1.152,7.963 0.898,7.235 1.189,6.628C1.48,6.022 2.196,5.767 2.802,6.058C3.409,6.349 3.664,7.077 3.372,7.684C3.166,8.108 2.729,8.363 2.281,8.363C2.099,8.363 1.929,8.327 1.759,8.242Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M20.866,7.622C20.563,7.028 20.818,6.3 21.424,6.009C22.019,5.706 22.747,5.96 23.038,6.567C23.038,6.567 23.038,6.567 23.05,6.567C23.341,7.161 23.087,7.889 22.48,8.181C22.31,8.265 22.128,8.302 21.958,8.302C21.509,8.302 21.073,8.059 20.866,7.622Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M4.355,4.104C3.931,3.582 4.016,2.818 4.537,2.406C5.071,1.981 5.823,2.066 6.248,2.588C6.672,3.109 6.588,3.874 6.066,4.298C5.835,4.48 5.569,4.565 5.302,4.565C4.95,4.565 4.598,4.407 4.355,4.104Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M18.161,4.262C17.627,3.838 17.542,3.073 17.955,2.552C18.379,2.03 19.132,1.945 19.666,2.358C20.187,2.77 20.272,3.534 19.86,4.068C19.617,4.359 19.265,4.517 18.913,4.517C18.646,4.517 18.379,4.432 18.161,4.262Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M8.492,1.497C8.334,0.854 8.747,0.199 9.402,0.041C10.057,-0.105 10.7,0.308 10.858,0.963C11.003,1.606 10.591,2.261 9.948,2.407C9.851,2.431 9.754,2.443 9.669,2.443C9.123,2.443 8.613,2.067 8.492,1.497Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M14.267,2.395C13.599,2.249 13.199,1.606 13.345,0.951C13.49,0.296 14.133,-0.116 14.788,0.029C15.443,0.175 15.856,0.83 15.71,1.485C15.589,2.043 15.08,2.431 14.534,2.431C14.437,2.431 14.352,2.419 14.267,2.395Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M7,17.037C6.527,16.564 6.527,15.8 7,15.326C7.473,14.841 8.237,14.841 8.71,15.314C9.196,15.787 9.196,16.552 8.723,17.025C8.48,17.267 8.177,17.389 7.861,17.389C7.546,17.389 7.242,17.267 7,17.037Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M15.565,17.012C15.092,16.539 15.092,15.762 15.565,15.289C16.038,14.816 16.814,14.816 17.288,15.289C17.761,15.762 17.761,16.539 17.288,17.012C17.045,17.243 16.742,17.364 16.426,17.364C16.111,17.364 15.807,17.243 15.565,17.012Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M4.853,11.917C4.853,11.237 5.386,10.691 6.054,10.691C6.721,10.691 7.279,11.225 7.279,11.892C7.279,12.56 6.745,13.106 6.078,13.118C5.398,13.118 4.853,12.584 4.853,11.917Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M16.984,11.868C16.984,11.856 16.984,11.844 16.984,11.832C16.984,11.832 16.984,11.82 16.984,11.807C16.972,11.14 17.506,10.582 18.185,10.582C18.852,10.57 19.398,11.116 19.41,11.783C19.41,11.795 19.41,11.82 19.41,11.832C19.41,11.844 19.41,11.856 19.41,11.868C19.41,12.535 18.865,13.081 18.197,13.081C17.53,13.081 16.984,12.535 16.984,11.868Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M6.952,8.471C6.478,7.997 6.478,7.233 6.952,6.76C6.952,6.76 6.952,6.76 6.939,6.76C7.413,6.275 8.189,6.275 8.662,6.748C9.135,7.221 9.147,7.985 8.674,8.458C8.432,8.701 8.116,8.822 7.813,8.822C7.497,8.822 7.194,8.701 6.952,8.471Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M15.529,8.399C15.043,7.938 15.043,7.161 15.504,6.688C15.977,6.203 16.742,6.203 17.227,6.664C17.7,7.137 17.712,7.901 17.239,8.387C17.009,8.629 16.693,8.751 16.378,8.751C16.075,8.751 15.759,8.629 15.529,8.399Z"/>
+ <path
+ android:fillColor="#000000"
+ android:pathData="M10.87,5.815C10.858,5.148 11.392,4.59 12.071,4.59C12.738,4.578 13.284,5.124 13.284,5.791C13.296,6.458 12.762,7.016 12.083,7.016C11.416,7.016 10.87,6.483 10.87,5.815Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index ca93fa54faa5..8d3582a736fe 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> oor tot vol"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> oor tot vol"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Laaiproses word geoptimeer"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – Laaiproses word geoptimeer"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Onbekend"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Laai"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Laai tans vinnig"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Kon nie \'n nuwe gebruiker skep nie"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Kon nie ’n nuwe gas skep nie"</string>
<string name="user_nickname" msgid="262624187455825083">"Bynaam"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Jou naam en prent sal sigbaar wees vir enigiemand wat hierdie toestel gebruik."</string>
<string name="user_add_user" msgid="7876449291500212468">"Voeg gebruiker by"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Voeg gas by"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Verwyder gas"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index ccaea8f933e2..f6931ad30353 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"እስኪሞላ ድረስ <xliff:g id="TIME">%1$s</xliff:g> ይቀራል"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - እስኪሞላ ድረስ <xliff:g id="TIME">%2$s</xliff:g> ይቀራል"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ኃይል መሙላት እንዲተባ ተደርጓል"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - ኃይል መሙላት እንዲተባ ተደርጓል"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"ያልታወቀ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ኃይል በመሙላት ላይ"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ኃይል በፍጥነት በመሙላት ላይ"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"አዲስ ተጠቃሚን መፍጠር አልተሳካም"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"አዲስ እንግዳ መፍጠር አልተሳካም"</string>
<string name="user_nickname" msgid="262624187455825083">"ቅጽል ስም"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"ይህን መሳሪያ ለሚጠቀም ማንኛውም ሰው ስምዎ እና ምስልዎ ይታያል።"</string>
<string name="user_add_user" msgid="7876449291500212468">"ተጠቃሚን አክል"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"እንግዳን አክል"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"እንግዳን አስወግድ"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 00bc6137cd44..92dc69ca54fe 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"يتبقّى <xliff:g id="TIME">%1$s</xliff:g> حتى اكتمال شحن البطارية."</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - يتبقّى <xliff:g id="TIME">%2$s</xliff:g> حتى اكتمال شحن البطارية."</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - تم تحسين الشحن"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - تم تحسين الشحن"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"غير معروف"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"جارٍ الشحن"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"جارٍ الشحن سريعًا"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"تعذّر إنشاء مستخدم جديد."</string>
<string name="add_guest_failed" msgid="8074548434469843443">"تعذّر إنشاء جلسة ضيف جديدة."</string>
<string name="user_nickname" msgid="262624187455825083">"اللقب"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"سيكون اسمك وصورتك مرئيَين لأي شخص يستخدم هذا الجهاز."</string>
<string name="user_add_user" msgid="7876449291500212468">"إضافة مستخدم"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"إضافة ضيف"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"إزالة جلسة الضيف"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index 59c87d2ff062..48d3df4621f3 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"সম্পূৰ্ণ হ’বলৈ <xliff:g id="TIME">%1$s</xliff:g> বাকী আছে"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"সম্পূৰ্ণ হ’বলৈ <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> বাকী আছে"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - চাৰ্জিং অপ্টিমাইজ কৰা হৈছে"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - চাৰ্জিং অপ্টিমাইজ কৰা হৈছে"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"অজ্ঞাত"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"চাৰ্জ কৰি থকা হৈছে"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"দ্ৰুততাৰে চাৰ্জ হৈছে"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"নতুন ব্যৱহাৰকাৰী সৃষ্টি কৰিব পৰা নগ’ল"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"নতুন অতিথি সৃষ্টি কৰিব পৰা নগ’ল"</string>
<string name="user_nickname" msgid="262624187455825083">"উপনাম"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"এই ডিভাইচটো ব্যৱহাৰ কৰা যিকোনো লোকে আপোনাৰ নাম আৰু চিত্ৰ দেখা পাব।"</string>
<string name="user_add_user" msgid="7876449291500212468">"ব্যৱহাৰকাৰী যোগ দিয়ক"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"অতিথি যোগ দিয়ক"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"অতিথি আঁতৰাওক"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 7dfe3bef233c..e27a40cdf7b1 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Tam şarj edilənədək <xliff:g id="TIME">%1$s</xliff:g> qalıb"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - tam şarj edilənədək <xliff:g id="TIME">%2$s</xliff:g> qalıb"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Şarj optimallaşdırılıb"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Şarj optimallaşdırılıb"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Naməlum"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Enerji doldurma"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Sürətlə doldurulur"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Yeni istifadəçi yaratmaq alınmadı"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Yeni qonaq yaratmaq alınmadı"</string>
<string name="user_nickname" msgid="262624187455825083">"Ləqəb"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Ad və şəklinizi bu cihazdan istifadə edən hər kəs görəcək."</string>
<string name="user_add_user" msgid="7876449291500212468">"İstifadəçi əlavə edin"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Qonaq əlavə edin"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Qonağı silin"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 8786acb36be1..dd48b70bd8ed 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> do kraja punjenja"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do kraja punjenja"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje je optimizovano"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje je optimizovano"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Nepoznato"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Puni se"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo se puni"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Pravljenje novog korisnika nije uspelo"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Pravljenje novog gosta nije uspelo"</string>
<string name="user_nickname" msgid="262624187455825083">"Nadimak"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Ime i slika će biti vidljivi svakom ko koristi ovaj uređaj."</string>
<string name="user_add_user" msgid="7876449291500212468">"Dodaj korisnika"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gosta"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Ukloni gosta"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 4a1f939617be..48247a01034f 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Да поўнай зарадкі засталося <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – да поўнай зарадкі засталося: <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Зарадка аптымізавана"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – Зарадка аптымізавана"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Невядома"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Зарадка"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Хуткая зарадка"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Не ўдалося стварыць новага карыстальніка"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Не ўдалося стварыць новага госця"</string>
<string name="user_nickname" msgid="262624187455825083">"Псеўданім"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Ваша імя і відарыс профілю будуць бачныя ўсім, хто выкарыстоўвае гэту прыладу."</string>
<string name="user_add_user" msgid="7876449291500212468">"Дадаць карыстальніка"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Дадаць госця"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Выдаліць госця"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 3bc531ff94a1..035b1dd19c5a 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Оставащо време до пълно зареждане: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – Оставащо време до пълно зареждане: <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Зареждането е оптимизирано"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – Зареждането е оптимизирано"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Неизвестно"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Зарежда се"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Зарежда се бързо"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Неуспешно създаване на нов потребител"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Създаването на нов гост не бе успешно"</string>
<string name="user_nickname" msgid="262624187455825083">"Псевдоним"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Вашето име и снимка няма да бъдат видими за нито един потребител, който използва това устройство."</string>
<string name="user_add_user" msgid="7876449291500212468">"Добавяне на потреби­тел"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Добавяне на гост"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Премахване на госта"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 1342aa3e43ed..9a2f88c1eb1f 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g>-এ ব্যাটারি পুরো চার্জ হয়ে যাবে"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>-এ ব্যাটারি পুরো চার্জ হয়ে যাবে"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - চার্জিং অপ্টিমাইজ করা হয়েছে"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - চার্জিং অপ্টিমাইজ করা হয়েছে"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"অজানা"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"চার্জ হচ্ছে"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"দ্রুত চার্জ হচ্ছে"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"নতুন ব্যবহারকারী যোগ করা যায়নি"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"নতুন অতিথি তৈরি করা যায়নি"</string>
<string name="user_nickname" msgid="262624187455825083">"বিশেষ নাম"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"এই ডিভাইস ব্যবহার করেন এমন যেকেউ আপনার নাম ও ছবি দেখতে পাবেন।"</string>
<string name="user_add_user" msgid="7876449291500212468">"ব্যবহারকারীর অ্যাকাউন্ট যোগ করুন"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"অতিথি যোগ করুন"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"অতিথি সরান"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 996856257213..01662dd8bed6 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> do potpune napunjenosti"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do potpune napunjenosti"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje je optimizirano"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje je optimizirano"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Nepoznato"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Punjenje"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo punjenje"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Kreiranje novog korisnika nije uspjelo"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Kreiranje novog gosta nije uspjelo"</string>
<string name="user_nickname" msgid="262624187455825083">"Nadimak"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Vaše ime i slika će biti vidljivi svakom ko koristi ovaj uređaj."</string>
<string name="user_add_user" msgid="7876449291500212468">"Dodavanje korisnika"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Dodavanje gosta"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Ukloni gosta"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 98226f62ddd3..61898881baac 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> per completar la càrrega"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> per completar la càrrega"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g>: càrrega optimitzada"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g>: càrrega optimitzada"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Desconegut"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"S\'està carregant"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregant ràpidament"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"No s\'ha pogut crear l\'usuari"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"No s\'ha pogut crear un convidat"</string>
<string name="user_nickname" msgid="262624187455825083">"Àlies"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"El teu nom i la teva foto seran visibles per a qualsevol persona que utilitzi aquest dispositiu."</string>
<string name="user_add_user" msgid="7876449291500212468">"Afegeix un usuari"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Afegeix un convidat"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Suprimeix el convidat"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 96272a8bd91f..ec104b4a95e4 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> do úplného nabití"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabití"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – optimalizované nabíjení"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – optimalizované nabíjení"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Neznámé"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Nabíjí se"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Rychlé nabíjení"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Nového uživatele se nepodařilo vytvořit"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Vytvoření nového hosta se nezdařilo"</string>
<string name="user_nickname" msgid="262624187455825083">"Přezdívka"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Vaše jméno a fotku uvidí kdokoli, kdo toto zařízení použije."</string>
<string name="user_add_user" msgid="7876449291500212468">"Přidat uživatele"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Přidat hosta"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Odstranit hosta"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index c1e200622426..b92fc040cd30 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Fuldt opladet om <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – fuldt opladet om <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – opladning er optimeret"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – opladning optimeres"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Ukendt"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Oplader"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Oplader hurtigt"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Der kunne ikke oprettes en ny bruger"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Der kunne ikke oprettes en ny gæst"</string>
<string name="user_nickname" msgid="262624187455825083">"Kaldenavn"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Dit navn og profilbillede vil være synligt for alle, der bruger denne enhed."</string>
<string name="user_add_user" msgid="7876449291500212468">"Tilføj bruger"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Tilføj gæst"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Fjern gæsten"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index f6be521667d6..debdb1e6b361 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Voll in <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – voll in <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Laden wird optimiert"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – Laden wird optimiert"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Unbekannt"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Wird aufgeladen"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Schnelles Aufladen"</string>
@@ -599,7 +600,7 @@
<string name="user_add_user_message_long" msgid="1527434966294733380">"Du kannst dieses Gerät zusammen mit anderen nutzen, indem du weitere Nutzer erstellst. Jeder erhält einen eigenen Bereich, in dem er Apps, den Hintergrund usw. personalisieren kann. Außerdem lassen sich Geräteeinstellungen wie WLAN ändern, die sich auf alle Nutzer auswirken.\n\nWenn du einen neuen Nutzer hinzufügst, muss dieser seinen Bereich einrichten.\n\nJeder Nutzer kann Apps für alle anderen Nutzer aktualisieren. Einstel­lun­gen und Dienste für die Bedie­nungs­hil­fen werden mög­licher­weise nicht auf den neuen Nutzer übertragen."</string>
<string name="user_add_user_message_short" msgid="3295959985795716166">"Wenn du einen neuen Nutzer hinzufügst, muss dieser seinen Bereich einrichten.\n\nJeder Nutzer kann Apps für alle anderen Nutzer aktualisieren."</string>
<string name="user_grant_admin_title" msgid="5157031020083343984">"Diesen Nutzer als Administrator festlegen?"</string>
- <string name="user_grant_admin_message" msgid="1673791931033486709">"Im Gegensatz zu anderen Nutzern haben Administratoren besondere Berechtigungen. Ein Administrator kann alle Nutzer verwalten, dieses Gerät aktualisieren oder zurücksetzen, Einstellungen ändern, alle installierten Apps sehen und anderen Administrator­berech­ti­gungen gewähren oder diese widerrufen."</string>
+ <string name="user_grant_admin_message" msgid="1673791931033486709">"Im Gegensatz zu anderen Nutzern haben Administratoren besondere Berechtigungen. Ein Administrator kann alle Nutzer verwalten, dieses Gerät aktualisieren oder zurücksetzen, Einstellungen ändern, alle installierten Apps sehen und anderen Administrator­­­berech­ti­gungen gewähren oder diese widerrufen."</string>
<string name="user_grant_admin_button" msgid="5441486731331725756">"Als Administrator festlegen"</string>
<string name="user_setup_dialog_title" msgid="8037342066381939995">"Nutzer jetzt einrichten?"</string>
<string name="user_setup_dialog_message" msgid="269931619868102841">"Die Person muss Zugang zum Gerät haben und bereit sein, ihren Bereich einzurichten."</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Nutzer konnte nicht erstellt werden"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Fehler beim Erstellen eines neuen Gasts"</string>
<string name="user_nickname" msgid="262624187455825083">"Alias"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Dein Name und Bild sind für alle Nutzer dieses Geräts sichtbar."</string>
<string name="user_add_user" msgid="7876449291500212468">"Nutzer hinzufügen"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Gast hinzufügen"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Gast entfernen"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 61e8ab43c850..6cf05597feb2 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Απομένουν <xliff:g id="TIME">%1$s</xliff:g> για πλήρη φόρτιση"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - Απομένουν <xliff:g id="TIME">%2$s</xliff:g> για πλήρη φόρτιση"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Η φόρτιση βελτιστοποιήθηκε"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Η φόρτιση βελτιστοποιήθηκε"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Άγνωστο"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Φόρτιση"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ταχεία φόρτιση"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Η δημιουργία νέου χρήστη απέτυχε"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Αποτυχία δημιουργίας νέου επισκέπτη"</string>
<string name="user_nickname" msgid="262624187455825083">"Ψευδώνυμο"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Το όνομα και η εικόνα σας θα είναι ορατά σε όλους τους χρήστες που χρησιμοποιούν αυτή τη συσκευή."</string>
<string name="user_add_user" msgid="7876449291500212468">"Προσθήκη χρήστη"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Προσθήκη επισκέπτη"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Κατάργηση επισκέπτη"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index b140131d5c94..b2c5718c999f 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> left until full"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> left until full"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging optimised"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging optimised"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Unknown"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Charging"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charging rapidly"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Failed to create a new user"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Failed to create a new guest"</string>
<string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Your name and picture will be visible to anyone that uses this device."</string>
<string name="user_add_user" msgid="7876449291500212468">"Add user"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index 6a118876e75e..21fff5e1e3be 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -478,7 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> left until full"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> left until full"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging optimized"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging optimized"</string>
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Unknown"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Charging"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charging rapidly"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index b140131d5c94..b2c5718c999f 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> left until full"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> left until full"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging optimised"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging optimised"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Unknown"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Charging"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charging rapidly"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Failed to create a new user"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Failed to create a new guest"</string>
<string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Your name and picture will be visible to anyone that uses this device."</string>
<string name="user_add_user" msgid="7876449291500212468">"Add user"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index b140131d5c94..b2c5718c999f 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> left until full"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> left until full"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging optimised"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging optimised"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Unknown"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Charging"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charging rapidly"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Failed to create a new user"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Failed to create a new guest"</string>
<string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Your name and picture will be visible to anyone that uses this device."</string>
<string name="user_add_user" msgid="7876449291500212468">"Add user"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index f4bdc120bc49..25e2779c4f5c 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -478,7 +478,7 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‎‏‎‎‎‎‎‎‏‎‏‎‏‎‏‏‎‏‏‏‎‎‏‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‎‏‎‎‎‎‏‎‎‎‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="TIME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ left until full‎‏‎‎‏‎"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‎‏‎‎‎‎‎‎‎‎‏‏‎‏‏‏‏‎‏‏‏‎‏‎‏‏‏‎‏‎‎‎‎‎‏‎‏‏‏‏‏‎‎‏‏‏‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="LEVEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎ - ‎‏‎‎‏‏‎<xliff:g id="TIME">%2$s</xliff:g>‎‏‎‎‏‏‏‎ left until full‎‏‎‎‏‎"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‎‎‏‏‏‏‏‎‎‎‎‏‏‏‎‎‏‎‏‎‎‎‏‎‏‏‏‎‎‎‎‏‏‎‏‎‏‎‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‏‏‎<xliff:g id="LEVEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎ - Charging optimized‎‏‎‎‏‎"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‎‎‏‏‎‏‎‎‏‎‎‏‏‏‏‎‏‏‏‏‎‏‎‎‎‎‎‏‏‎‎‎‏‏‎‎‎‏‏‎‏‎‏‏‏‏‏‏‎‏‏‏‎‎‏‎‎‎‎‎‎‏‎‎‏‏‎<xliff:g id="LEVEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎ - Charging optimized‎‏‎‎‏‎"</string>
+ <string name="power_charging_future_paused" msgid="1809543660923642799">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‎‎‏‎‎‎‏‏‏‎‎‏‏‎‎‏‎‏‎‎‏‎‏‏‏‏‏‎‏‎‎‏‎‏‎‏‏‏‎‏‎‎‏‏‏‏‎‎‎‏‏‏‎‏‎‏‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="LEVEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎ - Charging‎‏‎‎‏‎"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‎‏‏‏‎‏‏‏‎‏‎‎‏‎‏‏‎‎‏‎‏‎‏‏‏‎‏‎‏‎‎‎‎‏‎‏‎‏‏‏‎‏‏‏‏‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎Unknown‎‏‎‎‏‎"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‏‏‎‏‏‎‎‏‎‏‎‏‏‏‎‏‏‏‎‎‏‎‎‎‏‏‏‎‎‎‏‎‏‏‎‏‎‏‎‎‎‎‎‏‏‎‎‏‏‎‏‏‎‎‏‏‎‏‎Charging‎‏‎‎‏‎"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‎‎‏‏‏‏‎‎‏‏‏‏‏‎‎‎‏‎‎‎‎‏‏‏‎‏‏‏‏‏‏‎‏‎‎‎‏‎‎‏‎‏‏‎‏‏‎‎‎‎‏‎‏‎Charging rapidly‎‏‎‎‏‎"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index fa83538a144d..d0ba34c4c8f4 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> para completar"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carga optimizada"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carga optimizada"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Desconocido"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Cargando"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Cargando rápidamente"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"No se pudo crear el usuario nuevo"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"No se pudo crear un nuevo invitado"</string>
<string name="user_nickname" msgid="262624187455825083">"Sobrenombre"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Todas las personas que usen este dispositivo podrán ver tu nombre y foto."</string>
<string name="user_add_user" msgid="7876449291500212468">"Agregar usuario"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Agregar invitado"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Quitar invitado"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 2248d6509b8b..77049e0c6837 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> hasta la carga completa"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> hasta la carga completa"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carga optimizada"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carga optimizada"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Desconocido"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Cargando"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carga rápida"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"No se ha podido crear el usuario"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"No se ha podido crear un nuevo invitado"</string>
<string name="user_nickname" msgid="262624187455825083">"Apodo"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Cualquiera que use este dispositivo podrá ver tu nombre y tu imagen"</string>
<string name="user_add_user" msgid="7876449291500212468">"Añadir usuario"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Añadir invitado"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Quitar invitado"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index d7f6c5ac5bbc..6a3c208efa59 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Täislaadimiseks kulub <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – täislaadimiseks kulub <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – laadimine on optimeeritud"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – laadimine on optimeeritud"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Tundmatu"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Laadimine"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Kiirlaadimine"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Uue kasutaja loomine ebaõnnestus"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Uue külalise loomine ei õnnestunud"</string>
<string name="user_nickname" msgid="262624187455825083">"Hüüdnimi"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Teie nimi ja pilt on nähtav kõigile selle seadme kasutajatele."</string>
<string name="user_add_user" msgid="7876449291500212468">"Lisa kasutaja"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Lisa külaline"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Eemalda külaline"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 12803c5f8db1..64e372ac182a 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> guztiz kargatu arte"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> guztiz kargatu arte"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Kargatzeko modu optimizatua"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Kargatzeko modu optimizatua"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Ezezaguna"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Kargatzen"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Bizkor kargatzen"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Ezin izan da sortu erabiltzailea"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Ezin izan da sortu beste gonbidatu bat"</string>
<string name="user_nickname" msgid="262624187455825083">"Goitizena"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Gailu hau darabilen edonork ikusi ahal izango ditu zure izena eta argazkia."</string>
<string name="user_add_user" msgid="7876449291500212468">"Gehitu erabiltzaile bat"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Gehitu gonbidatu bat"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Kendu gonbidatua"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 86193bdb7e55..01d4618a319e 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> تا شارژ کامل باقی مانده است"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> تا شارژ کامل باقی مانده است"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - شارژ بهینه شده است"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - شارژ بهینه شده است"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"ناشناس"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"در حال شارژ شدن"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"درحال شارژ شدن سریع"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"کاربر جدید ایجاد نشد"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"مهمان جدید ایجاد نشد"</string>
<string name="user_nickname" msgid="262624187455825083">"نام مستعار"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"افرادی که از این دستگاه استفاده می‌کنند می‌توانند نام و عکس شما را ببینند."</string>
<string name="user_add_user" msgid="7876449291500212468">"افزودن کاربر"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"افزودن مهمان"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"حذف مهمان"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 320357c2781c..529bed536f1f 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -192,7 +192,7 @@
<string name="tts_play_example_title" msgid="1599468547216481684">"Kuuntele esimerkki"</string>
<string name="tts_play_example_summary" msgid="634044730710636383">"Toista lyhyt esittely puhesynteesistä"</string>
<string name="tts_install_data_title" msgid="1829942496472751703">"Asenna äänidata"</string>
- <string name="tts_install_data_summary" msgid="3608874324992243851">"Asenna puhesynteesiin tarvittavat äänitiedot"</string>
+ <string name="tts_install_data_summary" msgid="3608874324992243851">"Asenna puhesynteesiin tarvittava äänidata"</string>
<string name="tts_engine_security_warning" msgid="3372432853837988146">"Tämä puhesynteesimoottori saattaa kerätä kaiken puhutun tekstin, mukaan lukien henkilökohtaiset tiedot kuten salasanat ja luottokorttinumerot. Se on lähtöisin moottorista <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. Haluatko ottaa tämän puhesynteesimoottorin käyttöön?"</string>
<string name="tts_engine_network_required" msgid="8722087649733906851">"Tämä kieli vaatii verkkoyhteyden, jotta tekstistä puheeksi muuntaminen toimii."</string>
<string name="tts_default_sample_string" msgid="6388016028292967973">"Tämä on esimerkki puhesynteesistä."</string>
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> kunnes täynnä"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> kunnes täynnä"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Lataus optimoitu"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – Lataus optimoitu"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Tuntematon"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Ladataan"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Nopea lataus"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Uuden käyttäjän luominen epäonnistui"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Uutta vierasta ei voitu luoda"</string>
<string name="user_nickname" msgid="262624187455825083">"Lempinimi"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Nimesi ja kuvasi ovat näkyvillä kaikille tätä laitetta käyttäville henkilöille."</string>
<string name="user_add_user" msgid="7876449291500212468">"Lisää käyttäjä"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Lisää vieras"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Poista vieras"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index e0285b54fe09..ea2383425dc9 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> jusqu\'à la recharge complète"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> (<xliff:g id="TIME">%2$s</xliff:g> jusqu\'à la recharge complète)"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Recharge optimisée"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Recharge optimisée"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Inconnu"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Charge en cours…"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Recharge rapide"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Impossible de créer un utilisateur"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Impossible de créer un nouvel invité"</string>
<string name="user_nickname" msgid="262624187455825083">"Pseudo"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Votre nom et votre photo seront visibles à toute personne utilisant cet appareil."</string>
<string name="user_add_user" msgid="7876449291500212468">"Ajouter un utilisateur"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Ajouter un invité"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Supprimer l\'invité"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 63b47472dd42..dbf107acf46e 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Chargée à 100 %% dans <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - chargée à 100 %% dans <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Recharge optimisée"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Recharge optimisée"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Inconnu"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Batterie en charge"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charge rapide"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Échec de la création d\'un utilisateur"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Impossible de créer un profil invité"</string>
<string name="user_nickname" msgid="262624187455825083">"Pseudo"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Votre nom et votre photo seront visibles par quiconque utilise cet appareil."</string>
<string name="user_add_user" msgid="7876449291500212468">"Ajouter un utilisateur"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Ajouter un invité"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Supprimer l\'invité"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 7088dc6bc6e4..233c9d4d6a1b 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> para completar a carga"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> (<xliff:g id="TIME">%2$s</xliff:g> para completar a carga)"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> (carga optimizada)"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> (carga optimizada)"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Descoñecido"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Cargando"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Cargando rapidamente"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Non se puido crear un novo usuario"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Produciuse un erro ao crear o convidado"</string>
<string name="user_nickname" msgid="262624187455825083">"Alcume"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Calquera que use este dispositivo poderá ver o teu nome e imaxe."</string>
<string name="user_add_user" msgid="7876449291500212468">"Engadir usuario"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Engadir convidado"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Quitar convidado"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 77fc573fc164..762ed28a4317 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"પૂર્ણ ચાર્જ થવામાં <xliff:g id="TIME">%1$s</xliff:g> બાકી છે"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - પૂર્ણ ચાર્જ થવામાં <xliff:g id="TIME">%2$s</xliff:g> બાકી છે"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ચાર્જિંગ ઑપ્ટિમાઇઝ કરવામાં આવ્યું છે"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - ચાર્જિંગ ઑપ્ટિમાઇઝ કરવામાં આવ્યું છે"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"અજાણ્યું"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ચાર્જ થઈ રહ્યું છે"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ઝડપથી ચાર્જ થાય છે"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"નવો વપરાશકર્તા બનાવવામાં નિષ્ફળ"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"નવી અતિથિ બનાવવામાં નિષ્ફળ રહ્યાં"</string>
<string name="user_nickname" msgid="262624187455825083">"ઉપનામ"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"તમારું નામ અને ફોટો આ ડિવાઇસનો ઉપયોગ કરનાર કોઈપણ વ્યક્તિને દેખાશે."</string>
<string name="user_add_user" msgid="7876449291500212468">"વપરાશકર્તા ઉમેરો"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"અતિથિ ઉમેરો"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"અતિથિને કાઢી નાખો"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 4b1da1d14ada..6a0e3e0f706f 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> में बैटरी पूरी चार्ज हो जाएगी"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> में बैटरी पूरी चार्ज हो जाएगी"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्जिंग को ऑप्टिमाइज़ किया गया"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्जिंग को ऑप्टिमाइज़ किया गया"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"अज्ञात"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"चार्ज हो रही है"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"तेज़ चार्ज हो रही है"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"नया उपयोगकर्ता जोड़ा नहीं जा सका"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"नया मेहमान खाता नहीं बनाया जा सका"</string>
<string name="user_nickname" msgid="262624187455825083">"प्रचलित नाम"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"इस डिवाइस का इस्तेमाल करने वाले लोगों को आपका नाम और तस्वीर दिखेगी."</string>
<string name="user_add_user" msgid="7876449291500212468">"उपयोगकर्ता जोड़ें"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"मेहमान जोड़ें"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"मेहमान को हटाएं"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 6c7e4144bf4b..f019936ed9c4 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> do napunjenosti"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napunjenosti"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje se optimizira"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje se optimizira"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Nepoznato"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Punjenje"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo punjenje"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Izrada novog korisnika nije uspjela"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Izrada novog gosta nije uspjela"</string>
<string name="user_nickname" msgid="262624187455825083">"Nadimak"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Vaše ime i slika bit će vidljivi svima koji upotrebljavaju taj uređaj."</string>
<string name="user_add_user" msgid="7876449291500212468">"Dodajte korisnika"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Dodajte gosta"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Ukloni gosta"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index cf5bed2a6104..d3d2960b22b9 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> a teljes töltöttségig"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> a teljes töltöttségig"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Optimalizált töltés"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – Optimalizált töltés"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Ismeretlen"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Töltés"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Gyorstöltés"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Az új felhasználó létrehozása sikertelen"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Az új vendég létrehozása nem sikerült"</string>
<string name="user_nickname" msgid="262624187455825083">"Becenév"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Neve és képe mindenki számára látható lesz, aki ezt az eszközt használja."</string>
<string name="user_add_user" msgid="7876449291500212468">"Felhasználó hozzáadása"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Vendég hozzáadása"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Vendég munkamenet eltávolítása"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index ad2ccaee3add..4c14ae12fbb1 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> մինչև լրիվ լիցքավորումը"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> մինչև լրիվ լիցքավորումը"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Լիցքավորումն օպտիմալացված է"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – Լիցքավորումն օպտիմալացված է"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Անհայտ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Լիցքավորում"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Արագ լիցքավորում"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Չհաջողվեց ստեղծել նոր օգտատեր"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Չհաջողվեց նոր հյուր ստեղծել"</string>
<string name="user_nickname" msgid="262624187455825083">"Կեղծանուն"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Ձեր անունն ու նկարը տեսանելի կլինեն այս սարքն օգտագործող բոլոր մարդկանց։"</string>
<string name="user_add_user" msgid="7876449291500212468">"Ավելացնել օգտատեր"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Ավելացնել հյուր"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Հեռացնել հյուրին"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index ce85a0d28df1..1379ecfc99b7 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> lagi sampai penuh"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> lagi sampai penuh"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Pengisian daya dioptimalkan"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Pengisian daya dioptimalkan"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Tidak diketahui"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Mengisi daya"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Mengisi daya cepat"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Gagal membuat pengguna baru"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Gagal membuat tamu baru"</string>
<string name="user_nickname" msgid="262624187455825083">"Nama panggilan"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Nama dan foto Anda akan dapat dilihat oleh siapa saja yang menggunakan perangkat ini."</string>
<string name="user_add_user" msgid="7876449291500212468">"Tambahkan pengguna"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Tambahkan tamu"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Hapus tamu"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 30dba227163f..e48d063d9d3c 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> fram að fullri hleðslu"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> fram að fullri hleðslu"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Hleðsla fínstillt"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – Hleðsla fínstillt"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Óþekkt"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Í hleðslu"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hröð hleðsla"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Ekki tókst að stofna nýjan notanda"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Ekki tókst að búa til nýjan gest"</string>
<string name="user_nickname" msgid="262624187455825083">"Gælunafn"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Allir sem nota þetta tæki geta séð nafnið þitt og myndina."</string>
<string name="user_add_user" msgid="7876449291500212468">"Bæta notanda við"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Bæta gesti við"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Fjarlægja gest"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 5fe9e4cda45a..b33551440684 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> alla ricarica completa"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> alla ricarica completa"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Ricarica ottimizzata"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Ricarica ottimizzata"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Sconosciuta"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"In carica"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ricarica veloce"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Creazione nuovo utente non riuscita"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Impossibile creare un nuovo ospite"</string>
<string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Il tuo nome e la tua foto saranno visibili a chiunque usi questo dispositivo."</string>
<string name="user_add_user" msgid="7876449291500212468">"Aggiungi utente"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Aggiungi ospite"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Rimuovi ospite"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 92e0516841ae..a559cc616105 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"הזמן הנותר לטעינה מלאה: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – הזמן הנותר לטעינה מלאה: <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – הטעינה עברה אופטימיזציה"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – הטעינה עברה אופטימיזציה"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"לא ידוע"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"בטעינה"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"הסוללה נטענת מהר"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"לא ניתן היה ליצור משתמש חדש"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"יצירת אורח חדש נכשלה"</string>
<string name="user_nickname" msgid="262624187455825083">"כינוי"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"השם והתמונה שלך יהיו גלויים לכל מי שמשתמש במכשיר הזה."</string>
<string name="user_add_user" msgid="7876449291500212468">"הוספת משתמש"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"הוספת אורח"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"הסרת אורח"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 5c7a9dd48f4e..a7be818b3f85 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"完了まであと <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - 完了まであと <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充電が最適化されています"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充電が最適化されています"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"不明"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"充電中"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"急速充電中"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"新しいユーザーを作成できませんでした"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"新しいゲストを作成できませんでした"</string>
<string name="user_nickname" msgid="262624187455825083">"ニックネーム"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"このデバイスを使用するすべてのユーザーに、あなたの名前と写真が表示されます。"</string>
<string name="user_add_user" msgid="7876449291500212468">"ユーザーを追加"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"ゲストを追加"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"ゲストを削除"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index c70787f2d233..eb06df31e676 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"სრულ დატენვამდე დარჩენილია <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> — სრულ დატენვამდე დარჩენილია <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - დატენვა ოპტიმიზირებულია"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - დატენვა ოპტიმიზირებულია"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"უცნობი"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"იტენება"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"სწრაფად იტენება"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"ახალი მომხმარებლის შექმნა ვერ მოხერხდა"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"ახალი სტუმრის შექმნა ვერ მოხერხდა"</string>
<string name="user_nickname" msgid="262624187455825083">"მეტსახელი"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"თქვენი სახელი და სურათი ხილვადი იქნება ყველასთვის, ვინც ამ მოწყობილობას იყენებს."</string>
<string name="user_add_user" msgid="7876449291500212468">"მომხმარებლის დამატება"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"სტუმრის დამატება"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"სტუმრის ამოშლა"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 9eab6b0052ec..86da0a4c0179 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Толық зарядталғанға дейін <xliff:g id="TIME">%1$s</xliff:g> қалды."</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g>: толық зарядталуға <xliff:g id="TIME">%2$s</xliff:g> қалды"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарядтау оңтайландырылды"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарядтау оңтайландырылды."</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Белгісіз"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Зарядталуда"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Жылдам зарядтау"</string>
@@ -552,8 +553,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"Өшірілгенге дейін"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Дәл қазір"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"Осы телефон"</string>
- <!-- no translation found for media_transfer_this_device_name_tablet (2975593806278422086) -->
- <skip />
+ <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"Осы планшет"</string>
<!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
<skip />
<string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Қондыру динамигі"</string>
@@ -620,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Жаңа пайдаланушы жасалмады."</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Жаңа қонақ профилі жасалмады."</string>
<string name="user_nickname" msgid="262624187455825083">"Лақап ат"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Атыңыз бен суретіңіз осы құрылғыны пайдаланатын барлық адамға көрінеді."</string>
<string name="user_add_user" msgid="7876449291500212468">"Пайда­ланушы қосу"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Қонақ қосу"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Қонақты жою"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index de00d948e01b..848e9049df33 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> ទៀតទើបពេញ"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - នៅសល់ <xliff:g id="TIME">%2$s</xliff:g> ទៀតទើបពេញ"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - បានបង្កើនប្រសិទ្ធភាពនៃការសាក"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - បានបង្កើនប្រសិទ្ធភាពនៃការសាក"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"មិន​ស្គាល់"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"កំពុងសាក​ថ្ម"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"កំពុងសាកថ្មយ៉ាងឆាប់រហ័ស"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"មិន​អាច​បង្កើត​អ្នកប្រើប្រាស់ថ្មី​បានទេ"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"មិនអាចបង្កើតភ្ញៀវថ្មីបានទេ"</string>
<string name="user_nickname" msgid="262624187455825083">"ឈ្មោះ​ហៅក្រៅ"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"គ្រប់គ្នាដែលប្រើឧបករណ៍នេះនឹងអាចមើលឃើញឈ្មោះ និងរូបភាពរបស់អ្នក។"</string>
<string name="user_add_user" msgid="7876449291500212468">"បញ្ចូល​អ្នក​ប្រើប្រាស់"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"បញ្ចូល​ភ្ញៀវ"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"ដកភ្ញៀវចេញ"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 93432b1c2129..d13019c28fe5 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> - ಸಮಯದಲ್ಲಿ ಪೂರ್ತಿ ಚಾರ್ಜ್ ಆಗುತ್ತದೆ"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ಸಮಯದಲ್ಲಿ ಪೂರ್ತಿ ಚಾರ್ಜ್ ಆಗುತ್ತದೆ"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಚಾರ್ಜಿಂಗ್ ಅನ್ನು ಆಪ್ಟಿಮೈಸ್ ಮಾಡಲಾಗಿದೆ"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಚಾರ್ಜಿಂಗ್ ಅನ್ನು ಆಪ್ಟಿಮೈಸ್ ಮಾಡಲಾಗಿದೆ"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"ಅಪರಿಚಿತ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ವೇಗದ ಚಾರ್ಜಿಂಗ್"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"ಹೊಸ ಬಳಕೆದಾರರನ್ನು ರಚಿಸಲು ವಿಫಲವಾಗಿದೆ"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"ಹೊಸ ಅತಿಥಿಯನ್ನು ರಚಿಸಲು ವಿಫಲವಾಗಿದೆ"</string>
<string name="user_nickname" msgid="262624187455825083">"ಅಡ್ಡ ಹೆಸರು"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"ಈ ಸಾಧನವನ್ನು ಬಳಸುವ ಯಾರಿಗಾದರೂ ನಿಮ್ಮ ಹೆಸರು ಮತ್ತು ಚಿತ್ರವು ಗೋಚರಿಸುತ್ತದೆ."</string>
<string name="user_add_user" msgid="7876449291500212468">"ಬಳಕೆದಾರರನ್ನು ಸೇರಿಸಿ"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"ಅತಿಥಿಯನ್ನು ಸೇರಿಸಿ"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"ಅತಿಥಿಯನ್ನು ತೆಗೆದುಹಾಕಿ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index c30e11252d64..3c8ad01f0f02 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> 후 충전 완료"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> 후 충전 완료"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - 충전 최적화됨"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - 충전 최적화됨"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"알 수 없음"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"충전 중"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"고속 충전 중"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"새 사용자를 만들지 못함"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"새 게스트 생성 실패"</string>
<string name="user_nickname" msgid="262624187455825083">"닉네임"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"이 기기를 사용하는 모든 사람에게 내 이름과 사진이 공개됩니다."</string>
<string name="user_add_user" msgid="7876449291500212468">"사용자 추가"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"게스트 추가"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"게스트 삭제"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 45773b932c0d..cec61bbdf764 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> кийин толук кубатталат"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> кийин толук кубатталат"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> — Кубаттоо жакшыртылды"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> — Кубаттоо жакшыртылды"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Белгисиз"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Кубатталууда"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ыкчам кубатталууда"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Жаңы колдонуучу түзүлбөй калды"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Жаңы конок түзүлгөн жок"</string>
<string name="user_nickname" msgid="262624187455825083">"Ылакап аты"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Аты-жөнүңүз менен сүрөтүңүз ушул түзмөктү колдонгондордун баарына көрүнөт."</string>
<string name="user_add_user" msgid="7876449291500212468">"Колдонуучу кошуу"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Конок кошуу"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Конокту өчүрүү"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 5dedbdec374d..420925920982 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"ຍັງເຫຼືອອີກ <xliff:g id="TIME">%1$s</xliff:g> ຈຶ່ງຈະສາກເຕັມ"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"ຍັງເຫຼືອອີກ <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ຈຶ່ງຈະສາກເຕັມ"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ການສາກຖືກປັບໃຫ້ເໝາະສົມແລ້ວ"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - ການສາກຖືກປັບໃຫ້ເໝາະສົມແລ້ວ"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"ບໍ່ຮູ້ຈັກ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ກຳລັງສາກໄຟ"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ກຳລັງສາກໄຟດ່ວນ"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"ສ້າງຜູ້ໃຊ້ໃໝ່ບໍ່ສຳເລັດ"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"ສ້າງແຂກໃໝ່ບໍ່ສຳເລັດ"</string>
<string name="user_nickname" msgid="262624187455825083">"ຊື່ຫຼິ້ນ"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"ຊື່ ແລະ ຮູບຂອງທ່ານຈະເຫັນໄດ້ໂດຍທຸກຄົນທີ່ໃຊ້ອຸປະກອນນີ້."</string>
<string name="user_add_user" msgid="7876449291500212468">"ເພີ່ມຜູ້ໃຊ້"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"ເພີ່ມແຂກ"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"ລຶບແຂກອອກ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 9cc69b0d4473..cb0069d66e42 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Liko <xliff:g id="TIME">%1$s</xliff:g>, kol bus visiškai įkrauta"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – liko <xliff:g id="TIME">%2$s</xliff:g>, kol bus visiškai įkrauta"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – įkrovimas optimizuotas"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – įkrovimas optimizuotas"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Nežinomas"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Kraunasi..."</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Greitai įkraunama"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Nepavyko sukurti naujo naudotojo"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Nepavyko sukurti naujo gesto"</string>
<string name="user_nickname" msgid="262624187455825083">"Slapyvardis"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Jūsų vardas ir nuotrauka bus rodomi visiems šio įrenginio naudotojams."</string>
<string name="user_add_user" msgid="7876449291500212468">"Pridėti naudotoją"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Pridėti svečią"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Pašalinti svečią"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 2bf1bee19392..db566881f016 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> līdz pilnai uzlādei"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="TIME">%2$s</xliff:g> līdz pilnai uzlādei"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> — uzlāde optimizēta"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> — uzlāde optimizēta"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Nezināms"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Uzlāde"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Notiek ātrā uzlāde"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Neizdevās izveidot jaunu lietotāju"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Neizdevās izveidot jaunu viesa profilu"</string>
<string name="user_nickname" msgid="262624187455825083">"Segvārds"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Jūsu vārds un attēls būs redzams jebkuram šīs ierīces lietotājam."</string>
<string name="user_add_user" msgid="7876449291500212468">"Pievienot lietotāju"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Pievienot viesi"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Noņemt viesi"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 3f1fa533d648..6738e2068ae1 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> до полна батерија"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полна батерија"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Полнењето е оптимизирано"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Полнењето е оптимизирано"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Непознато"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Се полни"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Брзо полнење"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Не успеа да создаде нов корисник"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Не успеа создавањето нов гостин"</string>
<string name="user_nickname" msgid="262624187455825083">"Прекар"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Вашите име и слика ќе бидат видливи за секој што го користи уредов."</string>
<string name="user_add_user" msgid="7876449291500212468">"Додајте корисник"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Додајте гостин"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Отстрани гостин"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 396547b18a2e..a5c38bc22d1f 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"പൂർണ്ണമാകാൻ <xliff:g id="TIME">%1$s</xliff:g> ശേഷിക്കുന്നു"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - പൂർണ്ണമാകാൻ <xliff:g id="TIME">%2$s</xliff:g> ശേഷിക്കുന്നു"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ചാർജിംഗ് ഒപ്റ്റിമൈസ് ചെയ്തു"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - ചാർജിംഗ് ഒപ്റ്റിമൈസ് ചെയ്തു"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"അജ്ഞാതം"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ചാർജ് ചെയ്യുന്നു"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"അതിവേഗ ചാർജിംഗ്"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"പുതിയ ഉപയോക്താവിനെ സൃഷ്‌ടിക്കാനായില്ല"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"പുതിയ അതിഥിയെ സൃഷ്‌ടിക്കാനായില്ല"</string>
<string name="user_nickname" msgid="262624187455825083">"വിളിപ്പേര്"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"നിങ്ങളുടെ പേരും ചിത്രവും ഈ ഉപകരണം ഉപയോഗിക്കുന്ന എല്ലാവർക്കും ദൃശ്യമാകും."</string>
<string name="user_add_user" msgid="7876449291500212468">"ഉപയോക്താവിനെ ചേർക്കുക"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"അതിഥിയെ ചേർക്കുക"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"അതിഥിയെ നീക്കം ചെയ്യുക"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index f1f3a90666b2..c03c89f68b18 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Дүүрэх хүртэл <xliff:g id="TIME">%1$s</xliff:g> үлдсэн"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - дүүрэх хүртэл <xliff:g id="TIME">%2$s</xliff:g> үлдсэн"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Цэнэглэх явцыг оновчилсон"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Цэнэглэх явцыг оновчилсон"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Тодорхойгүй"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Цэнэглэж байна"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Хурдан цэнэглэж байна"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Шинэ хэрэглэгч үүсгэж чадсангүй"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Шинэ зочин үүсгэж чадсангүй"</string>
<string name="user_nickname" msgid="262624187455825083">"Хоч"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Таны нэр болон зураг энэ төхөөрөмжийг ашигладаг дурын хүнд харагдана."</string>
<string name="user_add_user" msgid="7876449291500212468">"Хэрэглэгч нэмэх"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Зочин нэмэх"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Зочин хасах"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 562004fd396b..bbe782da8f85 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -52,7 +52,7 @@
<string name="wifi_remembered" msgid="3266709779723179188">"सेव्ह केले"</string>
<string name="wifi_disconnected" msgid="7054450256284661757">"डिस्कनेक्ट केले"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"अक्षम"</string>
- <string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP कॉंफिगरेशन अयशस्वी"</string>
+ <string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP कॉन्फिगरेशन अयशस्वी"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"प्रमाणीकरण समस्या"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"कनेक्ट करू शकत नाही"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"\'<xliff:g id="AP_NAME">%1$s</xliff:g>\'शी कनेक्‍ट करू शकत नाही"</string>
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"पूर्ण चार्ज होण्यासाठी <xliff:g id="TIME">%1$s</xliff:g> शिल्लक आहेत"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - पूर्ण चार्ज होण्यासाठी <xliff:g id="TIME">%2$s</xliff:g> शिल्लक आहे"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्जिंग ऑप्टिमाइझ केले"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्जिंग ऑप्टिमाइझ केले"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"अज्ञात"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"चार्ज होत आहे"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"वेगाने चार्ज होत आहे"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"नवीन वापरकर्ता तयार करता आला नाही"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"नवीन अतिथी तयार करता आला नाही"</string>
<string name="user_nickname" msgid="262624187455825083">"टोपणनाव"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"हे डिव्हाइस वापरणाऱ्या कोणत्याही व्यक्तीला तुमचे नाव आणि फोटो दिसेल."</string>
<string name="user_add_user" msgid="7876449291500212468">"वापरकर्ता जोडा"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"अतिथी जोडा"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"अतिथी काढून टाका"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index 264ab3552b0e..3da4f08be11b 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> lagi sebelum penuh"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> lagi sebelum penuh"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Pengecasan dioptimumkan"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Pengecasan dioptimumkan"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Tidak diketahui"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Mengecas"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Mengecas dgn cepat"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Gagal membuat pengguna baharu"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Gagal membuat tetamu baharu"</string>
<string name="user_nickname" msgid="262624187455825083">"Nama panggilan"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Nama dan gambar anda akan kelihatan kepada sesiapa yang menggunakan peranti ini."</string>
<string name="user_add_user" msgid="7876449291500212468">"Tambah pengguna"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Tambah tetamu"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Alih keluar tetamu"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index d839a4544440..f4b669773aeb 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"အားပြည့်ရန် <xliff:g id="TIME">%1$s</xliff:g> လိုသည်"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - အားပြည့်ရန် <xliff:g id="TIME">%2$s</xliff:g> လိုသည်"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - အားသွင်းခြင်းကို အကောင်းဆုံးပြင်ဆင်ထားသည်"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - အားသွင်းခြင်းကို အကောင်းဆုံးပြင်ဆင်ထားသည်"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"မသိ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"အားသွင်းနေပါသည်"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"အမြန် အားသွင်းနေသည်"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"အသုံးပြုသူအသစ် ပြုလုပ်၍မရပါ"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"ဧည့်သည်သစ် ပြုလုပ်၍မရပါ"</string>
<string name="user_nickname" msgid="262624187455825083">"နာမည်ပြောင်"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"သင့်အမည်နှင့် ဓာတ်ပုံကို ဤစက်သုံးသူတိုင်း မြင်ရပါမည်။"</string>
<string name="user_add_user" msgid="7876449291500212468">"အသုံးပြုသူ ထည့်ရန်"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"ဧည့်သည် ထည့်ရန်"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"ဧည့်သည်ကို ဖယ်ရှားရန်"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index dc326f0e4414..fbdc9e33f576 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Fulladet om <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – Fulladet om <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Ladingen er optimalisert"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – Ladingen er optimalisert"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Ukjent"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Lader"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Lader raskt"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Kunne ikke opprette noen ny bruker"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Kunne ikke opprette en ny gjest"</string>
<string name="user_nickname" msgid="262624187455825083">"Kallenavn"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Navnet og bildet ditt blir synlige for alle som bruker denne enheten."</string>
<string name="user_add_user" msgid="7876449291500212468">"Legg til bruker"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Legg til gjest"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Fjern gjesten"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index d962cece54ce..245a31f9fcae 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"पूरा चार्ज हुन <xliff:g id="TIME">%1$s</xliff:g> लाग्ने छ"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - पूरा चार्ज हुन <xliff:g id="TIME">%2$s</xliff:g> लाग्ने छ"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्ज गर्ने प्रक्रिया अप्टिमाइज गरिएको छ"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्ज गर्ने प्रक्रिया अप्टिमाइज गरिएको छ"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"अज्ञात"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"चार्ज हुँदै छ"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"द्रुत गतिमा चार्ज गरिँदै छ"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"नयाँ प्रयोगकर्ता सिर्जना गर्न सकिएन"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"नयाँ अतिथि बनाउन सकिएन"</string>
<string name="user_nickname" msgid="262624187455825083">"उपनाम"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"यो डिभाइस प्रयोग गर्ने सबै जना मान्छे तपाईंको नाम र फोटो देख्ने छन्।"</string>
<string name="user_add_user" msgid="7876449291500212468">"प्रयोगकर्ता कनेक्ट गर्नुहोस्"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"अतिथि कनेक्ट गर्नुहोस्"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"गेस्ट मोडबाट बाहिर निस्कियोस्"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 40c343c8a21b..4eafb5740e05 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Vol over <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - vol over <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Opladen geoptimaliseerd"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Opladen geoptimaliseerd"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Onbekend"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Opladen"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Snel opladen"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Kan geen nieuwe gebruiker maken"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Kan geen nieuwe gast maken"</string>
<string name="user_nickname" msgid="262624187455825083">"Bijnaam"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Je naam en foto zijn zichtbaar voor iedereen die dit apparaat gebruikt."</string>
<string name="user_add_user" msgid="7876449291500212468">"Gebruiker toevoegen"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Gast toevoegen"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Gast verwijderen"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 133e5c752657..5a8bed43faf7 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"ପୂର୍ଣ୍ଣ ହେବାକୁ ଆଉ <xliff:g id="TIME">%1$s</xliff:g> ବାକି ଅଛି"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - ପୂର୍ଣ୍ଣ ହେବାକୁ ଆଉ <xliff:g id="TIME">%2$s</xliff:g> ବାକି ଅଛି"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ଚାର୍ଜିଂକୁ ଅପ୍ଟିମାଇଜ କରାଯାଇଛି"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - ଚାର୍ଜିଂକୁ ଅପ୍ଟିମାଇଜ କରାଯାଇଛି"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"ଅଜ୍ଞାତ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ଚାର୍ଜ ହେଉଛି"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ଶୀଘ୍ର ଚାର୍ଜ ହେଉଛି"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"ନୂଆ ଉପଯୋଗକର୍ତ୍ତା ତିଆରି କରିବାକୁ ବିଫଳ ହେଲା"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"ଜଣେ ନୂଆ ଅତିଥି ତିଆରି କରିବାରେ ବିଫଳ ହୋଇଛି"</string>
<string name="user_nickname" msgid="262624187455825083">"ଡାକନାମ"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"ଏହି ଡିଭାଇସ ବ୍ୟବହାର କରୁଥିବା ଯେ କୌଣସି ବ୍ୟକ୍ତିଙ୍କୁ ଆପଣଙ୍କ ନାମ ଏବଂ ଛବି ଦେଖାଯିବ।"</string>
<string name="user_add_user" msgid="7876449291500212468">"ୟୁଜର ଯୋଗ କରନ୍ତୁ"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"ଅତିଥି ଯୋଗ କରନ୍ତୁ"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"ଅତିଥିଙ୍କୁ କାଢ଼ି ଦିଅନ୍ତୁ"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 160a43e10c4d..df1fea17f645 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"ਬੈਟਰੀ ਪੂਰੀ ਚਾਰਜ ਹੋਣ ਵਿੱਚ <xliff:g id="TIME">%1$s</xliff:g> ਬਾਕੀ"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਬੈਟਰੀ ਪੂਰੀ ਚਾਰਜ ਹੋਣ ਵਿੱਚ <xliff:g id="TIME">%2$s</xliff:g> ਬਾਕੀ"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਚਾਰਜਿੰਗ ਨੂੰ ਸੁਯੋਗ ਬਣਾਇਆ ਗਿਆ"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਚਾਰਜਿੰਗ ਨੂੰ ਸੁਯੋਗ ਬਣਾਇਆ ਗਿਆ"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"ਅਗਿਆਤ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ਤੇਜ਼ ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"ਨਵਾਂ ਵਰਤੋਂਕਾਰ ਬਣਾਉਣਾ ਅਸਫਲ ਰਿਹਾ"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"ਨਵਾਂ ਮਹਿਮਾਨ ਪ੍ਰੋਫਾਈਲ ਬਣਾਉਣਾ ਅਸਫਲ ਰਿਹਾ"</string>
<string name="user_nickname" msgid="262624187455825083">"ਉਪਨਾਮ"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"ਤੁਹਾਡਾ ਨਾਮ ਅਤੇ ਤਸਵੀਰ ਇਸ ਡੀਵਾਈਸ ਦੀ ਵਰਤੋਂ ਕਰਨ ਵਾਲੇ ਕਿਸੇ ਵੀ ਵਿਅਕਤੀ ਦਿਖਾਈ ਦੇਵੇਗੀ।"</string>
<string name="user_add_user" msgid="7876449291500212468">"ਵਰਤੋਂਕਾਰ ਨੂੰ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"ਮਹਿਮਾਨ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"ਮਹਿਮਾਨ ਹਟਾਓ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 6669c22b6533..e9f9da8e095b 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> do pełnego naładowania"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do pełnego naładowania"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – ładowanie zoptymalizowane"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – ładowanie zoptymalizowane"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Nieznane"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Ładowanie"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Szybkie ładowanie"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Nie udało się utworzyć nowego użytkownika"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Nie udało się utworzyć nowego gościa"</string>
<string name="user_nickname" msgid="262624187455825083">"Pseudonim"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Twoja nazwa użytkownika oraz zdjęcie będą widoczne dla każdego, kto korzysta z tego urządzenia."</string>
<string name="user_add_user" msgid="7876449291500212468">"Dodaj użytkownika"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gościa"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Usuń gościa"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 0b44fc9fabdc..4ee1cbd417ef 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> até a conclusão"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> até a conclusão"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carregamento otimizado"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carregamento otimizado"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Desconhecido"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Carregando"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregando rápido"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Falha ao criar um novo usuário"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Falha ao criar um novo convidado"</string>
<string name="user_nickname" msgid="262624187455825083">"Apelido"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Seu nome e foto vão ficar visíveis para qualquer pessoa que use este dispositivo."</string>
<string name="user_add_user" msgid="7876449291500212468">"Adicionar usuário"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Adicionar visitante"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Remover visitante"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index de0eb7d9f4e8..eae5cc965f7f 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> até à carga máxima"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> até à carga máxima"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g>: carregamento otimizado"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g>: carregamento otimizado"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Desconhecido"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"A carregar"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregamento rápido"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Falha ao criar um novo utilizador"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Falha ao criar um novo convidado"</string>
<string name="user_nickname" msgid="262624187455825083">"Alcunha"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"O seu nome e imagem vão ser visíveis para qualquer pessoa que use este dispositivo."</string>
<string name="user_add_user" msgid="7876449291500212468">"Adicionar utilizador"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Adicionar convidado"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Remover convidado"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 0b44fc9fabdc..4ee1cbd417ef 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> até a conclusão"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> até a conclusão"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carregamento otimizado"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carregamento otimizado"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Desconhecido"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Carregando"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregando rápido"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Falha ao criar um novo usuário"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Falha ao criar um novo convidado"</string>
<string name="user_nickname" msgid="262624187455825083">"Apelido"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Seu nome e foto vão ficar visíveis para qualquer pessoa que use este dispositivo."</string>
<string name="user_add_user" msgid="7876449291500212468">"Adicionar usuário"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Adicionar visitante"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Remover visitante"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 848a5f6760a8..91261d49f15e 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> până la finalizare"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> până la finalizare"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Încărcare optimizată"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – Încărcare optimizată"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Necunoscut"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Se încarcă"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Se încarcă rapid"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Nu s-a creat noul utilizator"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Nu s-a putut crea un invitat nou"</string>
<string name="user_nickname" msgid="262624187455825083">"Pseudonim"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Numele și fotografia ta vor fi vizibile pentru orice persoană care folosește acest dispozitiv."</string>
<string name="user_add_user" msgid="7876449291500212468">"Adaugă un utilizator"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Adaugă un invitat"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Șterge invitatul"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 166e7cbf3f74..87b2a938fa31 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> до полной зарядки"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полной зарядки"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарядка оптимизирована"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарядка оптимизирована"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Неизвестно"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Идет зарядка"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Быстрая зарядка"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Не удалось создать пользователя"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Не удалось создать гостя."</string>
<string name="user_nickname" msgid="262624187455825083">"Псевдоним"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Ваше имя и фото профиля будут видны всем пользователям этого устройства."</string>
<string name="user_add_user" msgid="7876449291500212468">"Добавить пользователя"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Добавить гостя"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Удалить аккаунт гостя"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index d87792db2fb3..72bd507a5175 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"සම්පූර්ණ වීමට <xliff:g id="TIME">%1$s</xliff:g>ක් ඉතිරියි"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - සම්පූර්ණ වීමට <xliff:g id="TIME">%2$s</xliff:g>ක් ඉතිරියි"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ආරෝපණය ප්‍රශස්ත කර ඇත"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - ආරෝපණය ප්‍රශස්ත කර ඇත"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"නොදනී"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ආරෝපණය වෙමින්"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ශීඝ්‍ර ආරෝපණය"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"නව පරිශීලකයෙකු තැනීමට අසමත් විය"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"නව අමුත්තකු තැනීම අසාර්ථක විය"</string>
<string name="user_nickname" msgid="262624187455825083">"අපනාමය"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"මෙම උපාංගය භාවිත කරන ඕනෑම කෙනෙකුට ඔබේ නම සහ පින්තූරය දැකිය හැකි වෙයි."</string>
<string name="user_add_user" msgid="7876449291500212468">"පරිශීලකයා එක් කරන්න"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"අමුත්තා එක් කරන්න"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"අමුත්තා ඉවත් කරන්න"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 33293f83cbf4..dd8be3a1d7a0 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> do úplného nabitia"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabitia"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Nabíjanie je optimalizované"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Nabíjanie je optimalizované"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Neznáme"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Nabíja sa"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Rýchle nabíjanie"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Nového použív. sa nepodarilo vytvoriť"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Nového hosťa sa nepodarilo vytvoriť"</string>
<string name="user_nickname" msgid="262624187455825083">"Prezývka"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Vaše meno a fotku uvidia všetci používatelia tohto zariadenia."</string>
<string name="user_add_user" msgid="7876449291500212468">"Pridať používateľa"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Pridať hosťa"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Odobrať hosťa"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 1ec258c59aaf..eefb119eca02 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Še <xliff:g id="TIME">%1$s</xliff:g> do napolnjenosti"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – še <xliff:g id="TIME">%2$s</xliff:g> do napolnjenosti"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – polnjenje je optimizirano"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – polnjenje je optimizirano"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Neznano"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Polnjenje"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hitro polnjenje"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Ustvarjanje novega uporabnika ni uspelo."</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Ustvarjanje novega gosta ni uspelo."</string>
<string name="user_nickname" msgid="262624187455825083">"Vzdevek"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Vaša ime in fotografija bosta vidna vsem uporabnikom te naprave."</string>
<string name="user_add_user" msgid="7876449291500212468">"Dodaj uporabnika"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gosta"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Odstrani gosta"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 9ebfd6079ce1..e847c515bb7e 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> derisa të mbushet"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> derisa të mbushet"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Karikimi u optimizua"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Karikimi u optimizua"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"I panjohur"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Po karikohet"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Karikim i shpejtë"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Krijimi i një përdoruesi të ri dështoi"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Profili i vizitorit të ri nuk u krijua"</string>
<string name="user_nickname" msgid="262624187455825083">"Pseudonimi"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Emri dhe fotografia jote do të jenë të dukshme për çdo person që e përdor këtë pajisje."</string>
<string name="user_add_user" msgid="7876449291500212468">"Shto përdorues"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Shto vizitor"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Hiq vizitorin"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 50e534132e51..ed0e9a6ea975 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> до краја пуњења"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до краја пуњења"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – пуњење је оптимизовано"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – пуњење је оптимизовано"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Непознато"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Пуни се"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Брзо се пуни"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Прављење новог корисника није успело"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Прављење новог госта није успело"</string>
<string name="user_nickname" msgid="262624187455825083">"Надимак"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Име и слика ће бити видљиви сваком ко користи овај уређај."</string>
<string name="user_add_user" msgid="7876449291500212468">"Додај корисника"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Додај госта"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Уклони госта"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 2d71c29922f6..b4de50408daa 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> kvar tills fulladdat"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> kvar tills fulladdat"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Laddningen har optimerats"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Laddningen har optimerats"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Okänd"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Laddar"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Laddas snabbt"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Det gick inte att skapa en ny användare"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Det gick inte att skapa en ny gäst"</string>
<string name="user_nickname" msgid="262624187455825083">"Smeknamn"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Ditt namn och din bild visas för alla som använder den här enheten."</string>
<string name="user_add_user" msgid="7876449291500212468">"Lägg till användare"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Lägg till gäst"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Ta bort gäst"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 8c064950dbf9..40725c92d6ea 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Zimesalia <xliff:g id="TIME">%1$s</xliff:g> ijae chaji"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> zimesalia ijae chaji"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Hali ya kuchaji imeboreshwa"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Hali ya kuchaji imeboreshwa"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Haijulikani"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Inachaji"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Inachaji kwa kasi"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Imeshindwa kuweka mtumiaji mpya"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Imeshindwa kuunda wasifu mpya wa mgeni"</string>
<string name="user_nickname" msgid="262624187455825083">"Jina wakilishi"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Mtu yeyote anayetumia kifaa hiki ataona jina na picha yako."</string>
<string name="user_add_user" msgid="7876449291500212468">"Ongeza mtumiaji"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Ongeza mgeni"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Ondoa mgeni"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index ceeb6035f2df..2fe243aee324 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"முழுவதும் சார்ஜாக <xliff:g id="TIME">%1$s</xliff:g> ஆகும்"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - முழுவதும் சார்ஜாக <xliff:g id="TIME">%2$s</xliff:g> ஆகும்"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - சார்ஜிங் மேம்படுத்தப்பட்டது"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - சார்ஜிங் மேம்படுத்தப்பட்டது"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"அறியப்படாத"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"சார்ஜ் ஆகிறது"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"வேகமாக சார்ஜாகிறது"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"புதிய பயனரை உருவாக்க முடியவில்லை"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"புதிய விருந்தினரை உருவாக்க முடியவில்லை"</string>
<string name="user_nickname" msgid="262624187455825083">"புனைப்பெயர்"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"இந்தச் சாதனத்தைப் பயன்படுத்தும் அனைவருக்கும் உங்கள் பெயரும் படமும் காட்டப்படும்."</string>
<string name="user_add_user" msgid="7876449291500212468">"பயனரைச் சேர்"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"கெஸ்ட்டைச் சேர்"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"கெஸ்ட்டை அகற்று"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index cb3f8d30bd3d..43f2b835bc44 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g>లో పూర్తిగా ఛార్జ్ అవుతుంది"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>లో పూర్తిగా ఛార్జ్ అవుతుంది"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ఛార్జింగ్ ఆప్టిమైజ్ చేయబడింది"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - ఛార్జింగ్ ఆప్టిమైజ్ చేయబడింది"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"తెలియదు"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ఛార్జ్ అవుతోంది"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"వేగవంతమైన ఛార్జింగ్"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"కొత్త యూజర్‌ను క్రియేట్ చేయడం విఫలమైంది"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"కొత్త అతిథిని క్రియేట్ చేయడం విఫలమైంది"</string>
<string name="user_nickname" msgid="262624187455825083">"మారుపేరు"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"ఈ పరికరాన్ని ఉపయోగించే ప్రతి ఒక్కరికి మీ పేరు, ఫోటో కనిపిస్తుంది."</string>
<string name="user_add_user" msgid="7876449291500212468">"యూజర్‌ను జోడించండి"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"గెస్ట్‌ను జోడించండి"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"గెస్ట్‌ను తీసివేయండి"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 9617c2a9bca8..8395a5185363 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"อีก <xliff:g id="TIME">%1$s</xliff:g>จึงจะเต็ม"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - อีก <xliff:g id="TIME">%2$s</xliff:g> จึงจะเต็ม"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - ปรับการชาร์จให้เหมาะสมแล้ว"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - ปรับการชาร์จให้เหมาะสมแล้ว"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"ไม่ทราบ"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"กำลังชาร์จ"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"กำลังชาร์จอย่างเร็ว"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"สร้างผู้ใช้ใหม่ไม่ได้"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"สร้างผู้เข้าร่วมใหม่ไม่สำเร็จ"</string>
<string name="user_nickname" msgid="262624187455825083">"ชื่อเล่น"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"ชื่อและภาพของคุณจะปรากฏแก่ทุกคนที่ใช้อุปกรณ์นี้"</string>
<string name="user_add_user" msgid="7876449291500212468">"เพิ่มผู้ใช้"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"เพิ่มผู้ใช้ชั่วคราว"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"นำผู้ใช้ชั่วคราวออก"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index a551e2a4289c..a337bd8a69f2 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> na lang bago mapuno"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> na lang bago mapuno"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Naka-optimize ang pag-charge"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Naka-optimize ang pag-charge"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Hindi Kilala"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Nagcha-charge"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Mabilis na charge"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Hindi nakagawa ng bagong user"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Hindi nakagawa ng bagong guest"</string>
<string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Makikita ng sinumang gumagamit ng device na ito ang iyong pangalan at larawan."</string>
<string name="user_add_user" msgid="7876449291500212468">"Magdagdag ng user"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Magdagdag ng bisita"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Alisin ang bisita"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 54675f14a88e..43e18edca594 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Tamamen şarj olmasına <xliff:g id="TIME">%1$s</xliff:g> kaldı"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - Tamamen şarj olmasına <xliff:g id="TIME">%2$s</xliff:g> kaldı"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Şarj işlemi optimize edildi"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Şarj işlemi optimize edildi"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Bilinmiyor"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Şarj oluyor"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hızlı şarj oluyor"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Yeni kullanıcı oluşturulamadı"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Yeni misafir oluşturulamadı"</string>
<string name="user_nickname" msgid="262624187455825083">"Takma ad"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Adınızı ve resminizi, bu cihazı kullanan herkes görebilir."</string>
<string name="user_add_user" msgid="7876449291500212468">"Kullanıcı ekle"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Misafir ekle"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Misafir oturumunu kaldır"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 355f566d0c9d..984bb7978294 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> до повного заряду"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до повного заряду"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – заряджання оптимізовано"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – заряджання оптимізовано"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Невідомо"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Заряджається"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Швидке заряджання"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Не вдалося створити користувача"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Не вдалося створити гостя"</string>
<string name="user_nickname" msgid="262624187455825083">"Псевдонім"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Ваше ім’я і зображення бачитимуть усі користувачі цього пристрою."</string>
<string name="user_add_user" msgid="7876449291500212468">"Додати ​користувача"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Додати гостя"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Видалити гостя"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 5c3a0676ac1b..bc8a46748d8b 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"مکمل چارج ہونے میں <xliff:g id="TIME">%1$s</xliff:g> باقی ہے"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"مکمل چارج ہونے میں <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> باقی ہے"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - چارجنگ کو بہتر بنایا گیا"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - چارجنگ کو بہتر بنایا گیا"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"نامعلوم"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"چارج ہو رہا ہے"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"تیزی سے چارج ہو رہا ہے"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"نیا صارف بنانے میں ناکام"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"نیا مہمان بنانے میں ناکام"</string>
<string name="user_nickname" msgid="262624187455825083">"عرفی نام"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"آپ کا نام اور تصویر ہر اس شخص کو نظر آئے گی جو اس آلہ کو استعمال کرتا ہے۔"</string>
<string name="user_add_user" msgid="7876449291500212468">"صارف کو شامل کریں"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"مہمان کو شامل کریں"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"مہمان کو ہٹائیں"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 950a24e0c43c..a655dc0f0304 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Toʻlishiga <xliff:g id="TIME">%1$s</xliff:g> qoldi"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – Toʻlishiga <xliff:g id="TIME">%2$s</xliff:g> qoldi"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Quvvatlash optimallashtirildi"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Quvvatlash optimallashtirildi"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Noma’lum"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Quvvat olmoqda"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Tezkor quvvat olmoqda"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Yangi foydalanuvchi yaratilmadi"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Yangi mehmon yaratilmadi"</string>
<string name="user_nickname" msgid="262624187455825083">"Nik"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Ismingiz va rasmingiz ushbu qurilmadan foydalanadigan har bir kishiga koʻrinadi."</string>
<string name="user_add_user" msgid="7876449291500212468">"Foydalanuvchi kiritish"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Mehmon kiritish"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Mehmonni olib tashlash"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 0328e27a7832..643f8ca85499 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> nữa là pin đầy"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> nữa là pin đầy"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Quá trình sạc được tối ưu hoá"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> – Quá trình sạc được tối ưu hoá"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Không xác định"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Đang sạc"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Đang sạc nhanh"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Không tạo được người dùng mới"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Không tạo được khách mới"</string>
<string name="user_nickname" msgid="262624187455825083">"Biệt hiệu"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Bất kỳ ai dùng thiết bị này đều có thể thấy tên và hình ảnh của bạn."</string>
<string name="user_add_user" msgid="7876449291500212468">"Thêm người dùng"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Thêm khách"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Xóa khách"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 6a35264e42b9..3b08e1f27bd6 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"还需<xliff:g id="TIME">%1$s</xliff:g>充满"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - 还需<xliff:g id="TIME">%2$s</xliff:g>充满"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充电方式已优化"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充电方式已优化"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"未知"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"正在充电"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"正在快速充电"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"无法创建新用户"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"未能创建新的访客"</string>
<string name="user_nickname" msgid="262624187455825083">"昵称"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"所有使用此设备的用户都将可以看到您的姓名和照片。"</string>
<string name="user_add_user" msgid="7876449291500212468">"添加用户"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"添加访客"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"移除访客"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 6f49688f2b63..eba782e54e48 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g>後充滿電"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充滿電"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - 已優化充電"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - 已優化充電"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"未知"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"充電中"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"快速充電中"</string>
@@ -552,7 +553,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"直至你關閉為止"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"剛剛"</string>
<string name="media_transfer_this_device_name" msgid="2357329267148436433">"此手機"</string>
- <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"這台平板電腦"</string>
+ <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"此平板電腦"</string>
<!-- no translation found for media_transfer_this_device_name_tv (5285685336836896535) -->
<skip />
<string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"插座喇叭"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"無法建立新使用者"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"無法建立新訪客"</string>
<string name="user_nickname" msgid="262624187455825083">"暱稱"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"此裝置的使用者都會看到你的名稱和相片。"</string>
<string name="user_add_user" msgid="7876449291500212468">"新增使用者"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"新增訪客"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"移除訪客"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 0985041701cd..099ad0364b0e 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g>後充飽"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充飽"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充電效能已最佳化"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充電效能將最佳化"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"不明"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"充電中"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"快速充電中"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"無法建立新的使用者"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"無法建立新訪客"</string>
<string name="user_nickname" msgid="262624187455825083">"暱稱"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"這部裝置的使用者都會看到你的名稱和相片。"</string>
<string name="user_add_user" msgid="7876449291500212468">"新增使用者"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"新增訪客"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"移除訪客"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index cce45ea35073..4d652f819ff3 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -478,7 +478,8 @@
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> okusele kuze kugcwale"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> okusele kuze kugcwale"</string>
<string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Ukushaja kuthuthukisiwe"</string>
- <string name="power_charging_future_paused" msgid="4730177778538118032">"<xliff:g id="LEVEL">%1$s</xliff:g> - Ukushaja kuthuthukisiwe"</string>
+ <!-- no translation found for power_charging_future_paused (1809543660923642799) -->
+ <skip />
<string name="battery_info_status_unknown" msgid="268625384868401114">"Akwaziwa"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Iyashaja"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ishaja ngokushesha"</string>
@@ -619,8 +620,7 @@
<string name="add_user_failed" msgid="4809887794313944872">"Yehlulekile ukudala umsebenzisi omusha"</string>
<string name="add_guest_failed" msgid="8074548434469843443">"Yehlulekile ukusungula isimenywa esisha"</string>
<string name="user_nickname" msgid="262624187455825083">"Isiteketiso"</string>
- <!-- no translation found for edit_user_info_message (5199468585059260053) -->
- <skip />
+ <string name="edit_user_info_message" msgid="5199468585059260053">"Igama nesithombe sakho kuzobonakala kunoma ubani osebenzisa le divayisi."</string>
<string name="user_add_user" msgid="7876449291500212468">"Engeza umsebenzisi"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Engeza isivakashi"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Susa isihambeli"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 1092a16216f9..9588e502f28e 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1129,7 +1129,7 @@
<!-- [CHAR_LIMIT=80] Label for battery level chart when charge been limited -->
<string name="power_charging_limited"><xliff:g id="level">%1$s</xliff:g> - Charging optimized</string>
<!-- [CHAR_LIMIT=80] Label for battery charging future pause -->
- <string name="power_charging_future_paused"><xliff:g id="level">%1$s</xliff:g> - Charging optimized</string>
+ <string name="power_charging_future_paused"><xliff:g id="level">%1$s</xliff:g> - Charging</string>
<!-- Battery Info screen. Value for a status item. Used for diagnostic info screens, precise translation isn't needed -->
<string name="battery_info_status_unknown">Unknown</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java
new file mode 100644
index 000000000000..6578eb7d50a6
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedDropDownPreference.java
@@ -0,0 +1,70 @@
+/*
+ * 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;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.preference.DropDownPreference;
+import androidx.preference.PreferenceViewHolder;
+
+public class RestrictedDropDownPreference extends DropDownPreference {
+ RestrictedPreferenceHelper mHelper;
+
+ public RestrictedDropDownPreference(@NonNull Context context) {
+ super(context);
+ mHelper = new RestrictedPreferenceHelper(context, this, null);
+ }
+
+ /**
+ * Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
+ * package. Marks the preference as disabled if so.
+ * @param settingIdentifier The key identifying the setting
+ * @param packageName the package to check the settingIdentifier for
+ */
+ public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
+ @NonNull String packageName) {
+ mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+ mHelper.onBindViewHolder(holder);
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ if (enabled && isDisabledByEcm()) {
+ mHelper.setDisabledByEcm(null);
+ return;
+ }
+
+ super.setEnabled(enabled);
+ }
+
+ @Override
+ public void performClick() {
+ if (!mHelper.performClick()) {
+ super.performClick();
+ }
+ }
+
+ public boolean isDisabledByEcm() {
+ return mHelper.isDisabledByEcm();
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
index d9024575f247..f36da19afd30 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
@@ -23,11 +23,11 @@ import static android.app.role.RoleManager.ROLE_FINANCED_DEVICE_KIOSK;
import static com.android.settingslib.Utils.getColorAttrDefaultColor;
-import android.Manifest;
import android.annotation.UserIdInt;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.admin.DevicePolicyManager;
+import android.app.ecm.EnhancedConfirmationManager;
import android.app.role.RoleManager;
import android.content.ComponentName;
import android.content.Context;
@@ -42,12 +42,10 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManager.EnforcingUser;
-import android.provider.Settings;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
import android.text.style.ImageSpan;
-import android.util.ArraySet;
import android.util.Log;
import android.view.MenuItem;
import android.widget.TextView;
@@ -60,7 +58,6 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.widget.LockPatternUtils;
import java.util.List;
-import java.util.Set;
/**
* Utility class to host methods usable in adding a restricted padlock icon and showing admin
@@ -70,24 +67,11 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils {
private static final String LOG_TAG = "RestrictedLockUtils";
private static final boolean DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG);
- private static final Set<String> ECM_KEYS = new ArraySet<>();
// TODO(b/281701062): reference role name from role manager once its exposed.
private static final String ROLE_DEVICE_LOCK_CONTROLLER =
"android.app.role.SYSTEM_FINANCED_DEVICE_CONTROLLER";
- static {
- if (android.security.Flags.extendEcmToAllSettings()) {
- ECM_KEYS.add(AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW);
- ECM_KEYS.add(AppOpsManager.OPSTR_GET_USAGE_STATS);
- ECM_KEYS.add(AppOpsManager.OPSTR_LOADER_USAGE_STATS);
- ECM_KEYS.add(Manifest.permission.BIND_DEVICE_ADMIN);
- }
-
- ECM_KEYS.add(AppOpsManager.OPSTR_ACCESS_NOTIFICATIONS);
- ECM_KEYS.add(AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE);
- }
-
/**
* @return drawables for displaying with settings that are locked by a device admin.
*/
@@ -112,32 +96,63 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils {
*/
@Nullable
public static Intent checkIfRequiresEnhancedConfirmation(@NonNull Context context,
- @NonNull String restriction,
- int uid,
- @Nullable String packageName) {
- // TODO(b/297372999): Replace with call to mainline module once ready
+ @NonNull String settingIdentifier, @NonNull String packageName) {
- if (!ECM_KEYS.contains(restriction)) {
+ if (!android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
+ || !android.security.Flags.extendEcmToAllSettings()) {
return null;
}
- final AppOpsManager appOps = (AppOpsManager) context
- .getSystemService(Context.APP_OPS_SERVICE);
- final int mode = appOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
- uid, packageName, null, null);
- final boolean ecmEnabled = context.getResources().getBoolean(
- com.android.internal.R.bool.config_enhancedConfirmationModeEnabled);
- if (ecmEnabled && mode != AppOpsManager.MODE_ALLOWED) {
- final Intent intent = new Intent(Settings.ACTION_SHOW_RESTRICTED_SETTING_DIALOG);
- intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
- intent.putExtra(Intent.EXTRA_UID, uid);
- return intent;
+ EnhancedConfirmationManager ecManager = (EnhancedConfirmationManager) context
+ .getSystemService(Context.ECM_ENHANCED_CONFIRMATION_SERVICE);
+ try {
+ if (ecManager.isRestricted(packageName, settingIdentifier)) {
+ return ecManager.createRestrictedSettingDialogIntent(
+ packageName, settingIdentifier);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(LOG_TAG, "package not found: " + packageName, e);
}
return null;
}
/**
+ * <p>This is {@code true} when the setting is a protected setting (i.e., a sensitive resource),
+ * and the app is restricted (i.e., considered dangerous), and the user has not yet cleared the
+ * app's restriction status (i.e., by clicking "Allow restricted settings" for this app). *
+ */
+ public static boolean isEnhancedConfirmationRestricted(@NonNull Context context,
+ @NonNull String settingIdentifier, @NonNull String packageName) {
+ if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
+ && android.security.Flags.extendEcmToAllSettings()) {
+ try {
+ return context.getSystemService(EnhancedConfirmationManager.class)
+ .isRestricted(packageName, settingIdentifier);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(LOG_TAG, "Exception when retrieving package:" + packageName, e);
+ return false;
+ }
+ } else {
+ try {
+ if (!settingIdentifier.equals(AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE)) {
+ return false;
+ }
+ int uid = context.getPackageManager().getPackageUid(packageName, 0);
+ final int mode = context.getSystemService(AppOpsManager.class)
+ .noteOpNoThrow(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
+ uid, packageName);
+ final boolean ecmEnabled = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_enhancedConfirmationModeEnabled);
+ return ecmEnabled && mode != AppOpsManager.MODE_ALLOWED;
+ } catch (Exception e) {
+ // Fallback in case if app ops is not available in testing.
+ return false;
+ }
+ }
+ }
+
+ /**
* Checks if a restriction is enforced on a user and returns the enforced admin and
* admin userId.
*
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
index 50e3bd08026c..495410b0a8ae 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreference.java
@@ -23,6 +23,7 @@ import android.os.Process;
import android.os.UserHandle;
import android.util.AttributeSet;
+import androidx.annotation.NonNull;
import androidx.core.content.res.TypedArrayUtils;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceViewHolder;
@@ -99,12 +100,12 @@ public class RestrictedPreference extends TwoTargetPreference {
/**
* Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
* package. Marks the preference as disabled if so.
- * @param restriction The key identifying the setting
- * @param packageName the package to check the restriction for
- * @param uid the uid of the package
+ * @param settingIdentifier The key identifying the setting
+ * @param packageName the package to check the settingIdentifier for
*/
- public void checkEcmRestrictionAndSetDisabled(String restriction, String packageName, int uid) {
- mHelper.checkEcmRestrictionAndSetDisabled(restriction, packageName, uid);
+ public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
+ @NonNull String packageName) {
+ mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName);
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
index a479269f40fb..734b92c7ac6e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
@@ -31,6 +31,8 @@ import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
@@ -43,9 +45,17 @@ import com.android.settingslib.utils.BuildCompatUtils;
* by device admins via user restrictions.
*/
public class RestrictedPreferenceHelper {
+ private static final String TAG = "RestrictedPreferenceHelper";
+
private final Context mContext;
private final Preference mPreference;
String packageName;
+
+ /**
+ * @deprecated TODO(b/308921175): This will be deleted with the
+ * {@link android.security.Flags#extendEcmToAllSettings} feature flag. Do not use for any new
+ * code.
+ */
int uid;
private boolean mDisabledByAdmin;
@@ -148,14 +158,15 @@ public class RestrictedPreferenceHelper {
return true;
}
if (mDisabledByEcm) {
- if (android.security.Flags.extendEcmToAllSettings()) {
+ if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
+ && android.security.Flags.extendEcmToAllSettings()) {
mContext.startActivity(mDisabledByEcmIntent);
return true;
+ } else {
+ RestrictedLockUtilsInternal.sendShowRestrictedSettingDialogIntent(mContext,
+ packageName, uid);
+ return true;
}
-
- RestrictedLockUtilsInternal.sendShowRestrictedSettingDialogIntent(mContext, packageName,
- uid);
- return true;
}
return false;
}
@@ -184,14 +195,14 @@ public class RestrictedPreferenceHelper {
/**
* Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
* package. Marks the preference as disabled if so.
- * @param restriction The key identifying the setting
- * @param packageName the package to check the restriction for
- * @param uid the uid of the package
+ * @param settingIdentifier The key identifying the setting
+ * @param packageName the package to check the settingIdentifier for
*/
- public void checkEcmRestrictionAndSetDisabled(String restriction, String packageName, int uid) {
- updatePackageDetails(packageName, uid);
+ public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
+ @NonNull String packageName) {
+ updatePackageDetails(packageName, android.os.Process.INVALID_UID);
Intent intent = RestrictedLockUtilsInternal.checkIfRequiresEnhancedConfirmation(
- mContext, restriction, uid, packageName);
+ mContext, settingIdentifier, packageName);
setDisabledByEcm(intent);
}
@@ -240,7 +251,7 @@ public class RestrictedPreferenceHelper {
* be disabled.
* @return true if the disabled state was changed.
*/
- public boolean setDisabledByEcm(Intent disabledIntent) {
+ public boolean setDisabledByEcm(@Nullable Intent disabledIntent) {
boolean disabled = disabledIntent != null;
boolean changed = false;
if (mDisabledByEcm != disabled) {
@@ -275,6 +286,10 @@ public class RestrictedPreferenceHelper {
if (mPreference instanceof PrimarySwitchPreference) {
((PrimarySwitchPreference) mPreference).setSwitchEnabled(isEnabled);
}
+
+ if (!isEnabled && mDisabledByEcm) {
+ mPreference.setSummary(R.string.disabled_by_app_ops_text);
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
index 70ece0fa8076..0c54c1903742 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -200,12 +200,12 @@ public class RestrictedSwitchPreference extends SwitchPreferenceCompat {
/**
* Checks if the given setting is subject to Enhanced Confirmation Mode restrictions for this
* package. Marks the preference as disabled if so.
- * @param restriction The key identifying the setting
- * @param packageName the package to check the restriction for
- * @param uid the uid of the package
+ * @param settingIdentifier The key identifying the setting
+ * @param packageName the package to check the settingIdentifier for
*/
- public void checkEcmRestrictionAndSetDisabled(String restriction, String packageName, int uid) {
- mHelper.checkEcmRestrictionAndSetDisabled(restriction, packageName, uid);
+ public void checkEcmRestrictionAndSetDisabled(@NonNull String settingIdentifier,
+ @NonNull String packageName) {
+ mHelper.checkEcmRestrictionAndSetDisabled(settingIdentifier, packageName);
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index 249fa7f0df77..e489bc552b25 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -1702,7 +1702,8 @@ public class ApplicationsState {
}
public boolean isPrivateProfile() {
- return UserManager.USER_TYPE_PROFILE_PRIVATE.equals(mProfileType);
+ return android.os.Flags.allowPrivateProfile()
+ && UserManager.USER_TYPE_PROFILE_PRIVATE.equals(mProfileType);
}
/**
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothBroadcastUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothBroadcastUtils.java
index 1ff2befd19ed..5e3bd9a66e88 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothBroadcastUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothBroadcastUtils.java
@@ -43,5 +43,5 @@ public final class BluetoothBroadcastUtils {
/**
* Bluetooth scheme.
*/
- public static final String SCHEME_BT_BROADCAST_METADATA = "BT:";
+ public static final String SCHEME_BT_BROADCAST_METADATA = "BLUETOOTH:UUID:184F;";
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothLeBroadcastMetadataExt.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothLeBroadcastMetadataExt.kt
index 9bb11f8da645..da1fd55c4ccb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothLeBroadcastMetadataExt.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothLeBroadcastMetadataExt.kt
@@ -31,38 +31,34 @@ import com.android.settingslib.bluetooth.BluetoothBroadcastUtils.SCHEME_BT_BROAD
object BluetoothLeBroadcastMetadataExt {
private const val TAG = "BtLeBroadcastMetadataExt"
- // BluetoothLeBroadcastMetadata
- private const val KEY_BT_QR_VER = "R"
- private const val KEY_BT_ADDRESS_TYPE = "T"
- private const val KEY_BT_DEVICE = "D"
- private const val KEY_BT_ADVERTISING_SID = "AS"
- private const val KEY_BT_BROADCAST_ID = "B"
+ // Data Elements for directing Broadcast Assistants
private const val KEY_BT_BROADCAST_NAME = "BN"
- private const val KEY_BT_PUBLIC_BROADCAST_DATA = "PM"
- private const val KEY_BT_SYNC_INTERVAL = "SI"
- private const val KEY_BT_BROADCAST_CODE = "C"
- private const val KEY_BT_SUBGROUPS = "SG"
- private const val KEY_BT_VENDOR_SPECIFIC = "V"
- private const val KEY_BT_ANDROID_VERSION = "VN"
-
- // Subgroup data
+ private const val KEY_BT_ADVERTISER_ADDRESS_TYPE = "AT"
+ private const val KEY_BT_ADVERTISER_ADDRESS = "AD"
+ private const val KEY_BT_BROADCAST_ID = "BI"
+ private const val KEY_BT_BROADCAST_CODE = "BC"
+ private const val KEY_BT_STREAM_METADATA = "MD"
+ private const val KEY_BT_STANDARD_QUALITY = "SQ"
+ private const val KEY_BT_HIGH_QUALITY = "HQ"
+
+ // Extended Bluetooth URI Data Elements
+ private const val KEY_BT_ADVERTISING_SID = "AS"
+ private const val KEY_BT_PA_INTERVAL = "PI"
+ private const val KEY_BT_NUM_SUBGROUPS = "NS"
+
+ // Subgroup data elements
private const val KEY_BTSG_BIS_SYNC = "BS"
- private const val KEY_BTSG_BIS_MASK = "BM"
- private const val KEY_BTSG_AUDIO_CONTENT = "AC"
+ private const val KEY_BTSG_NUM_BISES = "NB"
+ private const val KEY_BTSG_METADATA = "SM"
- // Vendor specific data
- private const val KEY_BTVSD_COMPANY_ID = "VI"
- private const val KEY_BTVSD_VENDOR_DATA = "VD"
+ // Vendor specific data, not being used
+ private const val KEY_BTVSD_VENDOR_DATA = "VS"
private const val DELIMITER_KEY_VALUE = ":"
- private const val DELIMITER_BT_LEVEL_1 = ";"
- private const val DELIMITER_BT_LEVEL_2 = ","
+ private const val DELIMITER_ELEMENT = ";"
private const val SUFFIX_QR_CODE = ";;"
- private const val ANDROID_VER = "U"
- private const val QR_CODE_VER = 0x010000
-
// BT constants
private const val BIS_SYNC_MAX_CHANNEL = 32
private const val BIS_SYNC_NO_PREFERENCE = 0xFFFFFFFFu
@@ -71,33 +67,55 @@ object BluetoothLeBroadcastMetadataExt {
/**
* Converts [BluetoothLeBroadcastMetadata] to QR code string.
*
- * QR code string will prefix with "BT:".
+ * QR code string will prefix with "BLUETOOTH:UUID:184F".
*/
fun BluetoothLeBroadcastMetadata.toQrCodeString(): String {
val entries = mutableListOf<Pair<String, String>>()
- entries.add(Pair(KEY_BT_QR_VER, QR_CODE_VER.toString()))
- entries.add(Pair(KEY_BT_ADDRESS_TYPE, this.sourceAddressType.toString()))
- entries.add(Pair(KEY_BT_DEVICE, this.sourceDevice.address.replace(":", "-")))
- entries.add(Pair(KEY_BT_ADVERTISING_SID, this.sourceAdvertisingSid.toString()))
- entries.add(Pair(KEY_BT_BROADCAST_ID, this.broadcastId.toString()))
- if (this.broadcastName != null) {
- entries.add(Pair(KEY_BT_BROADCAST_NAME, Base64.encodeToString(
- this.broadcastName?.toByteArray(Charsets.UTF_8), Base64.NO_WRAP)))
- }
- if (this.publicBroadcastMetadata != null) {
- entries.add(Pair(KEY_BT_PUBLIC_BROADCAST_DATA, Base64.encodeToString(
- this.publicBroadcastMetadata?.rawMetadata, Base64.NO_WRAP)))
- }
- entries.add(Pair(KEY_BT_SYNC_INTERVAL, this.paSyncInterval.toString()))
+ // Generate data elements for directing Broadcast Assistants
+ require(this.broadcastName != null) { "Broadcast name is mandatory for QR code" }
+ entries.add(Pair(KEY_BT_BROADCAST_NAME, Base64.encodeToString(
+ this.broadcastName?.toByteArray(Charsets.UTF_8), Base64.NO_WRAP)))
+ entries.add(Pair(KEY_BT_ADVERTISER_ADDRESS_TYPE, this.sourceAddressType.toString()))
+ entries.add(Pair(KEY_BT_ADVERTISER_ADDRESS, this.sourceDevice.address.replace(":", "")))
+ entries.add(Pair(KEY_BT_BROADCAST_ID, String.format("%X", this.broadcastId.toLong())))
if (this.broadcastCode != null) {
entries.add(Pair(KEY_BT_BROADCAST_CODE,
Base64.encodeToString(this.broadcastCode, Base64.NO_WRAP)))
}
+ if (this.publicBroadcastMetadata != null &&
+ this.publicBroadcastMetadata?.rawMetadata?.size != 0) {
+ entries.add(Pair(KEY_BT_STREAM_METADATA, Base64.encodeToString(
+ this.publicBroadcastMetadata?.rawMetadata, Base64.NO_WRAP)))
+ }
+ if ((this.audioConfigQuality and
+ BluetoothLeBroadcastMetadata.AUDIO_CONFIG_QUALITY_STANDARD) != 0) {
+ entries.add(Pair(KEY_BT_STANDARD_QUALITY, "1"))
+ }
+ if ((this.audioConfigQuality and
+ BluetoothLeBroadcastMetadata.AUDIO_CONFIG_QUALITY_HIGH) != 0) {
+ entries.add(Pair(KEY_BT_HIGH_QUALITY, "1"))
+ }
+
+ // Generate extended Bluetooth URI data elements
+ entries.add(Pair(KEY_BT_ADVERTISING_SID,
+ String.format("%X", this.sourceAdvertisingSid.toLong())))
+ entries.add(Pair(KEY_BT_PA_INTERVAL, String.format("%X", this.paSyncInterval.toLong())))
+ entries.add(Pair(KEY_BT_NUM_SUBGROUPS, String.format("%X", this.subgroups.size.toLong())))
+
this.subgroups.forEach {
- subgroup -> entries.add(Pair(KEY_BT_SUBGROUPS, subgroup.toQrCodeString())) }
- entries.add(Pair(KEY_BT_ANDROID_VERSION, ANDROID_VER))
+ val (bisSync, bisCount) = getBisSyncFromChannels(it.channels)
+ entries.add(Pair(KEY_BTSG_BIS_SYNC, String.format("%X", bisSync.toLong())))
+ if (bisCount > 0u) {
+ entries.add(Pair(KEY_BTSG_NUM_BISES, String.format("%X", bisCount.toLong())))
+ }
+ if (it.contentMetadata.rawMetadata.size != 0) {
+ entries.add(Pair(KEY_BTSG_METADATA,
+ Base64.encodeToString(it.contentMetadata.rawMetadata, Base64.NO_WRAP)))
+ }
+ }
+
val qrCodeString = SCHEME_BT_BROADCAST_METADATA +
- entries.toQrCodeString(DELIMITER_BT_LEVEL_1) + SUFFIX_QR_CODE
+ entries.toQrCodeString(DELIMITER_ELEMENT) + SUFFIX_QR_CODE
Log.d(TAG, "Generated QR string : $qrCodeString")
return qrCodeString
}
@@ -105,7 +123,7 @@ object BluetoothLeBroadcastMetadataExt {
/**
* Converts QR code string to [BluetoothLeBroadcastMetadata].
*
- * QR code string should prefix with "BT:BluetoothLeBroadcastMetadata:".
+ * QR code string should prefix with "BLUETOOTH:UUID:184F".
*/
fun convertToBroadcastMetadata(qrCodeString: String): BluetoothLeBroadcastMetadata? {
if (!qrCodeString.startsWith(SCHEME_BT_BROADCAST_METADATA)) {
@@ -126,15 +144,6 @@ object BluetoothLeBroadcastMetadataExt {
}
}
- private fun BluetoothLeBroadcastSubgroup.toQrCodeString(): String {
- val entries = mutableListOf<Pair<String, String>>()
- entries.add(Pair(KEY_BTSG_BIS_SYNC, getBisSyncFromChannels(this.channels).toString()))
- entries.add(Pair(KEY_BTSG_BIS_MASK, getBisMaskFromChannels(this.channels).toString()))
- entries.add(Pair(KEY_BTSG_AUDIO_CONTENT,
- Base64.encodeToString(this.contentMetadata.rawMetadata, Base64.NO_WRAP)))
- return entries.toQrCodeString(DELIMITER_BT_LEVEL_2)
- }
-
private fun List<Pair<String, String>>.toQrCodeString(delimiter: String): String {
val entryStrings = this.map{ it.first + DELIMITER_KEY_VALUE + it.second }
return entryStrings.joinToString(separator = delimiter)
@@ -143,23 +152,29 @@ object BluetoothLeBroadcastMetadataExt {
@TargetApi(Build.VERSION_CODES.TIRAMISU)
private fun parseQrCodeToMetadata(input: String): BluetoothLeBroadcastMetadata {
// Split into a list of list
- val level1Fields = input.split(DELIMITER_BT_LEVEL_1)
+ val elementFields = input.split(DELIMITER_ELEMENT)
.map{it.split(DELIMITER_KEY_VALUE, limit = 2)}
- var qrCodeVersion = -1
+
var sourceAddrType = BluetoothDevice.ADDRESS_TYPE_UNKNOWN
var sourceAddrString: String? = null
var sourceAdvertiserSid = -1
var broadcastId = -1
var broadcastName: String? = null
- var publicBroadcastMetadata: BluetoothLeAudioContentMetadata? = null
+ var streamMetadata: BluetoothLeAudioContentMetadata? = null
var paSyncInterval = -1
var broadcastCode: ByteArray? = null
- // List of VendorID -> Data Pairs
- var vendorDataList = mutableListOf<Pair<Int, ByteArray?>>()
- var androidVersion: String? = null
+ var audioConfigQualityStandard = -1
+ var audioConfigQualityHigh = -1
+ var numSubgroups = -1
+
+ // List of subgroup data
+ var subgroupBisSyncList = mutableListOf<UInt>()
+ var subgroupNumOfBisesList = mutableListOf<UInt>()
+ var subgroupMetadataList = mutableListOf<ByteArray?>()
+
val builder = BluetoothLeBroadcastMetadata.Builder()
- for (field: List<String> in level1Fields) {
+ for (field: List<String> in elementFields) {
if (field.isEmpty()) {
continue
}
@@ -167,190 +182,200 @@ object BluetoothLeBroadcastMetadataExt {
// Ignore 3rd value and after
val value = if (field.size > 1) field[1] else ""
when (key) {
- KEY_BT_QR_VER -> {
- require(qrCodeVersion == -1) { "Duplicate qrCodeVersion: $input" }
- qrCodeVersion = value.toInt()
+ // Parse data elements for directing Broadcast Assistants
+ KEY_BT_BROADCAST_NAME -> {
+ require(broadcastName == null) { "Duplicate broadcastName: $input" }
+ broadcastName = String(Base64.decode(value, Base64.NO_WRAP))
}
- KEY_BT_ADDRESS_TYPE -> {
+ KEY_BT_ADVERTISER_ADDRESS_TYPE -> {
require(sourceAddrType == BluetoothDevice.ADDRESS_TYPE_UNKNOWN) {
"Duplicate sourceAddrType: $input"
}
sourceAddrType = value.toInt()
}
- KEY_BT_DEVICE -> {
+ KEY_BT_ADVERTISER_ADDRESS -> {
require(sourceAddrString == null) { "Duplicate sourceAddr: $input" }
- sourceAddrString = value.replace("-", ":")
- }
- KEY_BT_ADVERTISING_SID -> {
- require(sourceAdvertiserSid == -1) { "Duplicate sourceAdvertiserSid: $input" }
- sourceAdvertiserSid = value.toInt()
+ sourceAddrString = value.chunked(2).joinToString(":")
}
KEY_BT_BROADCAST_ID -> {
require(broadcastId == -1) { "Duplicate broadcastId: $input" }
- broadcastId = value.toInt()
+ broadcastId = value.toInt(16)
}
- KEY_BT_BROADCAST_NAME -> {
- require(broadcastName == null) { "Duplicate broadcastName: $input" }
- broadcastName = String(Base64.decode(value, Base64.NO_WRAP))
+ KEY_BT_BROADCAST_CODE -> {
+ require(broadcastCode == null) { "Duplicate broadcastCode: $input" }
+
+ broadcastCode = Base64.decode(value.dropLastWhile { it.equals(0.toByte()) }
+ .toByteArray(), Base64.NO_WRAP)
}
- KEY_BT_PUBLIC_BROADCAST_DATA -> {
- require(publicBroadcastMetadata == null) {
- "Duplicate publicBroadcastMetadata $input"
+ KEY_BT_STREAM_METADATA -> {
+ require(streamMetadata == null) {
+ "Duplicate streamMetadata $input"
}
- publicBroadcastMetadata = BluetoothLeAudioContentMetadata
+ streamMetadata = BluetoothLeAudioContentMetadata
.fromRawBytes(Base64.decode(value, Base64.NO_WRAP))
}
- KEY_BT_SYNC_INTERVAL -> {
+ KEY_BT_STANDARD_QUALITY -> {
+ require(audioConfigQualityStandard == -1) {
+ "Duplicate audioConfigQualityStandard: $input"
+ }
+ audioConfigQualityStandard = value.toInt()
+ }
+ KEY_BT_HIGH_QUALITY -> {
+ require(audioConfigQualityHigh == -1) {
+ "Duplicate audioConfigQualityHigh: $input"
+ }
+ audioConfigQualityHigh = value.toInt()
+ }
+
+ // Parse extended Bluetooth URI data elements
+ KEY_BT_ADVERTISING_SID -> {
+ require(sourceAdvertiserSid == -1) { "Duplicate sourceAdvertiserSid: $input" }
+ sourceAdvertiserSid = value.toInt(16)
+ }
+ KEY_BT_PA_INTERVAL -> {
require(paSyncInterval == -1) { "Duplicate paSyncInterval: $input" }
- paSyncInterval = value.toInt()
+ paSyncInterval = value.toInt(16)
}
- KEY_BT_BROADCAST_CODE -> {
- require(broadcastCode == null) { "Duplicate broadcastCode: $input" }
- broadcastCode = Base64.decode(value, Base64.NO_WRAP)
+ KEY_BT_NUM_SUBGROUPS -> {
+ require(numSubgroups == -1) { "Duplicate numSubgroups: $input" }
+ numSubgroups = value.toInt(16)
}
- KEY_BT_ANDROID_VERSION -> {
- require(androidVersion == null) { "Duplicate androidVersion: $input" }
- androidVersion = value
- Log.i(TAG, "QR code Android version: $androidVersion")
+
+ // Repeatable subgroup elements
+ KEY_BTSG_BIS_SYNC -> {
+ subgroupBisSyncList.add(value.toUInt(16))
}
- // Repeatable
- KEY_BT_SUBGROUPS -> {
- builder.addSubgroup(parseSubgroupData(value))
+ KEY_BTSG_NUM_BISES -> {
+ subgroupNumOfBisesList.add(value.toUInt(16))
}
- // Repeatable
- KEY_BT_VENDOR_SPECIFIC -> {
- vendorDataList.add(parseVendorData(value))
+ KEY_BTSG_METADATA -> {
+ subgroupMetadataList.add(Base64.decode(value, Base64.NO_WRAP))
}
}
}
- Log.d(TAG, "parseQrCodeToMetadata: sourceAddrType=$sourceAddrType, " +
+ Log.d(TAG, "parseQrCodeToMetadata: main data elements sourceAddrType=$sourceAddrType, " +
"sourceAddr=$sourceAddrString, sourceAdvertiserSid=$sourceAdvertiserSid, " +
"broadcastId=$broadcastId, broadcastName=$broadcastName, " +
- "publicBroadcastMetadata=${publicBroadcastMetadata != null}, " +
+ "streamMetadata=${streamMetadata != null}, " +
"paSyncInterval=$paSyncInterval, " +
- "broadcastCode=${broadcastCode?.toString(Charsets.UTF_8)}")
- Log.d(TAG, "Not used in current code, but part of the specification: " +
- "qrCodeVersion=$qrCodeVersion, androidVersion=$androidVersion, " +
- "vendorDataListSize=${vendorDataList.size}")
+ "broadcastCode=${broadcastCode?.toString(Charsets.UTF_8)}, " +
+ "audioConfigQualityStandard=$audioConfigQualityStandard, " +
+ "audioConfigQualityHigh=$audioConfigQualityHigh")
+
val adapter = BluetoothAdapter.getDefaultAdapter()
+ // Check parsed elements data
+ require(broadcastName != null) {
+ "broadcastName($broadcastName) must present in QR code string"
+ }
+ var addr = sourceAddrString
+ var addrType = sourceAddrType
+ if (sourceAddrString != null) {
+ require(sourceAddrType != BluetoothDevice.ADDRESS_TYPE_UNKNOWN) {
+ "sourceAddrType($sourceAddrType) must present if address present"
+ }
+ } else {
+ // Use placeholder device if not present
+ addr = "FF:FF:FF:FF:FF:FF"
+ addrType = BluetoothDevice.ADDRESS_TYPE_RANDOM
+ }
+ val device = adapter.getRemoteLeDevice(requireNotNull(addr), addrType)
+
// add source device and set broadcast code
- val device = adapter.getRemoteLeDevice(requireNotNull(sourceAddrString), sourceAddrType)
+ var audioConfigQuality = BluetoothLeBroadcastMetadata.AUDIO_CONFIG_QUALITY_NONE or
+ (if (audioConfigQualityStandard != -1) audioConfigQualityStandard else 0) or
+ (if (audioConfigQualityHigh != -1) audioConfigQualityHigh else 0)
+
+ // process subgroup data
+ // metadata should include at least 1 subgroup for metadata, add a placeholder group if not present
+ numSubgroups = if (numSubgroups > 0) numSubgroups else 1
+ for (i in 0 until numSubgroups) {
+ val bisSync = subgroupBisSyncList.getOrNull(i)
+ val bisNum = subgroupNumOfBisesList.getOrNull(i)
+ val metadata = subgroupMetadataList.getOrNull(i)
+
+ val channels = convertToChannels(bisSync, bisNum)
+ val audioCodecConfigMetadata = BluetoothLeAudioCodecConfigMetadata.Builder()
+ .setAudioLocation(0).build()
+ val subgroup = BluetoothLeBroadcastSubgroup.Builder().apply {
+ setCodecId(SUBGROUP_LC3_CODEC_ID)
+ setCodecSpecificConfig(audioCodecConfigMetadata)
+ setContentMetadata(
+ BluetoothLeAudioContentMetadata.fromRawBytes(metadata ?: ByteArray(0)))
+ channels.forEach(::addChannel)
+ }.build()
+
+ Log.d(TAG, "parseQrCodeToMetadata: subgroup $i elements bisSync=$bisSync, " +
+ "bisNum=$bisNum, metadata=${metadata != null}")
+
+ builder.addSubgroup(subgroup)
+ }
+
builder.apply {
setSourceDevice(device, sourceAddrType)
setSourceAdvertisingSid(sourceAdvertiserSid)
setBroadcastId(broadcastId)
setBroadcastName(broadcastName)
- setPublicBroadcast(publicBroadcastMetadata != null)
- setPublicBroadcastMetadata(publicBroadcastMetadata)
+ // QR code should set PBP(public broadcast profile) for auracast
+ setPublicBroadcast(true)
+ setPublicBroadcastMetadata(streamMetadata)
setPaSyncInterval(paSyncInterval)
setEncrypted(broadcastCode != null)
setBroadcastCode(broadcastCode)
// Presentation delay is unknown and not useful when adding source
// Broadcast sink needs to sync to the Broadcast source to get presentation delay
setPresentationDelayMicros(0)
+ setAudioConfigQuality(audioConfigQuality)
}
return builder.build()
}
- private fun parseSubgroupData(input: String): BluetoothLeBroadcastSubgroup {
- Log.d(TAG, "parseSubgroupData: $input")
- val fields = input.split(DELIMITER_BT_LEVEL_2)
- var bisSync: UInt? = null
- var bisMask: UInt? = null
- var metadata: ByteArray? = null
-
- fields.forEach { field ->
- val(key, value) = field.split(DELIMITER_KEY_VALUE)
- when (key) {
- KEY_BTSG_BIS_SYNC -> {
- require(bisSync == null) { "Duplicate bisSync: $input" }
- bisSync = value.toUInt()
- }
- KEY_BTSG_BIS_MASK -> {
- require(bisMask == null) { "Duplicate bisMask: $input" }
- bisMask = value.toUInt()
- }
- KEY_BTSG_AUDIO_CONTENT -> {
- require(metadata == null) { "Duplicate metadata: $input" }
- metadata = Base64.decode(value, Base64.NO_WRAP)
- }
- }
- }
- val channels = convertToChannels(requireNotNull(bisSync), requireNotNull(bisMask))
- val audioCodecConfigMetadata = BluetoothLeAudioCodecConfigMetadata.Builder()
- .setAudioLocation(0).build()
- return BluetoothLeBroadcastSubgroup.Builder().apply {
- setCodecId(SUBGROUP_LC3_CODEC_ID)
- setCodecSpecificConfig(audioCodecConfigMetadata)
- setContentMetadata(
- BluetoothLeAudioContentMetadata.fromRawBytes(metadata ?: ByteArray(0)))
- channels.forEach(::addChannel)
- }.build()
- }
-
- private fun parseVendorData(input: String): Pair<Int, ByteArray?> {
- var companyId = -1
- var data: ByteArray? = null
- val fields = input.split(DELIMITER_BT_LEVEL_2)
- fields.forEach { field ->
- val(key, value) = field.split(DELIMITER_KEY_VALUE)
- when (key) {
- KEY_BTVSD_COMPANY_ID -> {
- require(companyId == -1) { "Duplicate companyId: $input" }
- companyId = value.toInt()
- }
- KEY_BTVSD_VENDOR_DATA -> {
- require(data == null) { "Duplicate data: $input" }
- data = Base64.decode(value, Base64.NO_WRAP)
- }
- }
- }
- return Pair(companyId, data)
- }
-
- private fun getBisSyncFromChannels(channels: List<BluetoothLeBroadcastChannel>): UInt {
+ private fun getBisSyncFromChannels(
+ channels: List<BluetoothLeBroadcastChannel>
+ ): Pair<UInt, UInt> {
var bisSync = 0u
- // channel index starts from 1
- channels.forEach { channel ->
- if (channel.isSelected && channel.channelIndex > 0) {
- bisSync = bisSync or (1u shl (channel.channelIndex - 1))
- }
- }
- // No channel is selected means no preference on Android platform
- return if (bisSync == 0u) BIS_SYNC_NO_PREFERENCE else bisSync
- }
-
- private fun getBisMaskFromChannels(channels: List<BluetoothLeBroadcastChannel>): UInt {
- var bisMask = 0u
+ var bisCount = 0u
// channel index starts from 1
channels.forEach { channel ->
if (channel.channelIndex > 0) {
- bisMask = bisMask or (1u shl (channel.channelIndex - 1))
+ bisCount++
+ if (channel.isSelected) {
+ bisSync = bisSync or (1u shl (channel.channelIndex - 1))
+ }
}
}
- return bisMask
+ // No channel is selected means no preference on Android platform
+ return if (bisSync == 0u) Pair(BIS_SYNC_NO_PREFERENCE, bisCount)
+ else Pair(bisSync, bisCount)
}
- private fun convertToChannels(bisSync: UInt, bisMask: UInt):
- List<BluetoothLeBroadcastChannel> {
- Log.d(TAG, "convertToChannels: bisSync=$bisSync, bisMask=$bisMask")
- var selectionMask = bisSync
- if (bisSync != BIS_SYNC_NO_PREFERENCE) {
- require(bisMask == (bisMask or bisSync)) {
- "bisSync($bisSync) must select a subset of bisMask($bisMask) if it has preferences"
- }
- } else {
- // No channel preference means no channel is selected
- selectionMask = 0u
- }
+ private fun convertToChannels(
+ bisSync: UInt?,
+ bisNum: UInt?
+ ): List<BluetoothLeBroadcastChannel> {
+ Log.d(TAG, "convertToChannels: bisSync=$bisSync, bisNum=$bisNum")
+ // if no BIS_SYNC or BIS_NUM available or BIS_SYNC is no preference
+ // return empty channel map with one placeholder channel
+ var selectedChannels = if (bisSync != null && bisNum != null) bisSync else 0u
val channels = mutableListOf<BluetoothLeBroadcastChannel>()
val audioCodecConfigMetadata = BluetoothLeAudioCodecConfigMetadata.Builder()
.setAudioLocation(0).build()
+
+ if (bisSync == BIS_SYNC_NO_PREFERENCE || selectedChannels == 0u) {
+ // No channel preference means no channel is selected
+ // Generate one placeholder channel for metadata
+ val channel = BluetoothLeBroadcastChannel.Builder().apply {
+ setSelected(false)
+ setChannelIndex(1)
+ setCodecMetadata(audioCodecConfigMetadata)
+ }
+ return listOf(channel.build())
+ }
+
for (i in 0 until BIS_SYNC_MAX_CHANNEL) {
val channelMask = 1u shl i
- if ((bisMask and channelMask) != 0u) {
+ if ((selectedChannels and channelMask) != 0u) {
val channel = BluetoothLeBroadcastChannel.Builder().apply {
- setSelected((selectionMask and channelMask) != 0u)
+ setSelected(true)
setChannelIndex(i + 1)
setCodecMetadata(audioCodecConfigMetadata)
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index bcdb64d17636..61c3ce7f6988 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -300,37 +300,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
mLocalNapRoleConnected = false;
}
- if (!HearingAidStatsLogUtils.isUserCategorized(mContext)) {
- if (HearingAidStatsLogUtils.isJustBonded(getAddress())) {
- // Saves bonded timestamp as the source for judging whether to display
- // the survey
- if (getProfiles().stream().anyMatch(
- p -> (p instanceof HearingAidProfile
- || p instanceof HapClientProfile))) {
- HearingAidStatsLogUtils.addCurrentTimeToHistory(mContext,
- HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_AIDS_PAIRED);
- } else if (getProfiles().stream().anyMatch(
- p -> (p instanceof A2dpSinkProfile || p instanceof HeadsetProfile))) {
- HearingAidStatsLogUtils.addCurrentTimeToHistory(mContext,
- HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_DEVICES_PAIRED);
- }
- HearingAidStatsLogUtils.removeFromJustBonded(getAddress());
- }
-
- // Saves connected timestamp as the source for judging whether to display
- // the survey
- if (newProfileState == BluetoothProfile.STATE_CONNECTED) {
- if (profile instanceof HearingAidProfile
- || profile instanceof HapClientProfile) {
- HearingAidStatsLogUtils.addCurrentTimeToHistory(mContext,
- HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_AIDS_CONNECTED);
- } else if (profile instanceof A2dpSinkProfile
- || profile instanceof HeadsetProfile) {
- HearingAidStatsLogUtils.addCurrentTimeToHistory(mContext,
- HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_DEVICES_CONNECTED);
- }
- }
- }
+ HearingAidStatsLogUtils.updateHistoryIfNeeded(mContext, this, profile, newProfileState);
}
fetchActiveDevices();
@@ -987,11 +957,9 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
connect();
}
- if (!HearingAidStatsLogUtils.isUserCategorized(mContext)) {
- // Saves this device as just bonded and checks if it's an hearing device after
- // profiles are connected. This is for judging whether to display the survey.
- HearingAidStatsLogUtils.addToJustBonded(getAddress());
- }
+ // Saves this device as just bonded and checks if it's an hearing device after
+ // profiles are connected. This is for judging whether to display the survey.
+ HearingAidStatsLogUtils.addToJustBonded(getAddress());
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
index 9fd174d4586c..ca47efdc5df3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
@@ -337,51 +337,39 @@ public class HearingAidDeviceManager {
return null;
}
- private boolean isLeAudioHearingAid(CachedBluetoothDevice cachedDevice) {
- List<LocalBluetoothProfile> profiles = cachedDevice.getProfiles();
- boolean supportLeAudio = profiles.stream().anyMatch(p -> p instanceof LeAudioProfile);
- boolean supportHapClient = profiles.stream().anyMatch(p -> p instanceof HapClientProfile);
- return supportLeAudio && supportHapClient;
- }
-
- private boolean isAshaHearingAid(CachedBluetoothDevice cachedDevice) {
- return cachedDevice.getProfiles().stream().anyMatch(p -> p instanceof HearingAidProfile);
- }
-
private HearingAidInfo generateHearingAidInfo(CachedBluetoothDevice cachedDevice) {
final LocalBluetoothProfileManager profileManager = mBtManager.getProfileManager();
- if (isAshaHearingAid(cachedDevice)) {
- final HearingAidProfile asha = profileManager.getHearingAidProfile();
- if (asha == null) {
- Log.w(TAG, "HearingAidProfile is not supported on this device");
- } else {
- long hiSyncId = asha.getHiSyncId(cachedDevice.getDevice());
- if (isValidHiSyncId(hiSyncId)) {
- final HearingAidInfo.Builder infoBuilder = new HearingAidInfo.Builder()
- .setAshaDeviceSide(asha.getDeviceSide(cachedDevice.getDevice()))
- .setAshaDeviceMode(asha.getDeviceMode(cachedDevice.getDevice()))
- .setHiSyncId(hiSyncId);
- return infoBuilder.build();
- }
+
+ final HearingAidProfile asha = profileManager.getHearingAidProfile();
+ if (asha == null) {
+ Log.w(TAG, "HearingAidProfile is not supported on this device");
+ } else {
+ long hiSyncId = asha.getHiSyncId(cachedDevice.getDevice());
+ if (isValidHiSyncId(hiSyncId)) {
+ final HearingAidInfo.Builder infoBuilder = new HearingAidInfo.Builder()
+ .setAshaDeviceSide(asha.getDeviceSide(cachedDevice.getDevice()))
+ .setAshaDeviceMode(asha.getDeviceMode(cachedDevice.getDevice()))
+ .setHiSyncId(hiSyncId);
+ return infoBuilder.build();
}
}
- if (isLeAudioHearingAid(cachedDevice)) {
- final HapClientProfile hapClientProfile = profileManager.getHapClientProfile();
- final LeAudioProfile leAudioProfile = profileManager.getLeAudioProfile();
- if (hapClientProfile == null || leAudioProfile == null) {
- Log.w(TAG, "HapClientProfile or LeAudioProfile is not supported on this device");
- } else {
- int audioLocation = leAudioProfile.getAudioLocation(cachedDevice.getDevice());
- int hearingAidType = hapClientProfile.getHearingAidType(cachedDevice.getDevice());
- if (audioLocation != BluetoothLeAudio.AUDIO_LOCATION_INVALID
- && hearingAidType != HapClientProfile.HearingAidType.TYPE_INVALID) {
- final HearingAidInfo.Builder infoBuilder = new HearingAidInfo.Builder()
- .setLeAudioLocation(audioLocation)
- .setHapDeviceType(hearingAidType);
- return infoBuilder.build();
- }
+
+ final HapClientProfile hapClientProfile = profileManager.getHapClientProfile();
+ final LeAudioProfile leAudioProfile = profileManager.getLeAudioProfile();
+ if (hapClientProfile == null || leAudioProfile == null) {
+ Log.w(TAG, "HapClientProfile or LeAudioProfile is not supported on this device");
+ } else {
+ int audioLocation = leAudioProfile.getAudioLocation(cachedDevice.getDevice());
+ int hearingAidType = hapClientProfile.getHearingAidType(cachedDevice.getDevice());
+ if (audioLocation != BluetoothLeAudio.AUDIO_LOCATION_INVALID
+ && hearingAidType != HapClientProfile.HearingAidType.TYPE_INVALID) {
+ final HearingAidInfo.Builder infoBuilder = new HearingAidInfo.Builder()
+ .setLeAudioLocation(audioLocation)
+ .setHapDeviceType(hearingAidType);
+ return infoBuilder.build();
}
}
+
return null;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidStatsLogUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidStatsLogUtils.java
index 97b94da60274..8e3df8bcc2dd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidStatsLogUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidStatsLogUtils.java
@@ -16,10 +16,9 @@
package com.android.settingslib.bluetooth;
+import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.SharedPreferences;
-import android.icu.text.SimpleDateFormat;
-import android.icu.util.TimeZone;
import android.util.Log;
import androidx.annotation.IntDef;
@@ -30,12 +29,14 @@ import com.android.internal.util.FrameworkStatsLog;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
-import java.util.Locale;
import java.util.Set;
-import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/** Utils class to report hearing aid metrics to statsd */
@@ -54,13 +55,13 @@ public final class HearingAidStatsLogUtils {
private static final String BT_HEARING_USER_CATEGORY = "bt_hearing_user_category";
private static final String HISTORY_RECORD_DELIMITER = ",";
- private static final String CATEGORY_HEARING_AIDS = "A11yHearingAidsUser";
- private static final String CATEGORY_NEW_HEARING_AIDS = "A11yNewHearingAidsUser";
- private static final String CATEGORY_HEARING_DEVICES = "A11yHearingDevicesUser";
- private static final String CATEGORY_NEW_HEARING_DEVICES = "A11yNewHearingDevicesUser";
+ static final String CATEGORY_HEARING_AIDS = "A11yHearingAidsUser";
+ static final String CATEGORY_NEW_HEARING_AIDS = "A11yNewHearingAidsUser";
+ static final String CATEGORY_HEARING_DEVICES = "A11yHearingDevicesUser";
+ static final String CATEGORY_NEW_HEARING_DEVICES = "A11yNewHearingDevicesUser";
- private static final long PAIRED_HISTORY_EXPIRED_TIME = TimeUnit.DAYS.toMillis(30);
- private static final long CONNECTED_HISTORY_EXPIRED_TIME = TimeUnit.DAYS.toMillis(7);
+ static final int PAIRED_HISTORY_EXPIRED_DAY = 30;
+ static final int CONNECTED_HISTORY_EXPIRED_DAY = 7;
private static final int VALID_PAIRED_EVENT_COUNT = 1;
private static final int VALID_CONNECTED_EVENT_COUNT = 7;
@@ -126,16 +127,43 @@ public final class HearingAidStatsLogUtils {
}
/**
- * Indicates if user is categorized as one of {@link #CATEGORY_HEARING_AIDS},
- * {@link #CATEGORY_NEW_HEARING_AIDS}, {@link #CATEGORY_HEARING_DEVICES}, and
- * {@link #CATEGORY_NEW_HEARING_DEVICES}.
+ * Updates corresponding history if we found the device is a hearing device after profile state
+ * changed.
*
* @param context the request context
- * @return true if user is already categorized as one of interested group
+ * @param cachedDevice the remote device
+ * @param profile the profile that has a state changed
+ * @param profileState the new profile state
*/
- public static boolean isUserCategorized(Context context) {
- String userCategory = getSharedPreferences(context).getString(BT_HEARING_USER_CATEGORY, "");
- return !userCategory.isEmpty();
+ public static void updateHistoryIfNeeded(Context context, CachedBluetoothDevice cachedDevice,
+ LocalBluetoothProfile profile, int profileState) {
+
+ if (isJustBonded(cachedDevice.getAddress())) {
+ // Saves bonded timestamp as the source for judging whether to display
+ // the survey
+ if (cachedDevice.getProfiles().stream().anyMatch(
+ p -> (p instanceof HearingAidProfile || p instanceof HapClientProfile))) {
+ HearingAidStatsLogUtils.addCurrentTimeToHistory(context,
+ HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_AIDS_PAIRED);
+ } else if (cachedDevice.getProfiles().stream().anyMatch(
+ p -> (p instanceof A2dpSinkProfile || p instanceof HeadsetProfile))) {
+ HearingAidStatsLogUtils.addCurrentTimeToHistory(context,
+ HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_DEVICES_PAIRED);
+ }
+ removeFromJustBonded(cachedDevice.getAddress());
+ }
+
+ // Saves connected timestamp as the source for judging whether to display
+ // the survey
+ if (profileState == BluetoothProfile.STATE_CONNECTED) {
+ if (profile instanceof HearingAidProfile || profile instanceof HapClientProfile) {
+ HearingAidStatsLogUtils.addCurrentTimeToHistory(context,
+ HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_AIDS_CONNECTED);
+ } else if (profile instanceof A2dpSinkProfile || profile instanceof HeadsetProfile) {
+ HearingAidStatsLogUtils.addCurrentTimeToHistory(context,
+ HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_DEVICES_CONNECTED);
+ }
+ }
}
/**
@@ -186,14 +214,6 @@ public final class HearingAidStatsLogUtils {
userCategory = CATEGORY_HEARING_DEVICES;
}
}
-
- if (!userCategory.isEmpty()) {
- // History become useless once user is categorized. Clear all history.
- SharedPreferences.Editor editor = getSharedPreferences(context).edit();
- editor.putString(BT_HEARING_USER_CATEGORY, userCategory).apply();
- clearHistory(context);
- sJustBondedDeviceAddressSet.clear();
- }
return userCategory;
}
@@ -211,7 +231,7 @@ public final class HearingAidStatsLogUtils {
* Removes the device address from the just bonded list.
* @param address the device address
*/
- public static void removeFromJustBonded(String address) {
+ private static void removeFromJustBonded(String address) {
sJustBondedDeviceAddressSet.remove(address);
}
@@ -220,24 +240,11 @@ public final class HearingAidStatsLogUtils {
* @param address the device address
* @return true if the device address is in the just bonded list
*/
- public static boolean isJustBonded(String address) {
+ private static boolean isJustBonded(String address) {
return sJustBondedDeviceAddressSet.contains(address);
}
/**
- * Clears all BT hearing devices related history stored in shared preference.
- * @param context the request context
- */
- private static synchronized void clearHistory(Context context) {
- SharedPreferences.Editor editor = getSharedPreferences(context).edit();
- editor.remove(BT_HEARING_AIDS_PAIRED_HISTORY)
- .remove(BT_HEARING_AIDS_CONNECTED_HISTORY)
- .remove(BT_HEARING_DEVICES_PAIRED_HISTORY)
- .remove(BT_HEARING_DEVICES_CONNECTED_HISTORY)
- .apply();
- }
-
- /**
* Adds current timestamp into BT hearing devices related history.
* @param context the request context
* @param type the type of history to store the data. See {@link HistoryType}.
@@ -256,7 +263,7 @@ public final class HearingAidStatsLogUtils {
}
return;
}
- if (history.peekLast() != null && isSameDay(history.peekLast(), timestamp)) {
+ if (history.peekLast() != null && isSameDay(timestamp, history.peekLast())) {
if (DEBUG) {
Log.w(TAG, "Skip this record, it's same day record");
}
@@ -275,25 +282,25 @@ public final class HearingAidStatsLogUtils {
|| BT_HEARING_DEVICES_PAIRED_HISTORY.equals(spName)) {
LinkedList<Long> history = convertToHistoryList(
getSharedPreferences(context).getString(spName, ""));
- removeRecordsBeforeTime(history, PAIRED_HISTORY_EXPIRED_TIME);
+ removeRecordsBeforeDay(history, PAIRED_HISTORY_EXPIRED_DAY);
return history;
} else if (BT_HEARING_AIDS_CONNECTED_HISTORY.equals(spName)
|| BT_HEARING_DEVICES_CONNECTED_HISTORY.equals(spName)) {
LinkedList<Long> history = convertToHistoryList(
getSharedPreferences(context).getString(spName, ""));
- removeRecordsBeforeTime(history, CONNECTED_HISTORY_EXPIRED_TIME);
+ removeRecordsBeforeDay(history, CONNECTED_HISTORY_EXPIRED_DAY);
return history;
}
return null;
}
- private static void removeRecordsBeforeTime(LinkedList<Long> history, long time) {
- if (history == null) {
+ private static void removeRecordsBeforeDay(LinkedList<Long> history, int day) {
+ if (history == null || history.isEmpty()) {
return;
}
- Long currentTime = System.currentTimeMillis();
+ long currentTime = System.currentTimeMillis();
while (history.peekFirst() != null
- && currentTime - history.peekFirst() > time) {
+ && dayDifference(currentTime, history.peekFirst()) >= day) {
history.poll();
}
}
@@ -324,11 +331,13 @@ public final class HearingAidStatsLogUtils {
* @return {@code true} if two timestamps are on the same day
*/
private static boolean isSameDay(long t1, long t2) {
- final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd", Locale.getDefault());
- sdf.setTimeZone(TimeZone.getDefault());
- String dateString1 = sdf.format(t1);
- String dateString2 = sdf.format(t2);
- return dateString1.equals(dateString2);
+ return dayDifference(t1, t2) == 0;
+ }
+ private static long dayDifference(long t1, long t2) {
+ ZoneId zoneId = ZoneId.systemDefault();
+ LocalDate date1 = Instant.ofEpochMilli(t1).atZone(zoneId).toLocalDate();
+ LocalDate date2 = Instant.ofEpochMilli(t2).atZone(zoneId).toLocalDate();
+ return Math.abs(ChronoUnit.DAYS.between(date1, date2));
}
private static SharedPreferences getSharedPreferences(Context context) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 581c7dea002c..50e2f9cb13f7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -92,13 +92,13 @@ public abstract class InfoMediaManager extends MediaManager {
}
}
- protected String mPackageName;
+ @NonNull protected final String mPackageName;
private MediaDevice mCurrentConnectedDevice;
private final LocalBluetoothManager mBluetoothManager;
private final Map<String, RouteListingPreference.Item> mPreferenceItemMap =
new ConcurrentHashMap<>();
- public InfoMediaManager(
+ /* package */ InfoMediaManager(
Context context,
@NonNull String packageName,
Notification notification,
@@ -112,7 +112,7 @@ public abstract class InfoMediaManager extends MediaManager {
/** Creates an instance of InfoMediaManager. */
public static InfoMediaManager createInstance(
Context context,
- String packageName,
+ @Nullable String packageName,
Notification notification,
LocalBluetoothManager localBluetoothManager) {
@@ -148,8 +148,7 @@ public abstract class InfoMediaManager extends MediaManager {
}
private void updateRouteListingPreference() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE
- && !TextUtils.isEmpty(mPackageName)) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
RouteListingPreference routeListingPreference =
getRouteListingPreference();
Api34Impl.onRouteListingPreferenceUpdated(routeListingPreference,
@@ -162,11 +161,6 @@ public abstract class InfoMediaManager extends MediaManager {
protected abstract void startScanOnRouter();
- /**
- * Transfer MediaDevice for media without package name.
- */
- protected abstract boolean connectDeviceWithoutPackageName(@NonNull MediaDevice device);
-
protected abstract void transferToRoute(@NonNull MediaRoute2Info route);
protected abstract void selectRoute(
@@ -200,6 +194,12 @@ public abstract class InfoMediaManager extends MediaManager {
@NonNull
protected abstract List<RoutingSessionInfo> getRemoteSessions();
+ /**
+ * Returns a non-empty list containing the routing sessions associated to the target media app.
+ *
+ * <p> The first item of the list is always the {@link RoutingSessionInfo#isSystemSession()
+ * system session}, followed other remote sessions linked to the target media app.
+ */
@NonNull
protected abstract List<RoutingSessionInfo> getRoutingSessionsForPackage();
@@ -207,9 +207,6 @@ public abstract class InfoMediaManager extends MediaManager {
protected abstract RoutingSessionInfo getRoutingSessionById(@NonNull String sessionId);
@NonNull
- protected abstract List<MediaRoute2Info> getAllRoutes();
-
- @NonNull
protected abstract List<MediaRoute2Info> getAvailableRoutesFromRouter();
@NonNull
@@ -218,11 +215,7 @@ public abstract class InfoMediaManager extends MediaManager {
protected final void rebuildDeviceList() {
mMediaDevices.clear();
mCurrentConnectedDevice = null;
- if (TextUtils.isEmpty(mPackageName)) {
- buildAllRoutes();
- } else {
- buildAvailableRoutes();
- }
+ buildAvailableRoutes();
}
protected final void notifyCurrentConnectedDeviceChanged() {
@@ -250,12 +243,8 @@ public abstract class InfoMediaManager extends MediaManager {
return;
}
- if (TextUtils.isEmpty(mPackageName)) {
- connectDeviceWithoutPackageName(device);
- } else {
- device.setConnectedRecord();
- transferToRoute(device.mRouteInfo);
- }
+ device.setConnectedRecord();
+ transferToRoute(device.mRouteInfo);
}
/**
@@ -265,13 +254,8 @@ public abstract class InfoMediaManager extends MediaManager {
* @return If add device successful return {@code true}, otherwise return {@code false}
*/
boolean addDeviceToPlayMedia(MediaDevice device) {
- if (TextUtils.isEmpty(mPackageName)) {
- Log.w(TAG, "addDeviceToPlayMedia() package name is null or empty!");
- return false;
- }
-
- final RoutingSessionInfo info = getRoutingSessionInfo();
- if (info == null || !info.getSelectableRoutes().contains(device.mRouteInfo.getId())) {
+ final RoutingSessionInfo info = getActiveRoutingSession();
+ if (!info.getSelectableRoutes().contains(device.mRouteInfo.getId())) {
Log.w(TAG, "addDeviceToPlayMedia() Ignoring selecting a non-selectable device : "
+ device.getName());
return false;
@@ -281,13 +265,11 @@ public abstract class InfoMediaManager extends MediaManager {
return true;
}
- private RoutingSessionInfo getRoutingSessionInfo() {
- final List<RoutingSessionInfo> sessionInfos = getRoutingSessionsForPackage();
-
- if (sessionInfos.isEmpty()) {
- return null;
- }
- return sessionInfos.get(sessionInfos.size() - 1);
+ @NonNull
+ private RoutingSessionInfo getActiveRoutingSession() {
+ // List is never empty.
+ final List<RoutingSessionInfo> sessions = getRoutingSessionsForPackage();
+ return sessions.get(sessions.size() - 1);
}
boolean isRoutingSessionAvailableForVolumeControl() {
@@ -306,7 +288,6 @@ public abstract class InfoMediaManager extends MediaManager {
boolean preferRouteListingOrdering() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE
- && !TextUtils.isEmpty(mPackageName)
&& Api34Impl.preferRouteListingOrdering(getRouteListingPreference());
}
@@ -326,13 +307,8 @@ public abstract class InfoMediaManager extends MediaManager {
* @return If device stop successful return {@code true}, otherwise return {@code false}
*/
boolean removeDeviceFromPlayMedia(MediaDevice device) {
- if (TextUtils.isEmpty(mPackageName)) {
- Log.w(TAG, "removeDeviceFromMedia() package name is null or empty!");
- return false;
- }
-
- final RoutingSessionInfo info = getRoutingSessionInfo();
- if (info == null || !info.getSelectedRoutes().contains(device.mRouteInfo.getId())) {
+ final RoutingSessionInfo info = getActiveRoutingSession();
+ if (!info.getSelectedRoutes().contains(device.mRouteInfo.getId())) {
Log.w(TAG, "removeDeviceFromMedia() Ignoring deselecting a non-deselectable device : "
+ device.getName());
return false;
@@ -346,18 +322,7 @@ public abstract class InfoMediaManager extends MediaManager {
* Release session to stop playing media on MediaDevice.
*/
boolean releaseSession() {
- if (TextUtils.isEmpty(mPackageName)) {
- Log.w(TAG, "releaseSession() package name is null or empty!");
- return false;
- }
-
- final RoutingSessionInfo sessionInfo = getRoutingSessionInfo();
- if (sessionInfo == null) {
- Log.w(TAG, "releaseSession() Ignoring release session : " + mPackageName);
- return false;
- }
-
- releaseSession(sessionInfo);
+ releaseSession(getActiveRoutingSession());
return true;
}
@@ -367,17 +332,7 @@ public abstract class InfoMediaManager extends MediaManager {
*/
@NonNull
List<MediaDevice> getSelectableMediaDevices() {
- if (TextUtils.isEmpty(mPackageName)) {
- Log.w(TAG, "getSelectableMediaDevices() package name is null or empty!");
- return Collections.emptyList();
- }
-
- final RoutingSessionInfo info = getRoutingSessionInfo();
- if (info == null) {
- Log.w(TAG, "getSelectableMediaDevices() cannot find selectable MediaDevice from : "
- + mPackageName);
- return Collections.emptyList();
- }
+ final RoutingSessionInfo info = getActiveRoutingSession();
final List<MediaDevice> deviceList = new ArrayList<>();
for (MediaRoute2Info route : getSelectableRoutes(info)) {
@@ -394,17 +349,7 @@ public abstract class InfoMediaManager extends MediaManager {
*/
@NonNull
List<MediaDevice> getDeselectableMediaDevices() {
- if (TextUtils.isEmpty(mPackageName)) {
- Log.d(TAG, "getDeselectableMediaDevices() package name is null or empty!");
- return Collections.emptyList();
- }
-
- final RoutingSessionInfo info = getRoutingSessionInfo();
- if (info == null) {
- Log.d(TAG, "getDeselectableMediaDevices() cannot find deselectable MediaDevice from : "
- + mPackageName);
- return Collections.emptyList();
- }
+ final RoutingSessionInfo info = getActiveRoutingSession();
final List<MediaDevice> deviceList = new ArrayList<>();
for (MediaRoute2Info route : getDeselectableRoutes(info)) {
@@ -422,13 +367,7 @@ public abstract class InfoMediaManager extends MediaManager {
*/
@NonNull
List<MediaDevice> getSelectedMediaDevices() {
- RoutingSessionInfo info = getRoutingSessionInfo();
-
- if (info == null) {
- Log.w(TAG, "getSelectedMediaDevices() cannot find selectable MediaDevice from : "
- + mPackageName);
- return Collections.emptyList();
- }
+ RoutingSessionInfo info = getActiveRoutingSession();
final List<MediaDevice> deviceList = new ArrayList<>();
for (MediaRoute2Info route : getSelectedRoutes(info)) {
@@ -462,20 +401,8 @@ public abstract class InfoMediaManager extends MediaManager {
* @param volume the value of volume
*/
void adjustSessionVolume(int volume) {
- if (TextUtils.isEmpty(mPackageName)) {
- Log.w(TAG, "adjustSessionVolume() package name is null or empty!");
- return;
- }
-
- final RoutingSessionInfo info = getRoutingSessionInfo();
- if (info == null) {
- Log.w(TAG, "adjustSessionVolume() can't found corresponding RoutingSession with : "
- + mPackageName);
- return;
- }
-
Log.d(TAG, "adjustSessionVolume() adjust volume: " + volume + ", with : " + mPackageName);
- setSessionVolume(info, volume);
+ setSessionVolume(getActiveRoutingSession(), volume);
}
/**
@@ -484,19 +411,7 @@ public abstract class InfoMediaManager extends MediaManager {
* @return maximum volume of the session, and return -1 if not found.
*/
public int getSessionVolumeMax() {
- if (TextUtils.isEmpty(mPackageName)) {
- Log.w(TAG, "getSessionVolumeMax() package name is null or empty!");
- return -1;
- }
-
- final RoutingSessionInfo info = getRoutingSessionInfo();
- if (info == null) {
- Log.w(TAG, "getSessionVolumeMax() can't find corresponding RoutingSession with : "
- + mPackageName);
- return -1;
- }
-
- return info.getVolumeMax();
+ return getActiveRoutingSession().getVolumeMax();
}
/**
@@ -505,34 +420,11 @@ public abstract class InfoMediaManager extends MediaManager {
* @return current volume of the session, and return -1 if not found.
*/
public int getSessionVolume() {
- if (TextUtils.isEmpty(mPackageName)) {
- Log.w(TAG, "getSessionVolume() package name is null or empty!");
- return -1;
- }
-
- final RoutingSessionInfo info = getRoutingSessionInfo();
- if (info == null) {
- Log.w(TAG, "getSessionVolume() can't find corresponding RoutingSession with : "
- + mPackageName);
- return -1;
- }
-
- return info.getVolume();
+ return getActiveRoutingSession().getVolume();
}
CharSequence getSessionName() {
- if (TextUtils.isEmpty(mPackageName)) {
- Log.w(TAG, "Unable to get session name. The package name is null or empty!");
- return null;
- }
-
- final RoutingSessionInfo info = getRoutingSessionInfo();
- if (info == null) {
- Log.w(TAG, "Unable to get session name for package: " + mPackageName);
- return null;
- }
-
- return info.getName();
+ return getActiveRoutingSession().getName();
}
@TargetApi(Build.VERSION_CODES.R)
@@ -548,20 +440,6 @@ public abstract class InfoMediaManager extends MediaManager {
// MediaRoute2Info.getType was made public on API 34, but exists since API 30.
@SuppressWarnings("NewApi")
- private void buildAllRoutes() {
- for (MediaRoute2Info route : getAllRoutes()) {
- if (DEBUG) {
- Log.d(TAG, "buildAllRoutes() route : " + route.getName() + ", volume : "
- + route.getVolume() + ", type : " + route.getType());
- }
- if (route.isSystemRoute()) {
- addMediaDevice(route);
- }
- }
- }
-
- // MediaRoute2Info.getType was made public on API 34, but exists since API 30.
- @SuppressWarnings("NewApi")
private synchronized void buildAvailableRoutes() {
for (MediaRoute2Info route : getAvailableRoutes()) {
if (DEBUG) {
@@ -572,42 +450,39 @@ public abstract class InfoMediaManager extends MediaManager {
}
}
private synchronized List<MediaRoute2Info> getAvailableRoutes() {
- List<MediaRoute2Info> infos = new ArrayList<>();
- RoutingSessionInfo routingSessionInfo = getRoutingSessionInfo();
- List<MediaRoute2Info> selectedRouteInfos = new ArrayList<>();
- if (routingSessionInfo != null) {
- selectedRouteInfos = getSelectedRoutes(routingSessionInfo);
- infos.addAll(selectedRouteInfos);
- infos.addAll(getSelectableRoutes(routingSessionInfo));
- }
- final List<MediaRoute2Info> transferableRoutes =
- getTransferableRoutes(mPackageName);
+ List<MediaRoute2Info> availableRoutes = new ArrayList<>();
+ RoutingSessionInfo activeSession = getActiveRoutingSession();
+
+ List<MediaRoute2Info> selectedRoutes = getSelectedRoutes(activeSession);
+ availableRoutes.addAll(selectedRoutes);
+ availableRoutes.addAll(getSelectableRoutes(activeSession));
+
+ final List<MediaRoute2Info> transferableRoutes = getTransferableRoutes(mPackageName);
for (MediaRoute2Info transferableRoute : transferableRoutes) {
boolean alreadyAdded = false;
- for (MediaRoute2Info mediaRoute2Info : infos) {
+ for (MediaRoute2Info mediaRoute2Info : availableRoutes) {
if (TextUtils.equals(transferableRoute.getId(), mediaRoute2Info.getId())) {
alreadyAdded = true;
break;
}
}
if (!alreadyAdded) {
- infos.add(transferableRoute);
+ availableRoutes.add(transferableRoute);
}
}
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE
- && !TextUtils.isEmpty(mPackageName)) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
RouteListingPreference routeListingPreference = getRouteListingPreference();
if (routeListingPreference != null) {
final List<RouteListingPreference.Item> preferenceRouteListing =
Api34Impl.composePreferenceRouteListing(
routeListingPreference);
- infos = Api34Impl.arrangeRouteListByPreference(selectedRouteInfos,
+ availableRoutes = Api34Impl.arrangeRouteListByPreference(selectedRoutes,
getAvailableRoutesFromRouter(),
preferenceRouteListing);
}
- return Api34Impl.filterDuplicatedIds(infos);
+ return Api34Impl.filterDuplicatedIds(availableRoutes);
} else {
- return infos;
+ return availableRoutes;
}
}
@@ -679,8 +554,8 @@ public abstract class InfoMediaManager extends MediaManager {
break;
}
- if (mediaDevice != null && !TextUtils.isEmpty(mPackageName)
- && getRoutingSessionInfo().getSelectedRoutes().contains(route.getId())) {
+ if (mediaDevice != null
+ && getActiveRoutingSession().getSelectedRoutes().contains(route.getId())) {
mediaDevice.setState(STATE_SELECTED);
if (mCurrentConnectedDevice == null) {
mCurrentConnectedDevice = mediaDevice;
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
index 97bbf12fd055..453e807947cf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
@@ -51,9 +51,9 @@ public class ManagerInfoMediaManager extends InfoMediaManager {
private final Executor mExecutor = Executors.newSingleThreadExecutor();
- public ManagerInfoMediaManager(
+ /* package */ ManagerInfoMediaManager(
Context context,
- String packageName,
+ @NonNull String packageName,
Notification notification,
LocalBluetoothManager localBluetoothManager) {
super(context, packageName, notification, localBluetoothManager);
@@ -86,18 +86,6 @@ public class ManagerInfoMediaManager extends InfoMediaManager {
}
@Override
- protected boolean connectDeviceWithoutPackageName(@NonNull MediaDevice device) {
- final RoutingSessionInfo info = mRouterManager.getSystemRoutingSession(null);
- if (info != null) {
- // TODO: b/279555229 - provide real user handle and package name of a caller.
- mRouterManager.transfer(
- info, device.mRouteInfo, android.os.Process.myUserHandle(), mPackageName);
- return true;
- }
- return false;
- }
-
- @Override
protected void selectRoute(@NonNull MediaRoute2Info route, @NonNull RoutingSessionInfo info) {
mRouterManager.selectRoute(info, route);
}
@@ -174,12 +162,6 @@ public class ManagerInfoMediaManager extends InfoMediaManager {
@Override
@NonNull
- protected List<MediaRoute2Info> getAllRoutes() {
- return mRouterManager.getAllRoutes();
- }
-
- @Override
- @NonNull
protected List<MediaRoute2Info> getAvailableRoutesFromRouter() {
return mRouterManager.getAvailableRoutes(mPackageName);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java
index 9d578bcc3b16..ea4de392e139 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java
@@ -41,7 +41,7 @@ import java.util.List;
NoOpInfoMediaManager(
Context context,
- String packageName,
+ @NonNull String packageName,
Notification notification,
LocalBluetoothManager localBluetoothManager) {
super(context, packageName, notification, localBluetoothManager);
@@ -58,11 +58,6 @@ import java.util.List;
}
@Override
- protected boolean connectDeviceWithoutPackageName(@NonNull MediaDevice device) {
- return false;
- }
-
- @Override
protected void transferToRoute(@NonNull MediaRoute2Info route) {
// Do nothing.
}
@@ -136,12 +131,6 @@ import java.util.List;
@NonNull
@Override
- protected List<MediaRoute2Info> getAllRoutes() {
- return Collections.emptyList();
- }
-
- @NonNull
- @Override
protected List<MediaRoute2Info> getAvailableRoutesFromRouter() {
return Collections.emptyList();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/OWNERS b/packages/SettingsLib/src/com/android/settingslib/media/OWNERS
index 3cae39f4ea40..7467ee1c1a7c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/OWNERS
+++ b/packages/SettingsLib/src/com/android/settingslib/media/OWNERS
@@ -1,4 +1,7 @@
# Default reviewers for this and subdirectories.
+ethibodeau@google.com
+michaelmikhil@google.com
+apotapov@google.com
shaoweishen@google.com
#Android Media - For minor changes and renames only.
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java
index aef09ac236f3..df03167cd0f9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java
@@ -41,6 +41,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -64,10 +65,12 @@ public final class RouterInfoMediaManager extends InfoMediaManager {
refreshDevices();
};
+ private final AtomicReference<MediaRouter2.ScanToken> mScanToken = new AtomicReference<>();
+
// TODO (b/321969740): Plumb target UserHandle between UMO and RouterInfoMediaManager.
- public RouterInfoMediaManager(
+ /* package */ RouterInfoMediaManager(
Context context,
- String packageName,
+ @NonNull String packageName,
Notification notification,
LocalBluetoothManager localBluetoothManager)
throws PackageNotAvailableException {
@@ -101,12 +104,24 @@ public final class RouterInfoMediaManager extends InfoMediaManager {
mExecutor, mRouteListingPreferenceCallback);
mRouter.registerTransferCallback(mExecutor, mTransferCallback);
mRouter.registerControllerCallback(mExecutor, mControllerCallback);
- mRouter.startScan();
+ if (Flags.enableScreenOffScanning()) {
+ MediaRouter2.ScanRequest request = new MediaRouter2.ScanRequest.Builder().build();
+ mScanToken.compareAndSet(null, mRouter.requestScan(request));
+ } else {
+ mRouter.startScan();
+ }
}
@Override
public void stopScan() {
- mRouter.stopScan();
+ if (Flags.enableScreenOffScanning()) {
+ MediaRouter2.ScanToken token = mScanToken.getAndSet(null);
+ if (token != null) {
+ mRouter.cancelScanRequest(token);
+ }
+ } else {
+ mRouter.stopScan();
+ }
mRouter.unregisterControllerCallback(mControllerCallback);
mRouter.unregisterTransferCallback(mTransferCallback);
mRouter.unregisterRouteListingPreferenceUpdatedCallback(mRouteListingPreferenceCallback);
@@ -114,17 +129,6 @@ public final class RouterInfoMediaManager extends InfoMediaManager {
}
@Override
- protected boolean connectDeviceWithoutPackageName(@NonNull MediaDevice device) {
- if (device.mRouteInfo == null) {
- return false;
- }
-
- RoutingController controller = mRouter.getSystemController();
- mRouter.transfer(controller, device.mRouteInfo);
- return true;
- }
-
- @Override
protected void transferToRoute(@NonNull MediaRoute2Info route) {
mRouter.transferTo(route);
}
@@ -241,12 +245,6 @@ public final class RouterInfoMediaManager extends InfoMediaManager {
@NonNull
@Override
- protected List<MediaRoute2Info> getAllRoutes() {
- return mRouter.getAllRoutes();
- }
-
- @NonNull
- @Override
protected List<MediaRoute2Info> getAvailableRoutesFromRouter() {
return mRouter.getRoutes();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/session/MediaSessionManagerExt.kt b/packages/SettingsLib/src/com/android/settingslib/media/session/MediaSessionManagerExt.kt
new file mode 100644
index 000000000000..cda6b8bb36be
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/media/session/MediaSessionManagerExt.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2024 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.media.session
+
+import android.media.session.MediaController
+import android.media.session.MediaSessionManager
+import android.os.UserHandle
+import androidx.concurrent.futures.DirectExecutor
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.buffer
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.launch
+
+/** [Flow] for [MediaSessionManager.OnActiveSessionsChangedListener]. */
+val MediaSessionManager.activeMediaChanges: Flow<Collection<MediaController>?>
+ get() =
+ callbackFlow {
+ val listener =
+ MediaSessionManager.OnActiveSessionsChangedListener { launch { send(it) } }
+ addOnActiveSessionsChangedListener(
+ null,
+ UserHandle.of(UserHandle.myUserId()),
+ DirectExecutor.INSTANCE,
+ listener,
+ )
+ awaitClose { removeOnActiveSessionsChangedListener(listener) }
+ }
+ .buffer(capacity = Channel.CONFLATED)
diff --git a/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/FakeNotificationsSoundPolicyRepository.kt b/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/FakeNotificationsSoundPolicyRepository.kt
index 60983070b1cf..2a44511599f1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/FakeNotificationsSoundPolicyRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/FakeNotificationsSoundPolicyRepository.kt
@@ -40,3 +40,21 @@ class FakeNotificationsSoundPolicyRepository : NotificationsSoundPolicyRepositor
mutableZenMode.value = zenMode
}
}
+
+fun FakeNotificationsSoundPolicyRepository.updateNotificationPolicy(
+ priorityCategories: Int = 0,
+ priorityCallSenders: Int = NotificationManager.Policy.PRIORITY_SENDERS_ANY,
+ priorityMessageSenders: Int = NotificationManager.Policy.CONVERSATION_SENDERS_NONE,
+ suppressedVisualEffects: Int = NotificationManager.Policy.SUPPRESSED_EFFECTS_UNSET,
+ state: Int = NotificationManager.Policy.STATE_UNSET,
+ priorityConversationSenders: Int = NotificationManager.Policy.CONVERSATION_SENDERS_NONE,
+) = updateNotificationPolicy(
+ NotificationManager.Policy(
+ priorityCategories,
+ priorityCallSenders,
+ priorityMessageSenders,
+ suppressedVisualEffects,
+ state,
+ priorityConversationSenders,
+ )
+) \ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractor.kt b/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractor.kt
new file mode 100644
index 000000000000..794cf832f48b
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractor.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2024 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.statusbar.notification.domain.interactor
+
+import android.app.NotificationManager
+import android.media.AudioManager
+import android.provider.Settings
+import android.service.notification.ZenModeConfig
+import com.android.settingslib.statusbar.notification.data.model.ZenMode
+import com.android.settingslib.statusbar.notification.data.repository.NotificationsSoundPolicyRepository
+import com.android.settingslib.volume.shared.model.AudioStream
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.map
+
+/** Determines notification sounds state and limitations. */
+class NotificationsSoundPolicyInteractor(
+ private val repository: NotificationsSoundPolicyRepository
+) {
+
+ /** @see NotificationManager.getNotificationPolicy */
+ val notificationPolicy: StateFlow<NotificationManager.Policy?>
+ get() = repository.notificationPolicy
+
+ /** @see NotificationManager.getZenMode */
+ val zenMode: StateFlow<ZenMode?>
+ get() = repository.zenMode
+
+ /** Checks if [notificationPolicy] allows alarms. */
+ val areAlarmsAllowed: Flow<Boolean?> = notificationPolicy.map { it?.allowAlarms() }
+
+ /** Checks if [notificationPolicy] allows media. */
+ val isMediaAllowed: Flow<Boolean?> = notificationPolicy.map { it?.allowMedia() }
+
+ /** Checks if [notificationPolicy] allows ringer. */
+ val isRingerAllowed: Flow<Boolean?> =
+ notificationPolicy.map { policy ->
+ policy ?: return@map null
+ !ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(policy)
+ }
+
+ /** Checks if the [stream] is muted by either [zenMode] or [notificationPolicy]. */
+ fun isZenMuted(stream: AudioStream): Flow<Boolean> {
+ return combine(
+ zenMode.filterNotNull(),
+ areAlarmsAllowed.filterNotNull(),
+ isMediaAllowed.filterNotNull(),
+ isRingerAllowed.filterNotNull(),
+ ) { zenMode, areAlarmsAllowed, isMediaAllowed, isRingerAllowed ->
+ if (zenMode.zenMode == Settings.Global.ZEN_MODE_NO_INTERRUPTIONS) {
+ return@combine true
+ }
+
+ val isNotificationOrRing =
+ stream.value == AudioManager.STREAM_RING ||
+ stream.value == AudioManager.STREAM_NOTIFICATION
+ if (isNotificationOrRing && zenMode.zenMode == Settings.Global.ZEN_MODE_ALARMS) {
+ return@combine true
+ }
+ if (zenMode.zenMode != Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
+ return@combine false
+ }
+
+ if (stream.value == AudioManager.STREAM_ALARM && !areAlarmsAllowed) {
+ return@combine true
+ }
+ if (stream.value == AudioManager.STREAM_MUSIC && !isMediaAllowed) {
+ return@combine true
+ }
+ if (isNotificationOrRing && !isRingerAllowed) {
+ return@combine true
+ }
+
+ return@combine false
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/OWNERS b/packages/SettingsLib/src/com/android/settingslib/volume/OWNERS
new file mode 100644
index 000000000000..75c7642fbed1
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/OWNERS
@@ -0,0 +1,5 @@
+apotapov@google.com
+ethibodeau@google.com
+michaelmikhil@google.com
+
+juliacr@google.com #{LAST_RESORT_SUGGESTION} \ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
index f729c04fb849..0df4615c8b7c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
@@ -21,10 +21,12 @@ import android.media.AudioManager
import android.media.AudioManager.OnCommunicationDeviceChangedListener
import androidx.concurrent.futures.DirectExecutor
import com.android.internal.util.ConcurrentUtils
-import com.android.settingslib.volume.shared.AudioManagerIntentsReceiver
+import com.android.settingslib.volume.shared.AudioManagerEventsReceiver
+import com.android.settingslib.volume.shared.model.AudioManagerEvent
import com.android.settingslib.volume.shared.model.AudioStream
import com.android.settingslib.volume.shared.model.AudioStreamModel
import com.android.settingslib.volume.shared.model.RingerMode
+import com.android.settingslib.volume.shared.model.StreamAudioManagerEvent
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
@@ -33,9 +35,11 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -61,10 +65,10 @@ interface AudioRepository {
val communicationDevice: StateFlow<AudioDeviceInfo?>
/** State of the [AudioStream]. */
- suspend fun getAudioStream(audioStream: AudioStream): Flow<AudioStreamModel>
+ fun getAudioStream(audioStream: AudioStream): Flow<AudioStreamModel>
- /** Current state of the [AudioStream]. */
- suspend fun getCurrentAudioStream(audioStream: AudioStream): AudioStreamModel
+ /** Returns the last audible volume before stream was muted. */
+ suspend fun getLastAudibleVolume(audioStream: AudioStream): Int
suspend fun setVolume(audioStream: AudioStream, volume: Int)
@@ -72,7 +76,7 @@ interface AudioRepository {
}
class AudioRepositoryImpl(
- private val audioManagerIntentsReceiver: AudioManagerIntentsReceiver,
+ private val audioManagerEventsReceiver: AudioManagerEventsReceiver,
private val audioManager: AudioManager,
private val backgroundCoroutineContext: CoroutineContext,
private val coroutineScope: CoroutineScope,
@@ -89,8 +93,8 @@ class AudioRepositoryImpl(
.stateIn(coroutineScope, SharingStarted.WhileSubscribed(), audioManager.mode)
override val ringerMode: StateFlow<RingerMode> =
- audioManagerIntentsReceiver.intents
- .filter { AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION == it.action }
+ audioManagerEventsReceiver.events
+ .filterIsInstance(AudioManagerEvent.InternalRingerModeChanged::class)
.map { RingerMode(audioManager.ringerModeInternal) }
.flowOn(backgroundCoroutineContext)
.stateIn(
@@ -119,23 +123,34 @@ class AudioRepositoryImpl(
audioManager.communicationDevice,
)
- override suspend fun getAudioStream(audioStream: AudioStream): Flow<AudioStreamModel> {
- return audioManagerIntentsReceiver.intents
+ override fun getAudioStream(audioStream: AudioStream): Flow<AudioStreamModel> {
+ return audioManagerEventsReceiver.events
+ .filter {
+ if (it is StreamAudioManagerEvent) {
+ it.audioStream == audioStream
+ } else {
+ true
+ }
+ }
.map { getCurrentAudioStream(audioStream) }
+ .onStart { emit(getCurrentAudioStream(audioStream)) }
.flowOn(backgroundCoroutineContext)
}
- override suspend fun getCurrentAudioStream(audioStream: AudioStream): AudioStreamModel {
+ private fun getCurrentAudioStream(audioStream: AudioStream): AudioStreamModel {
+ return AudioStreamModel(
+ audioStream = audioStream,
+ minVolume = getMinVolume(audioStream),
+ maxVolume = audioManager.getStreamMaxVolume(audioStream.value),
+ volume = audioManager.getStreamVolume(audioStream.value),
+ isAffectedByRingerMode = audioManager.isStreamAffectedByRingerMode(audioStream.value),
+ isMuted = audioManager.isStreamMute(audioStream.value),
+ )
+ }
+
+ override suspend fun getLastAudibleVolume(audioStream: AudioStream): Int {
return withContext(backgroundCoroutineContext) {
- AudioStreamModel(
- audioStream = audioStream,
- minVolume = getMinVolume(audioStream),
- maxVolume = audioManager.getStreamMaxVolume(audioStream.value),
- volume = audioManager.getStreamVolume(audioStream.value),
- isAffectedByRingerMode =
- audioManager.isStreamAffectedByRingerMode(audioStream.value),
- isMuted = audioManager.isStreamMute(audioStream.value)
- )
+ audioManager.getLastAudibleStreamVolume(audioStream.value)
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt
index aa9ae76c66c4..298dd71e555e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt
@@ -15,13 +15,13 @@
*/
package com.android.settingslib.volume.data.repository
-import android.media.AudioManager
import android.media.MediaRouter2Manager
import android.media.RoutingSessionInfo
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
import com.android.settingslib.volume.data.model.RoutingSession
-import com.android.settingslib.volume.shared.AudioManagerIntentsReceiver
+import com.android.settingslib.volume.shared.AudioManagerEventsReceiver
+import com.android.settingslib.volume.shared.model.AudioManagerEvent
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
@@ -29,7 +29,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.callbackFlow
-import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
@@ -54,7 +54,7 @@ interface LocalMediaRepository {
}
class LocalMediaRepositoryImpl(
- audioManagerIntentsReceiver: AudioManagerIntentsReceiver,
+ audioManagerEventsReceiver: AudioManagerEventsReceiver,
private val localMediaManager: LocalMediaManager,
private val mediaRouter2Manager: MediaRouter2Manager,
coroutineScope: CoroutineScope,
@@ -62,9 +62,9 @@ class LocalMediaRepositoryImpl(
) : LocalMediaRepository {
private val devicesChanges =
- audioManagerIntentsReceiver.intents.filter {
- AudioManager.STREAM_DEVICES_CHANGED_ACTION == it.action
- }
+ audioManagerEventsReceiver.events.filterIsInstance(
+ AudioManagerEvent.StreamDevicesChanged::class
+ )
private val mediaDevicesUpdates: Flow<DevicesUpdate> =
callbackFlow {
val callback =
@@ -109,6 +109,7 @@ class LocalMediaRepositoryImpl(
override val currentConnectedDevice: StateFlow<MediaDevice?> =
merge(devicesChanges, mediaDevicesUpdates)
.map { localMediaManager.currentConnectedDevice }
+ .onStart { emit(localMediaManager.currentConnectedDevice) }
.stateIn(
coroutineScope,
SharingStarted.WhileSubscribed(),
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerExt.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerExt.kt
new file mode 100644
index 000000000000..1f037c0280e3
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerExt.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2024 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.volume.data.repository
+
+import android.media.MediaMetadata
+import android.media.session.MediaController
+import android.media.session.MediaSession
+import android.media.session.PlaybackState
+import android.os.Bundle
+import android.os.Handler
+import kotlinx.coroutines.channels.ProducerScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.launch
+
+/** [MediaController.Callback] flow representation. */
+fun MediaController.stateChanges(handler: Handler): Flow<MediaControllerChange> {
+ return callbackFlow {
+ val callback = MediaControllerCallbackProducer(this)
+ registerCallback(callback, handler)
+ awaitClose { unregisterCallback(callback) }
+ }
+}
+
+/** Models particular change event received by [MediaController.Callback]. */
+sealed interface MediaControllerChange {
+
+ data object SessionDestroyed : MediaControllerChange
+
+ data class SessionEvent(val event: String, val extras: Bundle?) : MediaControllerChange
+
+ data class PlaybackStateChanged(val state: PlaybackState?) : MediaControllerChange
+
+ data class MetadataChanged(val metadata: MediaMetadata?) : MediaControllerChange
+
+ data class QueueChanged(val queue: MutableList<MediaSession.QueueItem>?) :
+ MediaControllerChange
+
+ data class QueueTitleChanged(val title: CharSequence?) : MediaControllerChange
+
+ data class ExtrasChanged(val extras: Bundle?) : MediaControllerChange
+
+ data class AudioInfoChanged(val info: MediaController.PlaybackInfo?) : MediaControllerChange
+}
+
+private class MediaControllerCallbackProducer(
+ private val producingScope: ProducerScope<MediaControllerChange>
+) : MediaController.Callback() {
+
+ override fun onSessionDestroyed() {
+ send(MediaControllerChange.SessionDestroyed)
+ }
+
+ override fun onSessionEvent(event: String, extras: Bundle?) {
+ send(MediaControllerChange.SessionEvent(event, extras))
+ }
+
+ override fun onPlaybackStateChanged(state: PlaybackState?) {
+ send(MediaControllerChange.PlaybackStateChanged(state))
+ }
+
+ override fun onMetadataChanged(metadata: MediaMetadata?) {
+ send(MediaControllerChange.MetadataChanged(metadata))
+ }
+
+ override fun onQueueChanged(queue: MutableList<MediaSession.QueueItem>?) {
+ send(MediaControllerChange.QueueChanged(queue))
+ }
+
+ override fun onQueueTitleChanged(title: CharSequence?) {
+ send(MediaControllerChange.QueueTitleChanged(title))
+ }
+
+ override fun onExtrasChanged(extras: Bundle?) {
+ send(MediaControllerChange.ExtrasChanged(extras))
+ }
+
+ override fun onAudioInfoChanged(info: MediaController.PlaybackInfo?) {
+ send(MediaControllerChange.AudioInfoChanged(info))
+ }
+
+ private fun send(change: MediaControllerChange) {
+ producingScope.launch { producingScope.send(change) }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerRepository.kt
index ab8c6b820177..7c231d1fad4e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerRepository.kt
@@ -16,21 +16,21 @@
package com.android.settingslib.volume.data.repository
-import android.media.AudioManager
import android.media.session.MediaController
import android.media.session.MediaSessionManager
-import android.media.session.PlaybackState
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.settingslib.bluetooth.headsetAudioModeChanges
-import com.android.settingslib.volume.shared.AudioManagerIntentsReceiver
+import com.android.settingslib.media.session.activeMediaChanges
+import com.android.settingslib.volume.shared.AudioManagerEventsReceiver
+import com.android.settingslib.volume.shared.model.AudioManagerEvent
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filterIsInstance
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
@@ -38,11 +38,11 @@ import kotlinx.coroutines.flow.stateIn
interface MediaControllerRepository {
/** Current [MediaController]. Null is emitted when there is no active [MediaController]. */
- val activeMediaController: StateFlow<MediaController?>
+ val activeLocalMediaController: StateFlow<MediaController?>
}
class MediaControllerRepositoryImpl(
- audioManagerIntentsReceiver: AudioManagerIntentsReceiver,
+ audioManagerEventsReceiver: AudioManagerEventsReceiver,
private val mediaSessionManager: MediaSessionManager,
localBluetoothManager: LocalBluetoothManager?,
coroutineScope: CoroutineScope,
@@ -50,29 +50,31 @@ class MediaControllerRepositoryImpl(
) : MediaControllerRepository {
private val devicesChanges =
- audioManagerIntentsReceiver.intents.filter {
- AudioManager.STREAM_DEVICES_CHANGED_ACTION == it.action
- }
- override val activeMediaController: StateFlow<MediaController?> =
- buildList {
- localBluetoothManager?.headsetAudioModeChanges?.let { add(it) }
- add(devicesChanges)
+ audioManagerEventsReceiver.events.filterIsInstance(
+ AudioManagerEvent.StreamDevicesChanged::class
+ )
+
+ override val activeLocalMediaController: StateFlow<MediaController?> =
+ combine(
+ mediaSessionManager.activeMediaChanges.onStart {
+ emit(mediaSessionManager.getActiveSessions(null))
+ },
+ localBluetoothManager?.headsetAudioModeChanges?.onStart { emit(Unit) }
+ ?: flowOf(null),
+ devicesChanges.onStart { emit(AudioManagerEvent.StreamDevicesChanged) },
+ ) { controllers, _, _ ->
+ controllers?.let(::findLocalMediaController)
}
- .merge()
- .onStart { emit(Unit) }
- .map { getActiveLocalMediaController() }
.flowOn(backgroundContext)
.stateIn(coroutineScope, SharingStarted.WhileSubscribed(), null)
- private fun getActiveLocalMediaController(): MediaController? {
+ private fun findLocalMediaController(
+ controllers: Collection<MediaController>,
+ ): MediaController? {
var localController: MediaController? = null
val remoteMediaSessionLists: MutableList<String> = ArrayList()
- for (controller in mediaSessionManager.getActiveSessions(null)) {
+ for (controller in controllers) {
val playbackInfo: MediaController.PlaybackInfo = controller.playbackInfo ?: continue
- val playbackState = controller.playbackState ?: continue
- if (inactivePlaybackStates.contains(playbackState.state)) {
- continue
- }
when (playbackInfo.playbackType) {
MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE -> {
if (localController?.packageName.equals(controller.packageName)) {
@@ -94,9 +96,4 @@ class MediaControllerRepositoryImpl(
}
return localController
}
-
- private companion object {
- val inactivePlaybackStates =
- setOf(PlaybackState.STATE_STOPPED, PlaybackState.STATE_NONE, PlaybackState.STATE_ERROR)
- }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioVolumeInteractor.kt b/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioVolumeInteractor.kt
new file mode 100644
index 000000000000..56b0bf74574f
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioVolumeInteractor.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2024 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.volume.domain.interactor
+
+import android.media.AudioManager
+import com.android.settingslib.statusbar.notification.domain.interactor.NotificationsSoundPolicyInteractor
+import com.android.settingslib.volume.data.repository.AudioRepository
+import com.android.settingslib.volume.shared.model.AudioStream
+import com.android.settingslib.volume.shared.model.AudioStreamModel
+import com.android.settingslib.volume.shared.model.RingerMode
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+
+/** Provides audio stream state and an ability to change it */
+class AudioVolumeInteractor(
+ private val audioRepository: AudioRepository,
+ private val notificationsSoundPolicyInteractor: NotificationsSoundPolicyInteractor,
+) {
+
+ /** State of the [AudioStream]. */
+ fun getAudioStream(audioStream: AudioStream): Flow<AudioStreamModel> =
+ combine(
+ audioRepository.getAudioStream(audioStream),
+ audioRepository.ringerMode,
+ notificationsSoundPolicyInteractor.isZenMuted(audioStream)
+ ) { streamModel: AudioStreamModel, ringerMode: RingerMode, isZenMuted: Boolean ->
+ streamModel.copy(volume = processVolume(streamModel, ringerMode, isZenMuted))
+ }
+
+ suspend fun setVolume(audioStream: AudioStream, volume: Int) =
+ audioRepository.setVolume(audioStream, volume)
+
+ suspend fun setMuted(audioStream: AudioStream, isMuted: Boolean) =
+ audioRepository.setMuted(audioStream, isMuted)
+
+ /** Checks if the volume can be changed via the UI. */
+ fun canChangeVolume(audioStream: AudioStream): Flow<Boolean> {
+ return if (audioStream.value == AudioManager.STREAM_NOTIFICATION) {
+ getAudioStream(AudioStream(AudioManager.STREAM_RING)).map { !it.isMuted }
+ } else {
+ flowOf(true)
+ }
+ }
+
+ private suspend fun processVolume(
+ audioStreamModel: AudioStreamModel,
+ ringerMode: RingerMode,
+ isZenMuted: Boolean,
+ ): Int {
+ if (isZenMuted) {
+ return audioRepository.getLastAudibleVolume(audioStreamModel.audioStream)
+ }
+ val isNotificationOrRing =
+ audioStreamModel.audioStream.value == AudioManager.STREAM_RING ||
+ audioStreamModel.audioStream.value == AudioManager.STREAM_NOTIFICATION
+ if (isNotificationOrRing && ringerMode.value == AudioManager.RINGER_MODE_VIBRATE) {
+ // For ringer-mode affected streams, show volume as zero when ringer mode is vibrate
+ if (
+ audioStreamModel.audioStream.value == AudioManager.STREAM_RING ||
+ (audioStreamModel.audioStream.value == AudioManager.STREAM_NOTIFICATION &&
+ audioStreamModel.isMuted)
+ ) {
+ return 0
+ }
+ } else if (audioStreamModel.isMuted) {
+ return 0
+ }
+ return audioStreamModel.volume
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioManagerIntentsReceiver.kt b/packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioManagerEventsReceiver.kt
index 9fa4c86cdea1..c3b1a7cb16e3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioManagerIntentsReceiver.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioManagerEventsReceiver.kt
@@ -21,6 +21,9 @@ import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.media.AudioManager
+import android.util.Log
+import com.android.settingslib.volume.shared.model.AudioManagerEvent
+import com.android.settingslib.volume.shared.model.AudioStream
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.SharedFlow
@@ -28,19 +31,20 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.launch
-/** Exposes [AudioManager] intents as a observable shared flow. */
-interface AudioManagerIntentsReceiver {
+/** Exposes [AudioManager] events as a observable shared flow. */
+interface AudioManagerEventsReceiver {
- val intents: SharedFlow<Intent>
+ val events: SharedFlow<AudioManagerEvent>
}
-class AudioManagerIntentsReceiverImpl(
+class AudioManagerEventsReceiverImpl(
private val context: Context,
coroutineScope: CoroutineScope,
-) : AudioManagerIntentsReceiver {
+) : AudioManagerEventsReceiver {
private val allActions: Collection<String>
get() =
@@ -50,9 +54,10 @@ class AudioManagerIntentsReceiverImpl(
AudioManager.VOLUME_CHANGED_ACTION,
AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION,
AudioManager.STREAM_DEVICES_CHANGED_ACTION,
+ AudioManager.ACTION_VOLUME_CHANGED,
)
- override val intents: SharedFlow<Intent> =
+ override val events: SharedFlow<AudioManagerEvent> =
callbackFlow {
val receiver =
object : BroadcastReceiver() {
@@ -73,5 +78,34 @@ class AudioManagerIntentsReceiverImpl(
}
.filterNotNull()
.filter { intent -> allActions.contains(intent.action) }
+ .mapNotNull { it.toAudioManagerEvent() }
.shareIn(coroutineScope, SharingStarted.WhileSubscribed())
+
+ private fun Intent.toAudioManagerEvent(): AudioManagerEvent? {
+ when (action) {
+ AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION ->
+ return AudioManagerEvent.InternalRingerModeChanged
+ AudioManager.STREAM_DEVICES_CHANGED_ACTION ->
+ return AudioManagerEvent.StreamDevicesChanged
+ AudioManager.MASTER_MUTE_CHANGED_ACTION ->
+ return AudioManagerEvent.StreamMasterMuteChanged
+ }
+
+ val audioStreamType: Int =
+ getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, AudioManager.ERROR)
+ if (audioStreamType == AudioManager.ERROR) {
+ Log.e(
+ "AudioManagerIntentsReceiver",
+ "Intent doesn't have AudioManager.EXTRA_VOLUME_STREAM_TYPE extra",
+ )
+ return null
+ }
+ val audioStream = AudioStream(audioStreamType)
+ return when (action) {
+ AudioManager.STREAM_MUTE_CHANGED_ACTION ->
+ AudioManagerEvent.StreamMuteChanged(audioStream)
+ AudioManager.VOLUME_CHANGED_ACTION -> AudioManagerEvent.StreamVolumeChanged(audioStream)
+ else -> null
+ }
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/shared/model/AudioManagerEvent.kt b/packages/SettingsLib/src/com/android/settingslib/volume/shared/model/AudioManagerEvent.kt
new file mode 100644
index 000000000000..e19896bc5e87
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/shared/model/AudioManagerEvent.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 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.volume.shared.model
+
+/** Model events happening with the [android.media.AudioManager]. */
+sealed interface AudioManagerEvent {
+
+ data class StreamMuteChanged(override val audioStream: AudioStream) : StreamAudioManagerEvent
+
+ data class StreamVolumeChanged(override val audioStream: AudioStream) : StreamAudioManagerEvent
+
+ data object StreamMasterMuteChanged : AudioManagerEvent
+
+ data object InternalRingerModeChanged : AudioManagerEvent
+
+ data object StreamDevicesChanged : AudioManagerEvent
+}
+
+/** [AudioManagerEvent] that happens for a specific [AudioStream]. */
+sealed interface StreamAudioManagerEvent : AudioManagerEvent {
+
+ val audioStream: AudioStream
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/shared/model/AudioStream.kt b/packages/SettingsLib/src/com/android/settingslib/volume/shared/model/AudioStream.kt
index 58f3c2d61f3b..9c48299d81be 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/shared/model/AudioStream.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/shared/model/AudioStream.kt
@@ -25,7 +25,7 @@ value class AudioStream(val value: Int) {
require(value in supportedStreamTypes) { "Unsupported stream=$value" }
}
- private companion object {
+ companion object {
val supportedStreamTypes =
setOf(
AudioManager.STREAM_VOICE_CALL,
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java
index 213a66e546ab..1ad7d4930ecc 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java
@@ -22,21 +22,30 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.content.pm.ApplicationInfo;
+import android.os.Flags;
import android.os.UserManager;
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import androidx.test.core.app.ApplicationProvider;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public class ApplicationsStateTest {
+ private static final int APP_ENTRY_ID = 1;
private ApplicationsState.AppEntry mEntry;
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Before
public void setUp() {
- mEntry = mock(ApplicationsState.AppEntry.class);
- mEntry.info = mock(ApplicationInfo.class);
+ mEntry = new ApplicationsState.AppEntry(
+ ApplicationProvider.getApplicationContext(),
+ mock(ApplicationInfo.class),
+ APP_ENTRY_ID);
}
@Test
@@ -310,6 +319,8 @@ public class ApplicationsStateTest {
@Test
public void testPrivateProfileFilterDisplaysCorrectApps() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+
mEntry.showInPersonalTab = true;
mEntry.mProfileType = UserManager.USER_TYPE_FULL_SYSTEM;
assertThat(ApplicationsState.FILTER_PERSONAL.filterApp(mEntry)).isTrue();
@@ -320,4 +331,14 @@ public class ApplicationsStateTest {
assertThat(ApplicationsState.FILTER_PERSONAL.filterApp(mEntry)).isFalse();
assertThat(ApplicationsState.FILTER_PRIVATE_PROFILE.filterApp(mEntry)).isTrue();
}
+
+ @Test
+ public void testPrivateProfileFilterDisplaysCorrectAppsWhenFlagDisabled() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+
+ mEntry.showInPersonalTab = false;
+ mEntry.mProfileType = UserManager.USER_TYPE_PROFILE_PRIVATE;
+ assertThat(ApplicationsState.FILTER_PERSONAL.filterApp(mEntry)).isFalse();
+ assertThat(ApplicationsState.FILTER_PRIVATE_PROFILE.filterApp(mEntry)).isFalse();
+ }
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/OWNERS b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/OWNERS
new file mode 100644
index 000000000000..b7ade19dbb1e
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/OWNERS
@@ -0,0 +1 @@
+include /packages/SettingsLib/src/com/android/settingslib/volume/OWNERS \ No newline at end of file
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt
index 48b04db5b50b..1728a8022ce5 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt
@@ -20,7 +20,8 @@ import android.media.AudioDeviceInfo
import android.media.AudioManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.settingslib.volume.shared.FakeAudioManagerIntentsReceiver
+import com.android.settingslib.volume.shared.FakeAudioManagerEventsReceiver
+import com.android.settingslib.volume.shared.model.AudioManagerEvent
import com.android.settingslib.volume.shared.model.AudioStream
import com.android.settingslib.volume.shared.model.AudioStreamModel
import com.android.settingslib.volume.shared.model.RingerMode
@@ -46,7 +47,6 @@ import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@Suppress("UnspecifiedRegisterReceiverFlag")
@RunWith(AndroidJUnit4::class)
class AudioRepositoryTest {
@@ -59,7 +59,7 @@ class AudioRepositoryTest {
@Mock private lateinit var audioManager: AudioManager
@Mock private lateinit var communicationDevice: AudioDeviceInfo
- private val intentsReceiver = FakeAudioManagerIntentsReceiver()
+ private val eventsReceiver = FakeAudioManagerEventsReceiver()
private val volumeByStream: MutableMap<Int, Int> = mutableMapOf()
private val isAffectedByRingerModeByStream: MutableMap<Int, Boolean> = mutableMapOf()
private val isMuteByStream: MutableMap<Int, Boolean> = mutableMapOf()
@@ -77,12 +77,14 @@ class AudioRepositoryTest {
`when`(audioManager.getStreamMaxVolume(anyInt())).thenReturn(MAX_VOLUME)
`when`(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_NORMAL)
`when`(audioManager.setStreamVolume(anyInt(), anyInt(), anyInt())).then {
- volumeByStream[it.arguments[0] as Int] = it.arguments[1] as Int
- triggerIntent(AudioManager.ACTION_VOLUME_CHANGED)
+ val streamType = it.arguments[1] as Int
+ volumeByStream[it.arguments[0] as Int] = streamType
+ triggerEvent(AudioManagerEvent.StreamVolumeChanged(AudioStream(streamType)))
}
`when`(audioManager.adjustStreamVolume(anyInt(), anyInt(), anyInt())).then {
- isMuteByStream[it.arguments[0] as Int] = it.arguments[2] == AudioManager.ADJUST_MUTE
- triggerIntent(AudioManager.STREAM_MUTE_CHANGED_ACTION)
+ val streamType = it.arguments[0] as Int
+ isMuteByStream[streamType] = it.arguments[2] == AudioManager.ADJUST_MUTE
+ triggerEvent(AudioManagerEvent.StreamMuteChanged(AudioStream(streamType)))
}
`when`(audioManager.getStreamVolume(anyInt())).thenAnswer {
volumeByStream.getOrDefault(it.arguments[0] as Int, 0)
@@ -96,7 +98,7 @@ class AudioRepositoryTest {
underTest =
AudioRepositoryImpl(
- intentsReceiver,
+ eventsReceiver,
audioManager,
testScope.testScheduler,
testScope.backgroundScope,
@@ -125,7 +127,7 @@ class AudioRepositoryTest {
runCurrent()
`when`(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_SILENT)
- triggerIntent(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION)
+ triggerEvent(AudioManagerEvent.InternalRingerModeChanged)
runCurrent()
assertThat(modes)
@@ -179,24 +181,6 @@ class AudioRepositoryTest {
}
@Test
- fun adjustingVolume_currentModeIsUpToDate() {
- testScope.runTest {
- val audioStream = AudioStream(AudioManager.STREAM_SYSTEM)
- var streamModel: AudioStreamModel? = null
- underTest
- .getAudioStream(audioStream)
- .onEach { streamModel = it }
- .launchIn(backgroundScope)
- runCurrent()
-
- underTest.setVolume(audioStream, 50)
- runCurrent()
-
- assertThat(underTest.getCurrentAudioStream(audioStream)).isEqualTo(streamModel)
- }
- }
-
- @Test
fun muteStream_mutesTheStream() {
testScope.runTest {
val audioStream = AudioStream(AudioManager.STREAM_SYSTEM)
@@ -267,8 +251,8 @@ class AudioRepositoryTest {
modeListenerCaptor.value.onModeChanged(mode)
}
- private fun triggerIntent(action: String) {
- testScope.launch { intentsReceiver.triggerIntent(action) }
+ private fun triggerEvent(event: AudioManagerEvent) {
+ testScope.launch { eventsReceiver.triggerEvent(event) }
}
private companion object {
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/LocalMediaRepositoryImplTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/LocalMediaRepositoryImplTest.kt
index dc9ea10a1074..2d12dae36ff1 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/LocalMediaRepositoryImplTest.kt
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/LocalMediaRepositoryImplTest.kt
@@ -23,7 +23,7 @@ import androidx.test.filters.SmallTest
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
import com.android.settingslib.volume.data.model.RoutingSession
-import com.android.settingslib.volume.shared.FakeAudioManagerIntentsReceiver
+import com.android.settingslib.volume.shared.FakeAudioManagerEventsReceiver
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
@@ -58,7 +58,7 @@ class LocalMediaRepositoryImplTest {
@Captor
private lateinit var deviceCallbackCaptor: ArgumentCaptor<LocalMediaManager.DeviceCallback>
- private val intentsReceiver = FakeAudioManagerIntentsReceiver()
+ private val eventsReceiver = FakeAudioManagerEventsReceiver()
private val testScope = TestScope()
private lateinit var underTest: LocalMediaRepository
@@ -69,7 +69,7 @@ class LocalMediaRepositoryImplTest {
underTest =
LocalMediaRepositoryImpl(
- intentsReceiver,
+ eventsReceiver,
localMediaManager,
mediaRouter2Manager,
testScope.backgroundScope,
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/MediaControllerRepositoryImplTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/MediaControllerRepositoryImplTest.kt
index 430d733e4a88..f3d17141334e 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/MediaControllerRepositoryImplTest.kt
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/MediaControllerRepositoryImplTest.kt
@@ -16,7 +16,6 @@
package com.android.settingslib.volume.data.repository
-import android.media.AudioManager
import android.media.session.MediaController
import android.media.session.MediaController.PlaybackInfo
import android.media.session.MediaSessionManager
@@ -26,7 +25,8 @@ import androidx.test.filters.SmallTest
import com.android.settingslib.bluetooth.BluetoothCallback
import com.android.settingslib.bluetooth.BluetoothEventManager
import com.android.settingslib.bluetooth.LocalBluetoothManager
-import com.android.settingslib.volume.shared.FakeAudioManagerIntentsReceiver
+import com.android.settingslib.volume.shared.FakeAudioManagerEventsReceiver
+import com.android.settingslib.volume.shared.model.AudioManagerEvent
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
@@ -66,7 +66,7 @@ class MediaControllerRepositoryImplTest {
@Mock private lateinit var localPlaybackInfo: PlaybackInfo
private val testScope = TestScope()
- private val intentsReceiver = FakeAudioManagerIntentsReceiver()
+ private val eventsReceiver = FakeAudioManagerEventsReceiver()
private lateinit var underTest: MediaControllerRepository
@@ -94,7 +94,7 @@ class MediaControllerRepositoryImplTest {
underTest =
MediaControllerRepositoryImpl(
- intentsReceiver,
+ eventsReceiver,
mediaSessionManager,
localBluetoothManager,
testScope.backgroundScope,
@@ -116,12 +116,12 @@ class MediaControllerRepositoryImplTest {
)
)
var mediaController: MediaController? = null
- underTest.activeMediaController
+ underTest.activeLocalMediaController
.onEach { mediaController = it }
.launchIn(backgroundScope)
runCurrent()
- intentsReceiver.triggerIntent(AudioManager.STREAM_DEVICES_CHANGED_ACTION)
+ eventsReceiver.triggerEvent(AudioManagerEvent.StreamDevicesChanged)
triggerOnAudioModeChanged()
runCurrent()
@@ -141,12 +141,12 @@ class MediaControllerRepositoryImplTest {
)
)
var mediaController: MediaController? = null
- underTest.activeMediaController
+ underTest.activeLocalMediaController
.onEach { mediaController = it }
.launchIn(backgroundScope)
runCurrent()
- intentsReceiver.triggerIntent(AudioManager.STREAM_DEVICES_CHANGED_ACTION)
+ eventsReceiver.triggerEvent(AudioManagerEvent.StreamDevicesChanged)
triggerOnAudioModeChanged()
runCurrent()
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/AudioManagerEventsReceiverTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/AudioManagerEventsReceiverTest.kt
new file mode 100644
index 000000000000..35ee8287d52f
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/AudioManagerEventsReceiverTest.kt
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2024 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.volume.shared
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.media.AudioManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.volume.shared.model.AudioManagerEvent
+import com.android.settingslib.volume.shared.model.AudioStream
+import com.google.common.truth.Expect
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+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.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@Suppress("UnspecifiedRegisterReceiverFlag")
+@RunWith(AndroidJUnit4::class)
+class AudioManagerEventsReceiverTest {
+
+ @JvmField @Rule val expect = Expect.create()
+ private val testScope = TestScope()
+
+ @Mock private lateinit var context: Context
+ @Captor private lateinit var receiverCaptor: ArgumentCaptor<BroadcastReceiver>
+
+ private lateinit var underTest: AudioManagerEventsReceiver
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+
+ underTest = AudioManagerEventsReceiverImpl(context, testScope.backgroundScope)
+ }
+
+ @Test
+ fun validIntent_translatedToEvent() {
+ testScope.runTest {
+ val events = mutableListOf<AudioManagerEvent>()
+ underTest.events.onEach { events.add(it) }.launchIn(backgroundScope)
+ runCurrent()
+
+ triggerIntent(
+ Intent(AudioManager.STREAM_MUTE_CHANGED_ACTION).apply {
+ putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, AudioManager.STREAM_SYSTEM)
+ }
+ )
+ triggerIntent(
+ Intent(AudioManager.VOLUME_CHANGED_ACTION).apply {
+ putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, AudioManager.STREAM_SYSTEM)
+ }
+ )
+ triggerIntent(Intent(AudioManager.MASTER_MUTE_CHANGED_ACTION))
+ triggerIntent(Intent(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION))
+ triggerIntent(Intent(AudioManager.STREAM_DEVICES_CHANGED_ACTION))
+ runCurrent()
+
+ expect
+ .that(events)
+ .containsExactly(
+ AudioManagerEvent.StreamMuteChanged(
+ AudioStream(AudioManager.STREAM_SYSTEM),
+ ),
+ AudioManagerEvent.StreamVolumeChanged(
+ AudioStream(AudioManager.STREAM_SYSTEM),
+ ),
+ AudioManagerEvent.StreamMasterMuteChanged,
+ AudioManagerEvent.InternalRingerModeChanged,
+ AudioManagerEvent.StreamDevicesChanged,
+ )
+ }
+ }
+
+ @Test
+ fun streamAudioManagerEvent_withoutAudioStream_areSkipped() {
+ testScope.runTest {
+ val events = mutableListOf<AudioManagerEvent>()
+ underTest.events.onEach { events.add(it) }.launchIn(backgroundScope)
+ runCurrent()
+
+ triggerIntent(Intent(AudioManager.STREAM_MUTE_CHANGED_ACTION))
+ triggerIntent(Intent(AudioManager.VOLUME_CHANGED_ACTION))
+ runCurrent()
+
+ expect.that(events).isEmpty()
+ }
+ }
+
+ @Test
+ fun invalidIntents_areSkipped() {
+ testScope.runTest {
+ val events = mutableListOf<AudioManagerEvent>()
+ underTest.events.onEach { events.add(it) }.launchIn(backgroundScope)
+ runCurrent()
+
+ triggerIntent(null)
+ triggerIntent(Intent())
+ triggerIntent(Intent("invalid_action"))
+ runCurrent()
+
+ expect.that(events).isEmpty()
+ }
+ }
+
+ private fun triggerIntent(intent: Intent?) {
+ verify(context).registerReceiver(receiverCaptor.capture(), any())
+ receiverCaptor.value.onReceive(context, intent)
+ }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/FakeAudioManagerIntentsReceiver.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/FakeAudioManagerEventsReceiver.kt
index 530690a5faa9..b742df7afc46 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/FakeAudioManagerIntentsReceiver.kt
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/FakeAudioManagerEventsReceiver.kt
@@ -16,21 +16,17 @@
package com.android.settingslib.volume.shared
-import android.content.Intent
+import com.android.settingslib.volume.shared.model.AudioManagerEvent
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow
-class FakeAudioManagerIntentsReceiver : AudioManagerIntentsReceiver {
+class FakeAudioManagerEventsReceiver : AudioManagerEventsReceiver {
- private val mutableIntents = MutableSharedFlow<Intent>()
- override val intents: SharedFlow<Intent> = mutableIntents.asSharedFlow()
+ private val mutableIntents = MutableSharedFlow<AudioManagerEvent>()
+ override val events: SharedFlow<AudioManagerEvent> = mutableIntents.asSharedFlow()
- suspend fun triggerIntent(intent: Intent) {
- mutableIntents.emit(intent)
- }
-
- suspend fun triggerIntent(action: String) {
- triggerIntent(Intent(action))
+ suspend fun triggerEvent(event: AudioManagerEvent) {
+ mutableIntents.emit(event)
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java
index 701f00865da9..7ad54e187ae5 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java
@@ -17,6 +17,7 @@
package com.android.settingslib;
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.doReturn;
@@ -28,6 +29,7 @@ import static org.mockito.Mockito.when;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyResourcesManager;
import android.content.Context;
+import android.content.Intent;
import android.view.View;
import android.widget.TextView;
@@ -87,6 +89,19 @@ public class RestrictedPreferenceHelperTest {
}
@Test
+ public void bindPreference_disabledByEcm_shouldDisplayDisabledSummary() {
+ final TextView summaryView = mock(TextView.class, RETURNS_DEEP_STUBS);
+ when(mViewHolder.itemView.findViewById(android.R.id.summary))
+ .thenReturn(summaryView);
+
+ mHelper.setDisabledByEcm(mock(Intent.class));
+ mHelper.onBindViewHolder(mViewHolder);
+
+ verify(mPreference).setSummary(R.string.disabled_by_app_ops_text);
+ verify(summaryView, never()).setVisibility(View.GONE);
+ }
+
+ @Test
public void bindPreference_notDisabled_shouldNotHideSummary() {
final TextView summaryView = mock(TextView.class, RETURNS_DEEP_STUBS);
when(mViewHolder.itemView.findViewById(android.R.id.summary))
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidStatsLogUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidStatsLogUtilsTest.java
index 8a75bdc7bc35..bd5a022e44e5 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidStatsLogUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidStatsLogUtilsTest.java
@@ -16,6 +16,9 @@
package com.android.settingslib.bluetooth;
+import static com.android.settingslib.bluetooth.HearingAidStatsLogUtils.CONNECTED_HISTORY_EXPIRED_DAY;
+import static com.android.settingslib.bluetooth.HearingAidStatsLogUtils.PAIRED_HISTORY_EXPIRED_DAY;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
@@ -34,6 +37,9 @@ import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.ZoneId;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;
@@ -84,8 +90,8 @@ public class HearingAidStatsLogUtilsTest {
@Test
public void addCurrentTimeToHistory_addNewData() {
- final long currentTime = System.currentTimeMillis();
- final long lastData = currentTime - TimeUnit.DAYS.toMillis(2);
+ final long todayStartOfDay = convertToStartOfDayTime(System.currentTimeMillis());
+ final long lastData = todayStartOfDay - TimeUnit.DAYS.toMillis(6);
HearingAidStatsLogUtils.addToHistory(mContext, TEST_HISTORY_TYPE, lastData);
HearingAidStatsLogUtils.addCurrentTimeToHistory(mContext, TEST_HISTORY_TYPE);
@@ -96,22 +102,21 @@ public class HearingAidStatsLogUtilsTest {
}
@Test
public void addCurrentTimeToHistory_skipSameDateData() {
- final long currentTime = System.currentTimeMillis();
- final long lastData = currentTime - 1;
- HearingAidStatsLogUtils.addToHistory(mContext, TEST_HISTORY_TYPE, lastData);
+ final long todayStartOfDay = convertToStartOfDayTime(System.currentTimeMillis());
+ HearingAidStatsLogUtils.addToHistory(mContext, TEST_HISTORY_TYPE, todayStartOfDay);
HearingAidStatsLogUtils.addCurrentTimeToHistory(mContext, TEST_HISTORY_TYPE);
LinkedList<Long> history = HearingAidStatsLogUtils.getHistory(mContext, TEST_HISTORY_TYPE);
assertThat(history).isNotNull();
assertThat(history.size()).isEqualTo(1);
- assertThat(history.getFirst()).isEqualTo(lastData);
+ assertThat(history.getFirst()).isEqualTo(todayStartOfDay);
}
@Test
public void addCurrentTimeToHistory_cleanUpExpiredData() {
- final long currentTime = System.currentTimeMillis();
- final long expiredData = currentTime - TimeUnit.DAYS.toMillis(10);
+ final long todayStartOfDay = convertToStartOfDayTime(System.currentTimeMillis());
+ final long expiredData = todayStartOfDay - TimeUnit.DAYS.toMillis(6) - 1;
HearingAidStatsLogUtils.addToHistory(mContext, TEST_HISTORY_TYPE, expiredData);
HearingAidStatsLogUtils.addCurrentTimeToHistory(mContext, TEST_HISTORY_TYPE);
@@ -121,4 +126,71 @@ public class HearingAidStatsLogUtilsTest {
assertThat(history.size()).isEqualTo(1);
assertThat(history.getFirst()).isNotEqualTo(expiredData);
}
+
+ @Test
+ public void getUserCategory_hearingAidsUser() {
+ prepareHearingAidsUserHistory();
+
+ assertThat(HearingAidStatsLogUtils.getUserCategory(mContext)).isEqualTo(
+ HearingAidStatsLogUtils.CATEGORY_HEARING_AIDS);
+ }
+
+ @Test
+ public void getUserCategory_newHearingAidsUser() {
+ prepareHearingAidsUserHistory();
+ prepareNewUserHistory();
+
+ assertThat(HearingAidStatsLogUtils.getUserCategory(mContext)).isEqualTo(
+ HearingAidStatsLogUtils.CATEGORY_NEW_HEARING_AIDS);
+ }
+
+ @Test
+ public void getUserCategory_hearingDevicesUser() {
+ prepareHearingDevicesUserHistory();
+
+ assertThat(HearingAidStatsLogUtils.getUserCategory(mContext)).isEqualTo(
+ HearingAidStatsLogUtils.CATEGORY_HEARING_DEVICES);
+ }
+
+ @Test
+ public void getUserCategory_newHearingDevicesUser() {
+ prepareHearingDevicesUserHistory();
+ prepareNewUserHistory();
+
+ assertThat(HearingAidStatsLogUtils.getUserCategory(mContext)).isEqualTo(
+ HearingAidStatsLogUtils.CATEGORY_NEW_HEARING_DEVICES);
+ }
+
+ private long convertToStartOfDayTime(long timestamp) {
+ ZoneId zoneId = ZoneId.systemDefault();
+ LocalDate date = Instant.ofEpochMilli(timestamp).atZone(zoneId).toLocalDate();
+ return date.atStartOfDay(zoneId).toInstant().toEpochMilli();
+ }
+
+ private void prepareHearingAidsUserHistory() {
+ final long todayStartOfDay = convertToStartOfDayTime(System.currentTimeMillis());
+ for (int i = CONNECTED_HISTORY_EXPIRED_DAY - 1; i >= 0; i--) {
+ final long data = todayStartOfDay - TimeUnit.DAYS.toMillis(i);
+ HearingAidStatsLogUtils.addToHistory(mContext,
+ HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_AIDS_CONNECTED, data);
+ }
+ }
+
+ private void prepareHearingDevicesUserHistory() {
+ final long todayStartOfDay = convertToStartOfDayTime(System.currentTimeMillis());
+ for (int i = CONNECTED_HISTORY_EXPIRED_DAY - 1; i >= 0; i--) {
+ final long data = todayStartOfDay - TimeUnit.DAYS.toMillis(i);
+ HearingAidStatsLogUtils.addToHistory(mContext,
+ HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_DEVICES_CONNECTED, data);
+ }
+ }
+
+ private void prepareNewUserHistory() {
+ final long todayStartOfDay = convertToStartOfDayTime(System.currentTimeMillis());
+ final long data = todayStartOfDay - TimeUnit.DAYS.toMillis(PAIRED_HISTORY_EXPIRED_DAY - 1);
+ HearingAidStatsLogUtils.addToHistory(mContext,
+ HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_AIDS_PAIRED, data);
+ HearingAidStatsLogUtils.addToHistory(mContext,
+ HearingAidStatsLogUtils.HistoryType.TYPE_HEARING_DEVICES_PAIRED, data);
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java
index 5669276a0424..8edda1a1f3a2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java
@@ -90,7 +90,7 @@ public class DeviceIconUtilTest {
public void getIconResIdFromMediaRouteType_hdmi() {
assertThat(new DeviceIconUtil(/* isTv */ false)
.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_HDMI))
- .isEqualTo(R.drawable.ic_headphone);
+ .isEqualTo(R.drawable.ic_external_display);
}
@Test
@@ -101,10 +101,10 @@ public class DeviceIconUtilTest {
}
@Test
- public void getIconResIdFromMediaRouteType_hdmiArc_isHeadphone() {
+ public void getIconResIdFromMediaRouteType_hdmiArc_isExternalDisplay() {
assertThat(new DeviceIconUtil(/* isTv */ false)
.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_HDMI_ARC))
- .isEqualTo(R.drawable.ic_headphone);
+ .isEqualTo(R.drawable.ic_external_display);
}
@Test
@@ -115,10 +115,10 @@ public class DeviceIconUtilTest {
}
@Test
- public void getIconResIdFromMediaRouteType_hdmiEarc_isHeadphone() {
+ public void getIconResIdFromMediaRouteType_hdmiEarc_isExternalDisplay() {
assertThat(new DeviceIconUtil(/* isTv */ false)
.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_HDMI_EARC))
- .isEqualTo(R.drawable.ic_headphone);
+ .isEqualTo(R.drawable.ic_external_display);
}
@Test
@@ -229,10 +229,10 @@ public class DeviceIconUtilTest {
}
@Test
- public void getIconResIdFromAudioDeviceType_hdmi_isHeadphone() {
+ public void getIconResIdFromAudioDeviceType_hdmi_isExternalDisplay() {
assertThat(new DeviceIconUtil(/* isTv */ false)
.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_HDMI))
- .isEqualTo(R.drawable.ic_headphone);
+ .isEqualTo(R.drawable.ic_external_display);
}
@Test
@@ -243,10 +243,10 @@ public class DeviceIconUtilTest {
}
@Test
- public void getIconResIdFromAudioDeviceType_hdmiArc_isHeadphone() {
+ public void getIconResIdFromAudioDeviceType_hdmiArc_isExternalDisplay() {
assertThat(new DeviceIconUtil(/* isTv */ false)
.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_HDMI_ARC))
- .isEqualTo(R.drawable.ic_headphone);
+ .isEqualTo(R.drawable.ic_external_display);
}
@Test
@@ -257,10 +257,10 @@ public class DeviceIconUtilTest {
}
@Test
- public void getIconResIdFromAudioDeviceType_hdmiEarc_isHeadphone() {
+ public void getIconResIdFromAudioDeviceType_hdmiEarc_isExternalDisplay() {
assertThat(new DeviceIconUtil(/* isTv */ false)
.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_HDMI_EARC))
- .isEqualTo(R.drawable.ic_headphone);
+ .isEqualTo(R.drawable.ic_external_display);
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index f0330c46315c..c159d5ee37f0 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -30,6 +30,7 @@ import static com.android.settingslib.media.LocalMediaManager.MediaDeviceState.S
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -124,7 +125,11 @@ public class InfoMediaManagerTest {
@Test
public void stopScan_startFirst_callsUnregister() {
+ RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
mInfoMediaManager.mRouterManager = mRouterManager;
+ // Since test is running in Robolectric, return a fake session to avoid NPE.
+ when(mRouterManager.getRoutingSessions(anyString())).thenReturn(List.of(sessionInfo));
+
mInfoMediaManager.startScan();
mInfoMediaManager.stopScan();
@@ -212,28 +217,6 @@ public class InfoMediaManagerTest {
}
@Test
- public void onRouteAdded_buildAllRoutes_shouldAddMediaDevice() {
- final MediaRoute2Info info = mock(MediaRoute2Info.class);
- when(info.getId()).thenReturn(TEST_ID);
- when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
- when(info.isSystemRoute()).thenReturn(true);
-
- final List<MediaRoute2Info> routes = new ArrayList<>();
- routes.add(info);
- mShadowRouter2Manager.setAllRoutes(routes);
-
- final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
- assertThat(mediaDevice).isNull();
-
- mInfoMediaManager.mPackageName = "";
- mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated();
-
- final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
- assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
- assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
- }
-
- @Test
public void onPreferredFeaturesChanged_samePackageName_shouldAddMediaDevice() {
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
@@ -436,29 +419,6 @@ public class InfoMediaManagerTest {
}
@Test
- public void onRoutesChanged_buildAllRoutes_shouldAddMediaDevice() {
- final MediaRoute2Info info = mock(MediaRoute2Info.class);
- when(info.getId()).thenReturn(TEST_ID);
- when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
- when(info.isSystemRoute()).thenReturn(true);
- when(info.getDeduplicationIds()).thenReturn(Set.of());
-
- final List<MediaRoute2Info> routes = new ArrayList<>();
- routes.add(info);
- mShadowRouter2Manager.setAllRoutes(routes);
-
- final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
- assertThat(mediaDevice).isNull();
-
- mInfoMediaManager.mPackageName = "";
- mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated();
-
- final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
- assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
- assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
- }
-
- @Test
public void hasPreferenceRouteListing_oldSdkVersion_returnsFalse() {
assertThat(mInfoMediaManager.preferRouteListingOrdering()).isFalse();
}
@@ -545,18 +505,6 @@ public class InfoMediaManagerTest {
}
@Test
- public void connectDeviceWithoutPackageName_noSession_returnFalse() {
- final MediaRoute2Info info = mock(MediaRoute2Info.class);
- final MediaDevice device = new InfoMediaDevice(mContext, info);
-
- final List<RoutingSessionInfo> infos = new ArrayList<>();
-
- mShadowRouter2Manager.setRemoteSessions(infos);
-
- assertThat(mInfoMediaManager.connectDeviceWithoutPackageName(device)).isFalse();
- }
-
- @Test
public void onRoutesRemoved_getAvailableRoutes_shouldAddMediaDevice() {
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
@@ -587,36 +535,6 @@ public class InfoMediaManagerTest {
}
@Test
- public void onRoutesRemoved_buildAllRoutes_shouldAddMediaDevice() {
- final MediaRoute2Info info = mock(MediaRoute2Info.class);
- when(info.getId()).thenReturn(TEST_ID);
- when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
- when(info.isSystemRoute()).thenReturn(true);
-
- final List<MediaRoute2Info> routes = new ArrayList<>();
- routes.add(info);
- when(mRouterManager.getAllRoutes()).thenReturn(routes);
-
- final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
- assertThat(mediaDevice).isNull();
-
- mInfoMediaManager.mPackageName = "";
- mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated();
-
- final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
- assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
- assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
- }
-
- @Test
- public void addDeviceToPlayMedia_packageNameIsNull_returnFalse() {
- mInfoMediaManager.mPackageName = null;
- final MediaDevice device = mock(MediaDevice.class);
-
- assertThat(mInfoMediaManager.addDeviceToPlayMedia(device)).isFalse();
- }
-
- @Test
public void addDeviceToPlayMedia_containSelectableRoutes_returnTrue() {
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
@@ -660,14 +578,6 @@ public class InfoMediaManagerTest {
}
@Test
- public void removeDeviceFromMedia_packageNameIsNull_returnFalse() {
- mInfoMediaManager.mPackageName = null;
- final MediaDevice device = mock(MediaDevice.class);
-
- assertThat(mInfoMediaManager.removeDeviceFromPlayMedia(device)).isFalse();
- }
-
- @Test
public void removeDeviceFromMedia_containSelectedRoutes_returnTrue() {
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
@@ -711,13 +621,6 @@ public class InfoMediaManagerTest {
}
@Test
- public void getSelectableMediaDevice_packageNameIsNull_returnFalse() {
- mInfoMediaManager.mPackageName = null;
-
- assertThat(mInfoMediaManager.getSelectableMediaDevices()).isEmpty();
- }
-
- @Test
public void getSelectableMediaDevice_notContainPackageName_returnEmpty() {
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
@@ -730,13 +633,6 @@ public class InfoMediaManagerTest {
}
@Test
- public void getDeselectableMediaDevice_packageNameIsNull_returnFalse() {
- mInfoMediaManager.mPackageName = null;
-
- assertThat(mInfoMediaManager.getDeselectableMediaDevices()).isEmpty();
- }
-
- @Test
public void getDeselectableMediaDevice_checkList() {
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
@@ -761,20 +657,6 @@ public class InfoMediaManagerTest {
}
@Test
- public void adjustSessionVolume_packageNameIsNull_noCrash() {
- mInfoMediaManager.mPackageName = null;
-
- mInfoMediaManager.adjustSessionVolume(10);
- }
-
- @Test
- public void getSessionVolumeMax_packageNameIsNull_returnNotFound() {
- mInfoMediaManager.mPackageName = null;
-
- assertThat(mInfoMediaManager.getSessionVolumeMax()).isEqualTo(-1);
- }
-
- @Test
public void getSessionVolumeMax_containPackageName_returnMaxVolume() {
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
@@ -789,24 +671,6 @@ public class InfoMediaManagerTest {
}
@Test
- public void getSessionVolumeMax_routeSessionInfoIsNull_returnNotFound() {
- final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
- final RoutingSessionInfo info = null;
- routingSessionInfos.add(info);
-
- mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
-
- assertThat(mInfoMediaManager.getSessionVolumeMax()).isEqualTo(-1);
- }
-
- @Test
- public void getSessionVolume_packageNameIsNull_returnNotFound() {
- mInfoMediaManager.mPackageName = null;
-
- assertThat(mInfoMediaManager.getSessionVolume()).isEqualTo(-1);
- }
-
- @Test
public void getSessionVolume_containPackageName_returnMaxVolume() {
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
@@ -821,17 +685,6 @@ public class InfoMediaManagerTest {
}
@Test
- public void getSessionVolume_routeSessionInfoIsNull_returnNotFound() {
- final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
- final RoutingSessionInfo info = null;
- routingSessionInfos.add(info);
-
- mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
-
- assertThat(mInfoMediaManager.getSessionVolume()).isEqualTo(-1);
- }
-
- @Test
public void getRemoteSessions_returnsRemoteSessions() {
final List<RoutingSessionInfo> infos = new ArrayList<>();
infos.add(mock(RoutingSessionInfo.class));
@@ -841,13 +694,6 @@ public class InfoMediaManagerTest {
}
@Test
- public void releaseSession_packageNameIsNull_returnFalse() {
- mInfoMediaManager.mPackageName = null;
-
- assertThat(mInfoMediaManager.releaseSession()).isFalse();
- }
-
- @Test
public void releaseSession_removeSuccessfully_returnTrue() {
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
@@ -860,24 +706,6 @@ public class InfoMediaManagerTest {
}
@Test
- public void getSessionName_packageNameIsNull_returnNull() {
- mInfoMediaManager.mPackageName = null;
-
- assertThat(mInfoMediaManager.getSessionName()).isNull();
- }
-
- @Test
- public void getSessionName_routeSessionInfoIsNull_returnNull() {
- final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
- final RoutingSessionInfo info = null;
- routingSessionInfos.add(info);
-
- mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
-
- assertThat(mInfoMediaManager.getSessionName()).isNull();
- }
-
- @Test
public void getSessionName_containPackageName_returnName() {
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
@@ -942,32 +770,6 @@ public class InfoMediaManagerTest {
}
@Test
- public void onTransferred_buildAllRoutes_shouldAddMediaDevice() {
- final MediaRoute2Info info = mock(MediaRoute2Info.class);
- final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
- mInfoMediaManager.registerCallback(mCallback);
-
- when(info.getId()).thenReturn(TEST_ID);
- when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
- when(info.isSystemRoute()).thenReturn(true);
-
- final List<MediaRoute2Info> routes = new ArrayList<>();
- routes.add(info);
- mShadowRouter2Manager.setAllRoutes(routes);
-
- final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
- assertThat(mediaDevice).isNull();
-
- mInfoMediaManager.mPackageName = "";
- mInfoMediaManager.mMediaRouterCallback.onTransferred(sessionInfo, sessionInfo);
-
- final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
- assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
- assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
- verify(mCallback).onConnectedDeviceChanged(null);
- }
-
- @Test
public void onSessionUpdated_shouldDispatchDeviceListAdded() {
final MediaRoute2Info info = mock(MediaRoute2Info.class);
when(info.getId()).thenReturn(TEST_ID);
@@ -978,7 +780,6 @@ public class InfoMediaManagerTest {
routes.add(info);
mShadowRouter2Manager.setAllRoutes(routes);
- mInfoMediaManager.mPackageName = "";
mInfoMediaManager.registerCallback(mCallback);
mInfoMediaManager.mMediaRouterCallback.onSessionUpdated(mock(RoutingSessionInfo.class));
diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java
index fde378fc3e5e..3adb20409bd9 100644
--- a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java
+++ b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java
@@ -27,12 +27,13 @@ import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.shadow.api.Shadow;
+import java.util.ArrayList;
import java.util.List;
@Implements(MediaRouter2Manager.class)
public class ShadowRouter2Manager {
- private List<MediaRoute2Info> mAvailableRoutes;
+ private List<MediaRoute2Info> mAvailableRoutes = new ArrayList<>();
private List<MediaRoute2Info> mAllRoutes;
private List<MediaRoute2Info> mDeselectableRoutes;
private List<RoutingSessionInfo> mRemoteSessions;
diff --git a/packages/SettingsLib/tests/unit/src/com/android/settingslib/bluetooth/BluetoothLeBroadcastMetadataExtTest.kt b/packages/SettingsLib/tests/unit/src/com/android/settingslib/bluetooth/BluetoothLeBroadcastMetadataExtTest.kt
index 27d7078774d5..1ad20dc02042 100644
--- a/packages/SettingsLib/tests/unit/src/com/android/settingslib/bluetooth/BluetoothLeBroadcastMetadataExtTest.kt
+++ b/packages/SettingsLib/tests/unit/src/com/android/settingslib/bluetooth/BluetoothLeBroadcastMetadataExtTest.kt
@@ -32,7 +32,7 @@ import org.junit.runner.RunWith
class BluetoothLeBroadcastMetadataExtTest {
@Test
- fun toQrCodeString() {
+ fun toQrCodeString_encrypted() {
val subgroup = BluetoothLeBroadcastSubgroup.Builder().apply {
setCodecId(0x6)
val audioCodecConfigMetadata = BluetoothLeAudioCodecConfigMetadata.Builder().build()
@@ -70,6 +70,37 @@ class BluetoothLeBroadcastMetadataExtTest {
}
@Test
+ fun toQrCodeString_non_encrypted() {
+ val subgroup = BluetoothLeBroadcastSubgroup.Builder().apply {
+ setCodecId(0x6)
+ val audioCodecConfigMetadata = BluetoothLeAudioCodecConfigMetadata.Builder().build()
+ setContentMetadata(BluetoothLeAudioContentMetadata.Builder()
+ .build())
+ setCodecSpecificConfig(audioCodecConfigMetadata)
+ addChannel(BluetoothLeBroadcastChannel.Builder().apply {
+ setSelected(true)
+ setChannelIndex(1)
+ setCodecMetadata(audioCodecConfigMetadata)
+ }.build())
+ }.build()
+
+ val metadata = BluetoothLeBroadcastMetadata.Builder().apply {
+ setSourceDevice(DevicePublic, BluetoothDevice.ADDRESS_TYPE_PUBLIC)
+ setSourceAdvertisingSid(1)
+ setBroadcastId(0xDE51E9)
+ setBroadcastName("Hockey")
+ setAudioConfigQuality(BluetoothLeBroadcastMetadata.AUDIO_CONFIG_QUALITY_STANDARD)
+ setPaSyncInterval(0xFFFF)
+ setEncrypted(false)
+ addSubgroup(subgroup)
+ }.build()
+
+ val qrCodeString = metadata.toQrCodeString()
+
+ assertThat(qrCodeString).isEqualTo(QR_CODE_STRING_NON_ENCRYPTED)
+ }
+
+ @Test
fun toQrCodeString_NoChannelSelected() {
val subgroup = BluetoothLeBroadcastSubgroup.Builder().apply {
setCodecId(0x6)
@@ -102,6 +133,7 @@ class BluetoothLeBroadcastMetadataExtTest {
addSubgroup(subgroup)
}.build()
+ // if no channel is selected, no preference(0xFFFFFFFFu) will be set in BIS
val qrCodeString = metadata.toQrCodeString()
val parsedMetadata =
@@ -111,13 +143,11 @@ class BluetoothLeBroadcastMetadataExtTest {
assertThat(parsedMetadata.subgroups).isNotNull()
assertThat(parsedMetadata.subgroups.size).isEqualTo(1)
assertThat(parsedMetadata.subgroups[0].channels).isNotNull()
- assertThat(parsedMetadata.subgroups[0].channels.size).isEqualTo(2)
+ assertThat(parsedMetadata.subgroups[0].channels.size).isEqualTo(1)
assertThat(parsedMetadata.subgroups[0].hasChannelPreference()).isFalse()
- // Input order does not matter due to parsing through bisMask
+ // placeholder channel with not selected
assertThat(parsedMetadata.subgroups[0].channels[0].channelIndex).isEqualTo(1)
assertThat(parsedMetadata.subgroups[0].channels[0].isSelected).isFalse()
- assertThat(parsedMetadata.subgroups[0].channels[1].channelIndex).isEqualTo(2)
- assertThat(parsedMetadata.subgroups[0].channels[1].isSelected).isFalse()
}
@Test
@@ -162,13 +192,11 @@ class BluetoothLeBroadcastMetadataExtTest {
assertThat(parsedMetadata.subgroups).isNotNull()
assertThat(parsedMetadata.subgroups.size).isEqualTo(1)
assertThat(parsedMetadata.subgroups[0].channels).isNotNull()
- // Only selected channel can be recovered
- assertThat(parsedMetadata.subgroups[0].channels.size).isEqualTo(2)
+ // Only selected channel can be recovered, non-selected ones will be ignored
+ assertThat(parsedMetadata.subgroups[0].channels.size).isEqualTo(1)
assertThat(parsedMetadata.subgroups[0].hasChannelPreference()).isTrue()
- assertThat(parsedMetadata.subgroups[0].channels[0].channelIndex).isEqualTo(1)
- assertThat(parsedMetadata.subgroups[0].channels[0].isSelected).isFalse()
- assertThat(parsedMetadata.subgroups[0].channels[1].channelIndex).isEqualTo(2)
- assertThat(parsedMetadata.subgroups[0].channels[1].isSelected).isTrue()
+ assertThat(parsedMetadata.subgroups[0].channels[0].channelIndex).isEqualTo(2)
+ assertThat(parsedMetadata.subgroups[0].channels[0].isSelected).isTrue()
}
@Test
@@ -180,16 +208,34 @@ class BluetoothLeBroadcastMetadataExtTest {
assertThat(qrCodeString).isEqualTo(QR_CODE_STRING)
}
+ @Test
+ fun decodeAndEncodeAgain_sameString_non_encrypted() {
+ val metadata =
+ BluetoothLeBroadcastMetadataExt
+ .convertToBroadcastMetadata(QR_CODE_STRING_NON_ENCRYPTED)!!
+
+ val qrCodeString = metadata.toQrCodeString()
+
+ assertThat(qrCodeString).isEqualTo(QR_CODE_STRING_NON_ENCRYPTED)
+ }
+
private companion object {
const val TEST_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1"
+ const val TEST_DEVICE_ADDRESS_PUBLIC = "AA:BB:CC:00:11:22"
val Device: BluetoothDevice =
BluetoothAdapter.getDefaultAdapter().getRemoteLeDevice(TEST_DEVICE_ADDRESS,
BluetoothDevice.ADDRESS_TYPE_RANDOM)
+ val DevicePublic: BluetoothDevice =
+ BluetoothAdapter.getDefaultAdapter().getRemoteLeDevice(TEST_DEVICE_ADDRESS_PUBLIC,
+ BluetoothDevice.ADDRESS_TYPE_PUBLIC)
+
const val QR_CODE_STRING =
- "BT:R:65536;T:1;D:00-A1-A1-A1-A1-A1;AS:1;B:123456;BN:VGVzdA==;" +
- "PM:BgNwVGVzdA==;SI:160;C:VGVzdENvZGU=;SG:BS:3,BM:3,AC:BQNUZXN0BARlbmc=;" +
- "VN:U;;"
+ "BLUETOOTH:UUID:184F;BN:VGVzdA==;AT:1;AD:00A1A1A1A1A1;BI:1E240;BC:VGVzdENvZGU=;" +
+ "MD:BgNwVGVzdA==;AS:1;PI:A0;NS:1;BS:3;NB:2;SM:BQNUZXN0BARlbmc=;;"
+ const val QR_CODE_STRING_NON_ENCRYPTED =
+ "BLUETOOTH:UUID:184F;BN:SG9ja2V5;AT:0;AD:AABBCC001122;BI:DE51E9;SQ:1;AS:1;PI:FFFF;" +
+ "NS:1;BS:1;NB:1;;"
}
} \ No newline at end of file
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
index cd35f67a1369..be480b941586 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
@@ -308,11 +308,8 @@ final class GenerationRegistry {
final long token = proto.start(GenerationRegistryProto.BACKING_STORES);
final int key = mKeyToBackingStoreMap.keyAt(i);
proto.write(BackingStoreProto.KEY, key);
- try {
- proto.write(BackingStoreProto.BACKING_STORE_SIZE,
- mKeyToBackingStoreMap.valueAt(i).size());
- } catch (IOException ignore) {
- }
+ proto.write(BackingStoreProto.BACKING_STORE_SIZE,
+ mKeyToBackingStoreMap.valueAt(i).size());
proto.write(BackingStoreProto.NUM_CACHED_ENTRIES,
mKeyToIndexMapMap.get(key).size());
final ArrayMap<String, Integer> indexMap = mKeyToIndexMapMap.get(key);
@@ -357,10 +354,7 @@ final class GenerationRegistry {
pw.print("_Backing store for type:"); pw.print(SettingsState.settingTypeToString(
SettingsState.getTypeFromKey(key)));
pw.print(" user:"); pw.print(SettingsState.getUserIdFromKey(key));
- try {
- pw.print(" size:" + mKeyToBackingStoreMap.valueAt(i).size());
- } catch (IOException ignore) {
- }
+ pw.print(" size:" + mKeyToBackingStoreMap.valueAt(i).size());
pw.println(" cachedEntries:" + mKeyToIndexMapMap.get(key).size());
final ArrayMap<String, Integer> indexMap = mKeyToIndexMapMap.get(key);
final MemoryIntArray backingStore = getBackingStoreLocked(key,
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index b58187d8e95e..28cdc6db192b 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -807,7 +807,9 @@ public class SettingsBackupTest {
Settings.Secure.UI_TRANSLATION_ENABLED,
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_EDGE_HAPTIC_ENABLED,
Settings.Secure.DND_CONFIGS_MIGRATED,
- Settings.Secure.NAVIGATION_MODE_RESTORE);
+ Settings.Secure.NAVIGATION_MODE_RESTORE,
+ Settings.Secure.V_TO_U_RESTORE_ALLOWLIST,
+ Settings.Secure.V_TO_U_RESTORE_DENYLIST);
@Test
public void systemSettingsBackedUpOrDenied() {
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 4305d912e806..53f2caf0b793 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -771,6 +771,9 @@
<!-- Permission required for CTS test - CtsDevicePolicyManagerTestCases -->
<uses-permission android:name="android.permission.READ_NEARBY_STREAMING_POLICY" />
+ <!-- Permission required for CTS test - CtsDevicePolicyTestCases -->
+ <uses-permission android:name="android.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING" />
+
<!-- Permission required for CTS test - CtsKeystoreTestCases -->
<uses-permission android:name="android.permission.REQUEST_UNIQUE_ID_ATTESTATION" />
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index d1a3571a87c1..bed95a584c27 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -550,5 +550,6 @@ android_app {
required: [
"privapp_whitelist_com.android.systemui",
"wmshell.protolog.json.gz",
+ "wmshell.protolog.pb",
],
}
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/TEST_MAPPING b/packages/SystemUI/accessibility/accessibilitymenu/TEST_MAPPING
index 29a25ad04cbe..4a10108b3e04 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/TEST_MAPPING
+++ b/packages/SystemUI/accessibility/accessibilitymenu/TEST_MAPPING
@@ -1,14 +1,5 @@
{
- "presubmit": [
- {
- "name": "AccessibilityMenuServiceTests",
- "options": [
- {
- "exclude-annotation": "android.support.test.filters.FlakyTest"
- }
- ]
- }
- ],
+ // TODO: b/324945360 - Re-enable on presubmit after fixing failures
"postsubmit": [
{
"name": "AccessibilityMenuServiceTests",
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/aconfig/Android.bp b/packages/SystemUI/accessibility/accessibilitymenu/aconfig/Android.bp
index 6d63409ffc06..f74e59abeca5 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/aconfig/Android.bp
+++ b/packages/SystemUI/accessibility/accessibilitymenu/aconfig/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_",
+}
+
aconfig_declarations {
name: "com_android_a11y_menu_flags",
package: "com.android.systemui.accessibility.accessibilitymenu",
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-sv/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-sv/strings.xml
index ef6966707fda..c02bbb2643d5 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-sv/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-sv/strings.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="accessibility_menu_service_name" msgid="730136711554740131">"Tillgänglighetsmenyn"</string>
+ <string name="accessibility_menu_service_name" msgid="730136711554740131">"Till- gänglighetsmeny"</string>
<string name="accessibility_menu_intro" msgid="3164193281544042394">"Tillgänglighetsmenyn är en stor meny på skärmen som du kan styra enheten med. Du kan låsa enheten, ställa in volym och ljusstyrka, ta skärmbilder och annat."</string>
<string name="assistant_label" msgid="6796392082252272356">"Assistent"</string>
<string name="assistant_utterance" msgid="65509599221141377">"Assistent"</string>
diff --git a/packages/SystemUI/aconfig/Android.bp b/packages/SystemUI/aconfig/Android.bp
index 03f9d74a4a34..76cb6bd91714 100644
--- a/packages/SystemUI/aconfig/Android.bp
+++ b/packages/SystemUI/aconfig/Android.bp
@@ -20,11 +20,13 @@
*/
package {
+ default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_",
default_visibility: [
"//visibility:override",
"//frameworks/base/packages/SystemUI:__subpackages__",
"//frameworks/libs/systemui/tracinglib:__subpackages__",
"//platform_testing:__subpackages__",
+ "//vendor:__subpackages__",
"//cts:__subpackages__",
],
}
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 6f34f05447a9..a69a2a64dccb 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -15,6 +15,16 @@ flag {
}
flag {
+ name: "udfps_view_performance"
+ namespace: "systemui"
+ description: "Decrease screen off blocking calls by waiting until the device is finished going to sleep before adding the udfps view."
+ bug: "225183106"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "notification_async_group_header_inflation"
namespace: "systemui"
description: "Inflates the notification group summary header views from the background thread."
@@ -29,6 +39,13 @@ flag {
}
flag {
+ name: "notification_color_update_logger"
+ namespace: "systemui"
+ description: "Enabled debug logging and dumping of notification color updates."
+ bug: "294347738"
+}
+
+flag {
name: "notifications_footer_view_refactor"
namespace: "systemui"
description: "Enables the refactored version of the footer view in the notification shade "
@@ -67,6 +84,16 @@ flag {
}
flag {
+ name: "notifications_background_media_icons"
+ namespace: "systemui"
+ description: "Updates icons for media notifications in the background."
+ bug: "315143160"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "nssl_falsing_fix"
namespace: "systemui"
description: "Minor touch changes to prevent falsing errors in NSSL"
@@ -295,12 +322,15 @@ flag {
}
flag {
- name: "centralized_status_bar_dimens_refactor"
+ name: "centralized_status_bar_height_fix"
namespace: "systemui"
description: "Refactors shade header and keyguard status bar to read status bar dimens from a"
" central place, instead of reading resources directly. This is to take into account display"
" cutouts and other special cases. "
- bug: "317199366"
+ bug: "317016114"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
flag {
@@ -347,6 +377,13 @@ flag {
}
flag {
+ name: "screenshot_action_dismiss_system_windows"
+ namespace: "systemui"
+ description: "Dismiss existing system windows when starting action from screenshot UI"
+ bug: "309933761"
+}
+
+flag {
name: "run_fingerprint_detect_on_dismissible_keyguard"
namespace: "systemui"
description: "Run fingerprint detect instead of authenticate if the keyguard is dismissible."
@@ -431,6 +468,16 @@ flag {
}
flag {
+ name: "slice_manager_binder_call_background"
+ namespace: "systemui"
+ description: "Move the ISliceManager#getPinnedSpecs binder call to the background thread."
+ bug: "322745650"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "register_new_wallet_card_in_background"
namespace: "systemui"
description: "Decide whether the call to registerNewWalletCards method should be issued on background thread."
@@ -450,3 +497,52 @@ flag {
}
}
+flag {
+ name: "register_zen_mode_content_observer_background"
+ namespace: "systemui"
+ description: "Decide whether to register zen mode content observers in the background thread."
+ bug: "324515627"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "clipboard_noninteractive_on_lockscreen"
+ namespace: "systemui"
+ description: "Prevents the interactive clipboard UI from appearing when device is locked"
+ bug: "317048495"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "trim_resources_with_background_trim_at_lock"
+ namespace: "systemui"
+ description: "Trim fonts and other caches when the device locks to lower memory consumption."
+ bug: "322143614"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "dedicated_notif_inflation_thread"
+ namespace: "systemui"
+ description: "Create a separate background thread for inflating notifications"
+ bug: "308967184"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "bind_keyguard_media_visibility"
+ namespace: "systemui"
+ description: "Binds Keyguard Media Controller Visibility to MediaContainerView"
+ bug: "298213983"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt
index e4dc9beb51f8..5d5f12e8e567 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt
@@ -123,7 +123,7 @@ constructor(
val views = LinkedList<View>().apply { add(view) }
while (views.isNotEmpty()) {
- val v = views.removeFirst()
+ val v = views.removeAt(0)
if (v.background != null) {
return v.background
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectView.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectView.kt
new file mode 100644
index 000000000000..aad593eb6a05
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectView.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 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.surfaceeffects.loadingeffect
+
+import android.content.Context
+import android.graphics.BlendMode
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.util.AttributeSet
+import android.view.View
+
+/** Custom View for drawing the [LoadingEffect] with [Canvas.drawPaint]. */
+open class LoadingEffectView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
+
+ private var paint: Paint? = null
+ private var blendMode: BlendMode = BlendMode.SRC_OVER
+
+ override fun onDraw(canvas: Canvas) {
+ if (!canvas.isHardwareAccelerated) {
+ return
+ }
+ paint?.let { canvas.drawPaint(it) }
+ }
+
+ /** Designed to be called on [LoadingEffect.PaintDrawCallback.onDraw]. */
+ fun draw(paint: Paint) {
+ this.paint = paint
+ this.paint!!.blendMode = blendMode
+
+ invalidate()
+ }
+
+ /** Sets the blend mode of the [Paint]. */
+ fun setBlendMode(blendMode: BlendMode) {
+ this.blendMode = blendMode
+ }
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt
index 535c2d32ed09..e862f0c43a58 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseController.kt
@@ -17,7 +17,6 @@ package com.android.systemui.surfaceeffects.turbulencenoise
import android.view.View
import androidx.annotation.VisibleForTesting
-import java.util.Random
/** Plays [TurbulenceNoiseView] in ease-in, main (no easing), and ease-out order. */
class TurbulenceNoiseController(private val turbulenceNoiseView: TurbulenceNoiseView) {
@@ -37,8 +36,6 @@ class TurbulenceNoiseController(private val turbulenceNoiseView: TurbulenceNoise
}
}
- private val random = Random()
-
/** Current state of the animation. */
@VisibleForTesting
var state: AnimationState = AnimationState.NOT_PLAYING
@@ -95,12 +92,7 @@ class TurbulenceNoiseController(private val turbulenceNoiseView: TurbulenceNoise
}
state = AnimationState.EASE_IN
- // Add offset to avoid repetitive noise.
- turbulenceNoiseView.playEaseIn(
- offsetX = random.nextFloat(),
- offsetY = random.nextFloat(),
- this::playMainAnimation
- )
+ turbulenceNoiseView.playEaseIn(this::playMainAnimation)
}
private fun playMainAnimation() {
diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt
index c59bc106ca91..5e72e3bd1e39 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt
@@ -109,7 +109,7 @@ class TurbulenceNoiseView(context: Context?, attrs: AttributeSet?) : View(contex
/** Plays the turbulence noise with linear ease-in. */
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
- fun playEaseIn(offsetX: Float = 0f, offsetY: Float = 0f, onAnimationEnd: Runnable? = null) {
+ fun playEaseIn(onAnimationEnd: Runnable? = null) {
if (noiseConfig == null) {
return
}
@@ -129,8 +129,8 @@ class TurbulenceNoiseView(context: Context?, attrs: AttributeSet?) : View(contex
val progress = updateListener.animatedValue as Float
shader.setNoiseMove(
- offsetX + initialX + timeInSec * config.noiseMoveSpeedX,
- offsetY + initialY + timeInSec * config.noiseMoveSpeedY,
+ initialX + timeInSec * config.noiseMoveSpeedX,
+ initialY + timeInSec * config.noiseMoveSpeedY,
initialZ + timeInSec * config.noiseMoveSpeedZ
)
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/PlatformSlider.kt b/packages/SystemUI/compose/core/src/com/android/compose/PlatformSlider.kt
index b8c4fae975af..62dd4ac8c230 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/PlatformSlider.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/PlatformSlider.kt
@@ -57,9 +57,6 @@ import androidx.compose.ui.unit.dp
import com.android.compose.modifiers.padding
import com.android.compose.theme.LocalAndroidColorScheme
-/** Indicator corner radius used when the user drags the [PlatformSlider]. */
-private val DefaultPlatformSliderDraggingCornerRadius = 8.dp
-
/**
* Platform slider implementation that displays a slider with an [icon] and a [label] at the start.
*
@@ -83,10 +80,8 @@ fun PlatformSlider(
valueRange: ClosedFloatingPointRange<Float> = 0f..1f,
enabled: Boolean = true,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
- colors: PlatformSliderColors =
- if (isSystemInDarkTheme()) darkThemePlatformSliderColors()
- else lightThemePlatformSliderColors(),
- draggingCornersRadius: Dp = DefaultPlatformSliderDraggingCornerRadius,
+ colors: PlatformSliderColors = PlatformSliderDefaults.defaultPlatformSliderColors(),
+ draggingCornersRadius: Dp = PlatformSliderDefaults.DefaultPlatformSliderDraggingCornerRadius,
icon: (@Composable (isDragging: Boolean) -> Unit)? = null,
label: (@Composable (isDragging: Boolean) -> Unit)? = null,
) {
@@ -109,7 +104,7 @@ fun PlatformSlider(
val paddingStart by
animateDpAsState(
targetValue =
- if ((!isDragging && value == 0f) || icon == null) {
+ if ((!isDragging && value == valueRange.start) || icon == null) {
16.dp
} else {
0.dp
@@ -125,6 +120,7 @@ fun PlatformSlider(
valueRange = valueRange,
onValueChangeFinished = onValueChangeFinished,
interactionSource = interactionSource,
+ enabled = enabled,
track = {
Track(
sliderState = it,
@@ -286,6 +282,17 @@ data class PlatformSliderColors(
val disabledLabelColor: Color,
)
+object PlatformSliderDefaults {
+
+ /** Indicator corner radius used when the user drags the [PlatformSlider]. */
+ val DefaultPlatformSliderDraggingCornerRadius = 8.dp
+
+ @Composable
+ fun defaultPlatformSliderColors(): PlatformSliderColors =
+ if (isSystemInDarkTheme()) darkThemePlatformSliderColors()
+ else lightThemePlatformSliderColors()
+}
+
/** [PlatformSliderColors] for the light theme */
@Composable
private fun lightThemePlatformSliderColors() =
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/theme/AndroidColorScheme.kt b/packages/SystemUI/compose/core/src/com/android/compose/theme/AndroidColorScheme.kt
index 1d6f813cfbdf..b28655b28b65 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/theme/AndroidColorScheme.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/theme/AndroidColorScheme.kt
@@ -69,6 +69,7 @@ class AndroidColorScheme(context: Context) {
val onTertiary = getColor(context, R.attr.materialColorOnTertiary)
val surfaceDim = getColor(context, R.attr.materialColorSurfaceDim)
val surfaceBright = getColor(context, R.attr.materialColorSurfaceBright)
+ val error = getColor(context, R.attr.materialColorError)
val onError = getColor(context, R.attr.materialColorOnError)
val surface = getColor(context, R.attr.materialColorSurface)
val surfaceContainerHigh = getColor(context, R.attr.materialColorSurfaceContainerHigh)
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 374a97d769c8..4398b2541f65 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
@@ -69,7 +69,7 @@ object ComposeFacade : BaseComposeFacade {
override fun setVolumePanelActivityContent(
activity: ComponentActivity,
viewModel: VolumePanelViewModel,
- onDismissAnimationFinished: () -> Unit,
+ onDismiss: () -> Unit,
) {
throwComposeUnavailableError()
}
diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/anc/AncModule.kt b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/anc/AncModule.kt
new file mode 100644
index 000000000000..a4fb05d3b5b9
--- /dev/null
+++ b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/anc/AncModule.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.anc
+
+import dagger.Module
+
+@Module interface AncModule
diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/mediaoutput/MediaOutputModule.kt b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/mediaoutput/MediaOutputModule.kt
new file mode 100644
index 000000000000..8ad0a080a9dd
--- /dev/null
+++ b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/mediaoutput/MediaOutputModule.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.mediaoutput
+
+import dagger.Module
+
+@Module interface MediaOutputModule
diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/volume/VolumeSlidersModule.kt b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/volume/VolumeSlidersModule.kt
new file mode 100644
index 000000000000..b4cb0983f213
--- /dev/null
+++ b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/volume/panel/component/volume/VolumeSlidersModule.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.volume
+
+import dagger.Module
+
+@Module interface VolumeSlidersModule
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 a1bbc7d7bfb8..76931a2b4d41 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
@@ -22,12 +22,15 @@ import android.view.View
import android.view.WindowInsets
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.lifecycle.LifecycleOwner
+import com.android.compose.theme.LocalAndroidColorScheme
import com.android.compose.theme.PlatformTheme
import com.android.compose.ui.platform.DensityAwareComposeView
import com.android.internal.policy.ScreenDecorationsUtils
@@ -89,12 +92,18 @@ object ComposeFacade : BaseComposeFacade {
) {
activity.setContent {
PlatformTheme {
- CommunalHub(
- viewModel = viewModel,
- onOpenWidgetPicker = onOpenWidgetPicker,
- widgetConfigurator = widgetConfigurator,
- onEditDone = onEditDone,
- )
+ Box(
+ modifier =
+ Modifier.fillMaxSize()
+ .background(LocalAndroidColorScheme.current.outlineVariant),
+ ) {
+ CommunalHub(
+ viewModel = viewModel,
+ onOpenWidgetPicker = onOpenWidgetPicker,
+ widgetConfigurator = widgetConfigurator,
+ onEditDone = onEditDone,
+ )
+ }
}
}
}
@@ -102,12 +111,12 @@ object ComposeFacade : BaseComposeFacade {
override fun setVolumePanelActivityContent(
activity: ComponentActivity,
viewModel: VolumePanelViewModel,
- onDismissAnimationFinished: () -> Unit,
+ onDismiss: () -> Unit,
) {
activity.setContent {
VolumePanelRoot(
viewModel = viewModel,
- onDismissAnimationFinished = onDismissAnimationFinished,
+ onDismiss = onDismiss,
)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
index d9493961e3ed..621ddf796f58 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
@@ -557,13 +557,18 @@ private fun StatusMessage(
targetState = message,
label = "Bouncer message",
animationSpec = if (message.isUpdateAnimated) tween() else snap(),
- modifier = modifier,
+ modifier = modifier.fillMaxWidth(),
) {
- Text(
- text = it.text,
- color = MaterialTheme.colorScheme.onSurface,
- style = MaterialTheme.typography.bodyLarge,
- )
+ Box(
+ contentAlignment = Alignment.Center,
+ modifier = Modifier.fillMaxWidth(),
+ ) {
+ Text(
+ text = it.text,
+ color = MaterialTheme.colorScheme.onSurface,
+ style = MaterialTheme.typography.bodyLarge,
+ )
+ }
}
}
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
index bc85513ae296..be5aa8a4c3b9 100644
--- 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
@@ -1,6 +1,7 @@
package com.android.systemui.communal.ui.compose
import androidx.compose.animation.core.tween
+import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
@@ -12,6 +13,7 @@ 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.FixedSizeEdgeDetector
+import com.android.compose.animation.scene.LowestZIndexScenePicker
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.SceneScope
@@ -21,6 +23,7 @@ import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.observableTransitionState
import com.android.compose.animation.scene.transitions
import com.android.compose.animation.scene.updateSceneTransitionLayoutState
+import com.android.compose.theme.LocalAndroidColorScheme
import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
import com.android.systemui.communal.ui.compose.extensions.allowGestures
@@ -31,16 +34,25 @@ import kotlinx.coroutines.flow.transform
object Communal {
object Elements {
+ val Scrim = ElementKey("Scrim", scenePicker = LowestZIndexScenePicker)
val Content = ElementKey("CommunalContent")
}
}
val sceneTransitions = transitions {
- from(TransitionSceneKey.Blank, to = TransitionSceneKey.Communal) {
- spec = tween(durationMillis = 500)
-
+ to(TransitionSceneKey.Communal) {
+ spec = tween(durationMillis = 1000)
+ translate(Communal.Elements.Content, Edge.Right)
+ timestampRange(startMillis = 167, endMillis = 334) {
+ fade(Communal.Elements.Scrim)
+ fade(Communal.Elements.Content)
+ }
+ }
+ to(TransitionSceneKey.Blank) {
+ spec = tween(durationMillis = 1000)
translate(Communal.Elements.Content, Edge.Right)
- fade(Communal.Elements.Content)
+ timestampRange(endMillis = 167) { fade(Communal.Elements.Content) }
+ timestampRange(startMillis = 167, endMillis = 334) { fade(Communal.Elements.Scrim) }
}
}
@@ -111,6 +123,12 @@ private fun SceneScope.CommunalScene(
viewModel: BaseCommunalViewModel,
modifier: Modifier = Modifier,
) {
+ Box(
+ modifier =
+ Modifier.element(Communal.Elements.Scrim)
+ .fillMaxSize()
+ .background(LocalAndroidColorScheme.current.outlineVariant),
+ )
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 090750e046a7..622a4f04ee0d 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
@@ -73,6 +73,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
+import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
@@ -85,7 +86,10 @@ import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.layout.positionInWindow
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.testTagsAsResourceId
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
@@ -110,6 +114,7 @@ import com.android.systemui.communal.widgets.WidgetConfigurator
import com.android.systemui.res.R
import kotlinx.coroutines.launch
+@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun CommunalHub(
modifier: Modifier = Modifier,
@@ -140,8 +145,9 @@ fun CommunalHub(
Box(
modifier =
modifier
+ .semantics { testTagsAsResourceId = true }
+ .testTag(COMMUNAL_HUB_TEST_TAG)
.fillMaxSize()
- .background(LocalAndroidColorScheme.current.outlineVariant)
.pointerInput(gridState, contentOffset, contentListState) {
// If not in edit mode, don't allow selecting items.
if (!viewModel.isEditMode) return@pointerInput
@@ -873,3 +879,6 @@ object Dimensions {
)
val IconSize = 48.dp
}
+
+/** The resource id of communal hub accessible from UiAutomator. */
+private const val COMMUNAL_HUB_TEST_TAG = "communal_hub"
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
index 67b79a06b4a0..9b8c9d0ab616 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
@@ -17,6 +17,7 @@
package com.android.systemui.communal.ui.compose
import android.content.ComponentName
+import android.os.UserHandle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.toMutableStateList
@@ -33,9 +34,10 @@ fun rememberContentListState(
return remember(communalContent) {
ContentListState(
communalContent,
- { componentName, priority ->
+ { componentName, user, priority ->
viewModel.onAddWidget(
componentName,
+ user,
priority,
widgetConfigurator,
)
@@ -54,7 +56,8 @@ fun rememberContentListState(
class ContentListState
internal constructor(
communalContent: List<CommunalContentModel>,
- private val onAddWidget: (componentName: ComponentName, priority: Int) -> Unit,
+ private val onAddWidget:
+ (componentName: ComponentName, user: UserHandle, priority: Int) -> Unit,
private val onDeleteWidget: (id: Int) -> Unit,
private val onReorderWidgets: (widgetIdToPriorityMap: Map<Int, Int>) -> Unit,
) {
@@ -81,10 +84,16 @@ internal constructor(
*
* @param newItemComponentName name of the new widget that was dropped into the list; null if no
* new widget was added.
+ * @param newItemUser user profile associated with the new widget that was dropped into the
+ * list; null if no new widget was added.
* @param newItemIndex index at which the a new widget was dropped into the list; null if no new
* widget was dropped.
*/
- fun onSaveList(newItemComponentName: ComponentName? = null, newItemIndex: Int? = null) {
+ fun onSaveList(
+ newItemComponentName: ComponentName? = null,
+ newItemUser: UserHandle? = null,
+ newItemIndex: Int? = null
+ ) {
// filters placeholder, but, maintains the indices of the widgets as if the placeholder was
// in the list. When persisted in DB, this leaves space for the new item (to be added) at
// the correct priority.
@@ -100,8 +109,8 @@ internal constructor(
.toMap()
// reorder and then add the new widget
onReorderWidgets(widgetIdToPriorityMap)
- if (newItemComponentName != null && newItemIndex != null) {
- onAddWidget(newItemComponentName, /*priority=*/ list.size - newItemIndex)
+ if (newItemComponentName != null && newItemUser != null && newItemIndex != null) {
+ onAddWidget(newItemComponentName, newItemUser, /*priority=*/ list.size - newItemIndex)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/DragAndDropTargetState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/DragAndDropTargetState.kt
index 881947ed6534..dee255917359 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/DragAndDropTargetState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/DragAndDropTargetState.kt
@@ -17,8 +17,6 @@
package com.android.systemui.communal.ui.compose
import android.content.ClipDescription
-import android.content.ComponentName
-import android.content.Intent
import android.view.DragEvent
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.draganddrop.dragAndDropTarget
@@ -44,6 +42,8 @@ import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.ui.compose.extensions.plus
+import com.android.systemui.communal.util.WidgetPickerIntentUtils
+import com.android.systemui.communal.util.WidgetPickerIntentUtils.getWidgetExtraFromIntent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
@@ -201,12 +201,14 @@ internal class DragAndDropTargetState(
return false
}
return placeHolderIndex?.let { dropIndex ->
- val componentName = event.maybeWidgetComponentName()
- if (componentName != null) {
+ val widgetExtra = event.maybeWidgetExtra() ?: return false
+ val (componentName, user) = widgetExtra
+ if (componentName != null && user != null) {
// Placeholder isn't removed yet to allow the setting the right priority for items
// before adding in the new item.
contentListState.onSaveList(
newItemComponentName = componentName,
+ newItemUser = user,
newItemIndex = dropIndex
)
return@let true
@@ -260,15 +262,12 @@ internal class DragAndDropTargetState(
}
/**
- * Parses and returns the component name of the widget that was dropped into the communal grid.
+ * Parses and returns the intent extra associated with the widget that is dropped into the grid.
*
- * Returns null if the drop event didn't include the widget information.
+ * Returns null if the drop event didn't include intent information.
*/
- private fun DragAndDropEvent.maybeWidgetComponentName(): ComponentName? {
+ private fun DragAndDropEvent.maybeWidgetExtra(): WidgetPickerIntentUtils.WidgetExtra? {
val clipData = this.toAndroidDragEvent().clipData.takeIf { it.itemCount != 0 }
- return clipData
- ?.getItemAt(0)
- ?.intent
- ?.getParcelableExtra(Intent.EXTRA_COMPONENT_NAME, ComponentName::class.java)
+ return clipData?.getItemAt(0)?.intent?.let { intent -> getWidgetExtraFromIntent(intent) }
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
index 7b21d091d451..dd043dbebaa6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
@@ -18,11 +18,14 @@ package com.android.systemui.keyguard.ui.composable
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.animateSceneFloatAsState
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
+import com.android.systemui.qs.ui.composable.QuickSettings
import com.android.systemui.scene.shared.model.Direction
import com.android.systemui.scene.shared.model.Edge
import com.android.systemui.scene.shared.model.SceneKey
@@ -87,10 +90,15 @@ constructor(
}
@Composable
-private fun LockscreenScene(
+private fun SceneScope.LockscreenScene(
lockscreenContent: Lazy<LockscreenContent>,
modifier: Modifier = Modifier,
) {
+ animateSceneFloatAsState(
+ value = QuickSettings.SharedValues.SquishinessValues.LockscreenSceneStarting,
+ key = QuickSettings.SharedValues.TilesSquishiness,
+ )
+
lockscreenContent
.get()
.Content(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt
index 3677cab890f5..53f400fac7e5 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt
@@ -20,6 +20,8 @@ import com.android.systemui.keyguard.ui.composable.blueprint.CommunalBlueprintMo
import com.android.systemui.keyguard.ui.composable.blueprint.DefaultBlueprintModule
import com.android.systemui.keyguard.ui.composable.blueprint.ShortcutsBesideUdfpsBlueprintModule
import com.android.systemui.keyguard.ui.composable.blueprint.SplitShadeBlueprintModule
+import com.android.systemui.keyguard.ui.composable.blueprint.SplitShadeWeatherClockBlueprintModule
+import com.android.systemui.keyguard.ui.composable.blueprint.WeatherClockBlueprintModule
import com.android.systemui.keyguard.ui.composable.section.OptionalSectionModule
import dagger.Module
@@ -31,6 +33,8 @@ import dagger.Module
OptionalSectionModule::class,
ShortcutsBesideUdfpsBlueprintModule::class,
SplitShadeBlueprintModule::class,
+ SplitShadeWeatherClockBlueprintModule::class,
+ WeatherClockBlueprintModule::class,
],
)
interface LockscreenSceneBlueprintModule
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt
index c4184905f28d..7a73c58ba193 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt
@@ -73,7 +73,7 @@ private fun rememberBurnInParameters(
BurnInParameters(
clockControllerProvider = { clock },
topInset = topInset,
- statusViewTop = topmostTop,
+ minViewY = topmostTop,
)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
index a07ab4a11731..452dc03facfd 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
@@ -33,7 +33,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
-import com.android.systemui.keyguard.ui.composable.section.ClockSection
+import com.android.systemui.keyguard.ui.composable.section.DefaultClockSection
import com.android.systemui.keyguard.ui.composable.section.LockSection
import com.android.systemui.keyguard.ui.composable.section.NotificationSection
import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
@@ -56,7 +56,7 @@ class DefaultBlueprint
constructor(
private val viewModel: LockscreenContentViewModel,
private val statusBarSection: StatusBarSection,
- private val clockSection: ClockSection,
+ private val clockSection: DefaultClockSection,
private val smartSpaceSection: SmartSpaceSection,
private val notificationSection: NotificationSection,
private val lockSection: LockSection,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
index b035e4220a5b..71c60c70a655 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
@@ -33,7 +33,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
-import com.android.systemui.keyguard.ui.composable.section.ClockSection
+import com.android.systemui.keyguard.ui.composable.section.DefaultClockSection
import com.android.systemui.keyguard.ui.composable.section.LockSection
import com.android.systemui.keyguard.ui.composable.section.NotificationSection
import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
@@ -56,7 +56,7 @@ class ShortcutsBesideUdfpsBlueprint
constructor(
private val viewModel: LockscreenContentViewModel,
private val statusBarSection: StatusBarSection,
- private val clockSection: ClockSection,
+ private val clockSection: DefaultClockSection,
private val smartSpaceSection: SmartSpaceSection,
private val notificationSection: NotificationSection,
private val lockSection: LockSection,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
index 660fc5a98c03..af836b68544c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
@@ -39,7 +39,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
-import com.android.systemui.keyguard.ui.composable.section.ClockSection
+import com.android.systemui.keyguard.ui.composable.section.DefaultClockSection
import com.android.systemui.keyguard.ui.composable.section.LockSection
import com.android.systemui.keyguard.ui.composable.section.NotificationSection
import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
@@ -63,7 +63,7 @@ class SplitShadeBlueprint
constructor(
private val viewModel: LockscreenContentViewModel,
private val statusBarSection: StatusBarSection,
- private val clockSection: ClockSection,
+ private val clockSection: DefaultClockSection,
private val smartSpaceSection: SmartSpaceSection,
private val notificationSection: NotificationSection,
private val lockSection: LockSection,
@@ -127,7 +127,7 @@ constructor(
}
with(notificationSection) {
val splitShadeTopMargin: Dp =
- if (Flags.centralizedStatusBarDimensRefactor()) {
+ if (Flags.centralizedStatusBarHeightFix()) {
largeScreenHeaderHelper.getLargeScreenHeaderHeight().dp
} else {
dimensionResource(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt
new file mode 100644
index 000000000000..e2e7a950cdfd
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) 2024 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.ui.composable.blueprint
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.IntRect
+import androidx.compose.ui.unit.dp
+import com.android.compose.animation.scene.SceneScope
+import com.android.compose.modifiers.padding
+import com.android.systemui.Flags
+import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor.Companion.SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID
+import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor.Companion.WEATHER_CLOCK_BLUEPRINT_ID
+import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
+import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
+import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
+import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
+import com.android.systemui.keyguard.ui.composable.section.LockSection
+import com.android.systemui.keyguard.ui.composable.section.NotificationSection
+import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
+import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection
+import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
+import com.android.systemui.keyguard.ui.composable.section.WeatherClockSection
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
+import com.android.systemui.res.R
+import com.android.systemui.shade.LargeScreenHeaderHelper
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
+import java.util.Optional
+import javax.inject.Inject
+
+class WeatherClockBlueprint
+@Inject
+constructor(
+ private val viewModel: LockscreenContentViewModel,
+ private val statusBarSection: StatusBarSection,
+ private val weatherClockSection: WeatherClockSection,
+ private val smartSpaceSection: SmartSpaceSection,
+ private val notificationSection: NotificationSection,
+ private val lockSection: LockSection,
+ private val ambientIndicationSectionOptional: Optional<AmbientIndicationSection>,
+ private val bottomAreaSection: BottomAreaSection,
+ private val settingsMenuSection: SettingsMenuSection,
+ private val clockInteractor: KeyguardClockInteractor,
+) : ComposableLockscreenSceneBlueprint {
+
+ override val id: String = WEATHER_CLOCK_BLUEPRINT_ID
+ @Composable
+ override fun SceneScope.Content(modifier: Modifier) {
+ val isUdfpsVisible = viewModel.isUdfpsVisible
+ val burnIn = rememberBurnIn(clockInteractor)
+ val resources = LocalContext.current.resources
+
+ LockscreenLongPress(
+ viewModel = viewModel.longPress,
+ modifier = modifier,
+ ) { onSettingsMenuPlaced ->
+ Layout(
+ content = {
+ // Constrained to above the lock icon.
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ ) {
+ with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
+ // TODO: Add weather clock for small and large clock
+ with(smartSpaceSection) {
+ SmartSpace(
+ burnInParams = burnIn.parameters,
+ onTopChanged = burnIn.onSmartspaceTopChanged,
+ modifier =
+ Modifier.fillMaxWidth()
+ .padding(
+ top = { viewModel.getSmartSpacePaddingTop(resources) },
+ )
+ .padding(
+ bottom =
+ dimensionResource(
+ R.dimen.keyguard_status_view_bottom_margin
+ ),
+ ),
+ )
+ }
+
+ if (viewModel.areNotificationsVisible) {
+ with(notificationSection) {
+ Notifications(
+ modifier = Modifier.fillMaxWidth().weight(weight = 1f)
+ )
+ }
+ }
+
+ if (!isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
+ with(ambientIndicationSectionOptional.get()) {
+ AmbientIndication(modifier = Modifier.fillMaxWidth())
+ }
+ }
+ }
+
+ with(lockSection) { LockIcon() }
+
+ // Aligned to bottom and constrained to below the lock icon.
+ Column(modifier = Modifier.fillMaxWidth()) {
+ if (isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
+ with(ambientIndicationSectionOptional.get()) {
+ AmbientIndication(modifier = Modifier.fillMaxWidth())
+ }
+ }
+
+ with(bottomAreaSection) {
+ IndicationArea(modifier = Modifier.fillMaxWidth())
+ }
+ }
+
+ // Aligned to bottom and NOT constrained by the lock icon.
+ with(bottomAreaSection) {
+ Shortcut(isStart = true, applyPadding = true)
+ Shortcut(isStart = false, applyPadding = true)
+ }
+ with(settingsMenuSection) { SettingsMenu(onSettingsMenuPlaced) }
+ },
+ modifier = Modifier.fillMaxSize(),
+ ) { measurables, constraints ->
+ check(measurables.size == 6)
+ val aboveLockIconMeasurable = measurables[0]
+ val lockIconMeasurable = measurables[1]
+ val belowLockIconMeasurable = measurables[2]
+ val startShortcutMeasurable = measurables[3]
+ val endShortcutMeasurable = measurables[4]
+ val settingsMenuMeasurable = measurables[5]
+
+ val noMinConstraints =
+ constraints.copy(
+ minWidth = 0,
+ minHeight = 0,
+ )
+ val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
+ val lockIconBounds =
+ IntRect(
+ left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
+ top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
+ right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
+ bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
+ )
+
+ val aboveLockIconPlaceable =
+ aboveLockIconMeasurable.measure(
+ noMinConstraints.copy(maxHeight = lockIconBounds.top)
+ )
+ val belowLockIconPlaceable =
+ belowLockIconMeasurable.measure(
+ noMinConstraints.copy(
+ maxHeight =
+ (constraints.maxHeight - lockIconBounds.bottom).coerceAtLeast(0)
+ )
+ )
+ val startShortcutPleaceable = startShortcutMeasurable.measure(noMinConstraints)
+ val endShortcutPleaceable = endShortcutMeasurable.measure(noMinConstraints)
+ val settingsMenuPlaceable = settingsMenuMeasurable.measure(noMinConstraints)
+
+ layout(constraints.maxWidth, constraints.maxHeight) {
+ aboveLockIconPlaceable.place(
+ x = 0,
+ y = 0,
+ )
+ lockIconPlaceable.place(
+ x = lockIconBounds.left,
+ y = lockIconBounds.top,
+ )
+ belowLockIconPlaceable.place(
+ x = 0,
+ y = constraints.maxHeight - belowLockIconPlaceable.height,
+ )
+ startShortcutPleaceable.place(
+ x = 0,
+ y = constraints.maxHeight - startShortcutPleaceable.height,
+ )
+ endShortcutPleaceable.place(
+ x = constraints.maxWidth - endShortcutPleaceable.width,
+ y = constraints.maxHeight - endShortcutPleaceable.height,
+ )
+ settingsMenuPlaceable.place(
+ x = (constraints.maxWidth - settingsMenuPlaceable.width) / 2,
+ y = constraints.maxHeight - settingsMenuPlaceable.height,
+ )
+ }
+ }
+ }
+ }
+}
+
+class SplitShadeWeatherClockBlueprint
+@Inject
+constructor(
+ private val viewModel: LockscreenContentViewModel,
+ private val statusBarSection: StatusBarSection,
+ private val smartSpaceSection: SmartSpaceSection,
+ private val notificationSection: NotificationSection,
+ private val lockSection: LockSection,
+ private val ambientIndicationSectionOptional: Optional<AmbientIndicationSection>,
+ private val bottomAreaSection: BottomAreaSection,
+ private val settingsMenuSection: SettingsMenuSection,
+ private val clockInteractor: KeyguardClockInteractor,
+ private val largeScreenHeaderHelper: LargeScreenHeaderHelper,
+ private val weatherClockSection: WeatherClockSection,
+) : ComposableLockscreenSceneBlueprint {
+ override val id: String = SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID
+
+ @Composable
+ override fun SceneScope.Content(modifier: Modifier) {
+ val isUdfpsVisible = viewModel.isUdfpsVisible
+ val burnIn = rememberBurnIn(clockInteractor)
+ val resources = LocalContext.current.resources
+
+ LockscreenLongPress(
+ viewModel = viewModel.longPress,
+ modifier = modifier,
+ ) { onSettingsMenuPlaced ->
+ Layout(
+ content = {
+ // Constrained to above the lock icon.
+ Column(
+ modifier = Modifier.fillMaxSize(),
+ ) {
+ with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
+ Row(
+ modifier = Modifier.fillMaxSize(),
+ ) {
+ // TODO: Add weather clock for small and large clock
+ Column(
+ modifier = Modifier.fillMaxHeight().weight(weight = 1f),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ with(smartSpaceSection) {
+ SmartSpace(
+ burnInParams = burnIn.parameters,
+ onTopChanged = burnIn.onSmartspaceTopChanged,
+ modifier =
+ Modifier.fillMaxWidth()
+ .padding(
+ top = {
+ viewModel.getSmartSpacePaddingTop(resources)
+ },
+ )
+ .padding(
+ bottom =
+ dimensionResource(
+ R.dimen
+ .keyguard_status_view_bottom_margin
+ )
+ ),
+ )
+ }
+ }
+ with(notificationSection) {
+ val splitShadeTopMargin: Dp =
+ if (Flags.centralizedStatusBarHeightFix()) {
+ largeScreenHeaderHelper.getLargeScreenHeaderHeight().dp
+ } else {
+ dimensionResource(
+ id = R.dimen.large_screen_shade_header_height
+ )
+ }
+ Notifications(
+ modifier =
+ Modifier.fillMaxHeight()
+ .weight(weight = 1f)
+ .padding(top = splitShadeTopMargin)
+ )
+ }
+ }
+
+ if (!isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
+ with(ambientIndicationSectionOptional.get()) {
+ AmbientIndication(modifier = Modifier.fillMaxWidth())
+ }
+ }
+ }
+
+ with(lockSection) { LockIcon() }
+
+ // Aligned to bottom and constrained to below the lock icon.
+ Column(modifier = Modifier.fillMaxWidth()) {
+ if (isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
+ with(ambientIndicationSectionOptional.get()) {
+ AmbientIndication(modifier = Modifier.fillMaxWidth())
+ }
+ }
+
+ with(bottomAreaSection) {
+ IndicationArea(modifier = Modifier.fillMaxWidth())
+ }
+ }
+
+ // Aligned to bottom and NOT constrained by the lock icon.
+ with(bottomAreaSection) {
+ Shortcut(isStart = true, applyPadding = true)
+ Shortcut(isStart = false, applyPadding = true)
+ }
+ with(settingsMenuSection) { SettingsMenu(onSettingsMenuPlaced) }
+ },
+ modifier = Modifier.fillMaxSize(),
+ ) { measurables, constraints ->
+ check(measurables.size == 6)
+ val aboveLockIconMeasurable = measurables[0]
+ val lockIconMeasurable = measurables[1]
+ val belowLockIconMeasurable = measurables[2]
+ val startShortcutMeasurable = measurables[3]
+ val endShortcutMeasurable = measurables[4]
+ val settingsMenuMeasurable = measurables[5]
+
+ val noMinConstraints =
+ constraints.copy(
+ minWidth = 0,
+ minHeight = 0,
+ )
+ val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
+ val lockIconBounds =
+ IntRect(
+ left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
+ top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
+ right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
+ bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
+ )
+
+ val aboveLockIconPlaceable =
+ aboveLockIconMeasurable.measure(
+ noMinConstraints.copy(maxHeight = lockIconBounds.top)
+ )
+ val belowLockIconPlaceable =
+ belowLockIconMeasurable.measure(
+ noMinConstraints.copy(
+ maxHeight =
+ (constraints.maxHeight - lockIconBounds.bottom).coerceAtLeast(0)
+ )
+ )
+ val startShortcutPleaceable = startShortcutMeasurable.measure(noMinConstraints)
+ val endShortcutPleaceable = endShortcutMeasurable.measure(noMinConstraints)
+ val settingsMenuPlaceable = settingsMenuMeasurable.measure(noMinConstraints)
+
+ layout(constraints.maxWidth, constraints.maxHeight) {
+ aboveLockIconPlaceable.place(
+ x = 0,
+ y = 0,
+ )
+ lockIconPlaceable.place(
+ x = lockIconBounds.left,
+ y = lockIconBounds.top,
+ )
+ belowLockIconPlaceable.place(
+ x = 0,
+ y = constraints.maxHeight - belowLockIconPlaceable.height,
+ )
+ startShortcutPleaceable.place(
+ x = 0,
+ y = constraints.maxHeight - startShortcutPleaceable.height,
+ )
+ endShortcutPleaceable.place(
+ x = constraints.maxWidth - endShortcutPleaceable.width,
+ y = constraints.maxHeight - endShortcutPleaceable.height,
+ )
+ settingsMenuPlaceable.place(
+ x = (constraints.maxWidth - settingsMenuPlaceable.width) / 2,
+ y = constraints.maxHeight - settingsMenuPlaceable.height,
+ )
+ }
+ }
+ }
+ }
+}
+
+@Module
+interface WeatherClockBlueprintModule {
+ @Binds
+ @IntoSet
+ fun blueprint(blueprint: WeatherClockBlueprint): ComposableLockscreenSceneBlueprint
+}
+
+@Module
+interface SplitShadeWeatherClockBlueprintModule {
+ @Binds
+ @IntoSet
+ fun blueprint(blueprint: SplitShadeWeatherClockBlueprint): ComposableLockscreenSceneBlueprint
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
index 8bd0d45920f4..97d5b41000de 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/BottomAreaSection.kt
@@ -35,7 +35,6 @@ import com.android.systemui.animation.view.LaunchableImageView
import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
import com.android.systemui.keyguard.ui.view.KeyguardIndicationArea
-import com.android.systemui.keyguard.ui.viewmodel.AodAlphaViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel
@@ -55,7 +54,6 @@ constructor(
private val vibratorHelper: VibratorHelper,
private val indicationController: KeyguardIndicationController,
private val indicationAreaViewModel: KeyguardIndicationAreaViewModel,
- private val alphaViewModel: AodAlphaViewModel,
) {
/**
* Renders a single lockscreen shortcut.
@@ -104,7 +102,6 @@ constructor(
content {
IndicationArea(
indicationAreaViewModel = indicationAreaViewModel,
- alphaViewModel = alphaViewModel,
indicationController = indicationController,
)
}
@@ -183,7 +180,6 @@ constructor(
@Composable
private fun IndicationArea(
indicationAreaViewModel: KeyguardIndicationAreaViewModel,
- alphaViewModel: AodAlphaViewModel,
indicationController: KeyguardIndicationController,
modifier: Modifier = Modifier,
) {
@@ -196,7 +192,6 @@ constructor(
KeyguardIndicationAreaBinder.bind(
view = view,
viewModel = indicationAreaViewModel,
- aodAlphaViewModel = alphaViewModel,
indicationController = indicationController,
)
)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/ClockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
index fa07bafda82c..335c915411ee 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/ClockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
@@ -18,6 +18,7 @@ package com.android.systemui.keyguard.ui.composable.section
import android.view.ViewGroup
import android.widget.FrameLayout
+import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
@@ -38,14 +39,17 @@ import com.android.systemui.keyguard.ui.composable.modifier.onTopPlacementChange
import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
+import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
import javax.inject.Inject
-class ClockSection
+/** Provides small clock and large clock composables for the default clock face. */
+class DefaultClockSection
@Inject
constructor(
private val viewModel: KeyguardClockViewModel,
private val clockInteractor: KeyguardClockInteractor,
private val aodBurnInViewModel: AodBurnInViewModel,
+ private val lockscreenSmartspaceController: LockscreenSmartspaceController,
) {
@Composable
@@ -151,6 +155,7 @@ constructor(
(newClockView.parent as? ViewGroup)?.removeView(newClockView)
it.addView(newClockView)
},
+ modifier = Modifier.fillMaxSize()
)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
index f387021daeac..c7d43fcdf5e6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
@@ -21,10 +21,10 @@ import android.view.ViewGroup
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.notifications.ui.composable.NotificationStack
import com.android.systemui.scene.shared.flag.SceneContainerFlags
import com.android.systemui.statusbar.notification.stack.AmbientState
@@ -55,41 +55,43 @@ constructor(
notificationStackAppearanceViewModel: NotificationStackAppearanceViewModel,
ambientState: AmbientState,
notificationStackSizeCalculator: NotificationStackSizeCalculator,
- @Main private val mainDispatcher: CoroutineDispatcher,
+ @Main private val mainImmediateDispatcher: CoroutineDispatcher,
) {
init {
- if (!KeyguardShadeMigrationNssl.isUnexpectedlyInLegacyMode()) {
- // This scene container section moves the NSSL to the SharedNotificationContainer.
- // This also requires that SharedNotificationContainer gets moved to the
- // SceneWindowRootView by the SceneWindowRootViewBinder. Prior to Scene Container,
- // but when the KeyguardShadeMigrationNssl flag is enabled, NSSL is moved into this
- // container by the NotificationStackScrollLayoutSection.
- // Ensure stackScrollLayout is a child of sharedNotificationContainer.
+ if (!migrateClocksToBlueprint()) {
+ throw IllegalStateException("this requires migrateClocksToBlueprint()")
+ }
+ // This scene container section moves the NSSL to the SharedNotificationContainer.
+ // This also requires that SharedNotificationContainer gets moved to the
+ // SceneWindowRootView by the SceneWindowRootViewBinder. Prior to Scene Container,
+ // but when the KeyguardShadeMigrationNssl flag is enabled, NSSL is moved into this
+ // container by the NotificationStackScrollLayoutSection.
+ // Ensure stackScrollLayout is a child of sharedNotificationContainer.
- if (stackScrollLayout.parent != sharedNotificationContainer) {
- (stackScrollLayout.parent as? ViewGroup)?.removeView(stackScrollLayout)
- sharedNotificationContainer.addNotificationStackScrollLayout(stackScrollLayout)
- }
+ if (stackScrollLayout.parent != sharedNotificationContainer) {
+ (stackScrollLayout.parent as? ViewGroup)?.removeView(stackScrollLayout)
+ sharedNotificationContainer.addNotificationStackScrollLayout(stackScrollLayout)
+ }
- SharedNotificationContainerBinder.bind(
+ SharedNotificationContainerBinder.bind(
+ sharedNotificationContainer,
+ sharedNotificationContainerViewModel,
+ sceneContainerFlags,
+ controller,
+ notificationStackSizeCalculator,
+ mainImmediateDispatcher = mainImmediateDispatcher,
+ )
+
+ if (sceneContainerFlags.flexiNotifsEnabled()) {
+ NotificationStackAppearanceViewBinder.bind(
+ context,
sharedNotificationContainer,
- sharedNotificationContainerViewModel,
- sceneContainerFlags,
+ notificationStackAppearanceViewModel,
+ ambientState,
controller,
- notificationStackSizeCalculator,
- mainDispatcher,
+ mainImmediateDispatcher = mainImmediateDispatcher,
)
-
- if (sceneContainerFlags.flexiNotifsEnabled()) {
- NotificationStackAppearanceViewBinder.bind(
- context,
- sharedNotificationContainer,
- notificationStackAppearanceViewModel,
- ambientState,
- controller,
- )
- }
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt
new file mode 100644
index 000000000000..2e7bc2a28c65
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 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.ui.composable.section
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import com.android.compose.animation.scene.SceneScope
+import javax.inject.Inject
+
+/** Provides small clock and large clock composables for the weather clock layout. */
+class WeatherClockSection @Inject constructor() {
+ @Composable
+ fun SceneScope.Time(
+ modifier: Modifier = Modifier,
+ ) {
+ // TODO: compose view
+ }
+
+ @Composable
+ fun SceneScope.Date(
+ modifier: Modifier = Modifier,
+ ) {
+ // TODO: compose view
+ }
+
+ @Composable
+ fun SceneScope.Weather(
+ modifier: Modifier = Modifier,
+ ) {
+ // TODO: compose view
+ }
+
+ @Composable
+ fun SceneScope.DndAlarmStatus(
+ modifier: Modifier = Modifier,
+ ) {
+ // TODO: compose view
+ }
+
+ @Composable
+ fun SceneScope.Temperature(
+ modifier: Modifier = Modifier,
+ ) {
+ // TODO: compose view
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
index 735c43356a40..61b2d4e26097 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
@@ -21,8 +21,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.SceneScope
-import com.android.systemui.media.controls.ui.MediaCarouselController
-import com.android.systemui.media.controls.ui.MediaHost
+import com.android.systemui.media.controls.ui.controller.MediaCarouselController
+import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.util.animation.MeasurementInput
private object MediaCarousel {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
index de8f2ec6e941..5d0b9ba2c736 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
@@ -16,7 +16,6 @@
package com.android.systemui.qs.ui.composable
-import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
@@ -32,14 +31,12 @@ import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.MovableElementScenePicker
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.TransitionState
+import com.android.compose.animation.scene.ValueKey
import com.android.compose.modifiers.thenIf
-import com.android.compose.theme.colorAttr
import com.android.systemui.qs.ui.adapter.QSSceneAdapter
import com.android.systemui.qs.ui.adapter.QSSceneAdapter.State.Companion.Collapsing
import com.android.systemui.qs.ui.adapter.QSSceneAdapter.State.Expanding
-import com.android.systemui.res.R
-import com.android.systemui.scene.ui.composable.Gone
-import com.android.systemui.scene.ui.composable.Lockscreen
+import com.android.systemui.qs.ui.adapter.QSSceneAdapter.State.Unsquishing
import com.android.systemui.scene.ui.composable.QuickSettings as QuickSettingsSceneKey
import com.android.systemui.scene.ui.composable.Shade
@@ -51,15 +48,24 @@ object QuickSettings {
)
object Elements {
- // TODO RENAME
val Content =
ElementKey("QuickSettingsContent", scenePicker = MovableElementScenePicker(SCENES))
- val CollapsedGrid = ElementKey("QuickSettingsCollapsedGrid")
val FooterActions = ElementKey("QuickSettingsFooterActions")
}
+
+ object SharedValues {
+ val TilesSquishiness = ValueKey("QuickSettingsTileSquishiness")
+ object SquishinessValues {
+ val Default = 1f
+ val LockscreenSceneStarting = 0f
+ val GoneSceneStarting = 0.3f
+ }
+ }
}
-private fun SceneScope.stateForQuickSettingsContent(): QSSceneAdapter.State {
+private fun SceneScope.stateForQuickSettingsContent(
+ squishiness: Float = QuickSettings.SharedValues.SquishinessValues.Default
+): QSSceneAdapter.State {
return when (val transitionState = layoutState.transitionState) {
is TransitionState.Idle -> {
when (transitionState.currentScene) {
@@ -73,10 +79,10 @@ private fun SceneScope.stateForQuickSettingsContent(): QSSceneAdapter.State {
when {
fromScene == Shade && toScene == QuickSettingsSceneKey -> Expanding(progress)
fromScene == QuickSettingsSceneKey && toScene == Shade -> Collapsing(progress)
- toScene == Shade -> QSSceneAdapter.State.QQS
- toScene == QuickSettingsSceneKey -> QSSceneAdapter.State.QS
- toScene == Gone -> QSSceneAdapter.State.CLOSED
- toScene == Lockscreen -> QSSceneAdapter.State.CLOSED
+ fromScene == Shade || toScene == Shade -> Unsquishing(squishiness)
+ fromScene == QuickSettingsSceneKey || toScene == QuickSettingsSceneKey -> {
+ QSSceneAdapter.State.QS
+ }
else ->
error(
"Bad transition for QuickSettings: fromScene=$fromScene," +
@@ -90,14 +96,24 @@ private fun SceneScope.stateForQuickSettingsContent(): QSSceneAdapter.State {
/**
* This composable will show QuickSettingsContent in the correct state (as determined by its
* [SceneScope]).
+ *
+ * If adding to scenes not in:
+ * * QuickSettingsScene
+ * * ShadeScene
+ *
+ * amend:
+ * * [stateForQuickSettingsContent],
+ * * [QuickSettings.SCENES],
+ * * this doc.
*/
@Composable
fun SceneScope.QuickSettings(
qsSceneAdapter: QSSceneAdapter,
heightProvider: () -> Int,
modifier: Modifier = Modifier,
+ squishiness: Float = QuickSettings.SharedValues.SquishinessValues.Default,
) {
- val contentState = stateForQuickSettingsContent()
+ val contentState = stateForQuickSettingsContent(squishiness)
MovableElement(
key = QuickSettings.Elements.Content,
@@ -136,7 +152,7 @@ private fun QuickSettingsContent(
modifier.fillMaxWidth().thenIf(isCustomizing) { Modifier.fillMaxHeight() }
) {
AndroidView(
- modifier = Modifier.fillMaxWidth().background(colorAttr(R.attr.underSurface)),
+ modifier = Modifier.fillMaxWidth(),
factory = { _ ->
qsSceneAdapter.setState(state)
view
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
index d36345a310d3..66cef86fb773 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
@@ -54,6 +54,7 @@ import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.unit.dp
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.TransitionState
+import com.android.compose.animation.scene.animateSceneFloatAsState
import com.android.compose.windowsizeclass.LocalWindowSizeClass
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.compose.modifiers.sysuiResTag
@@ -128,6 +129,7 @@ private fun SceneScope.QuickSettingsScene(
remember(lifecycleOwner, viewModel) {
viewModel.getFooterActionsViewModel(lifecycleOwner)
}
+ animateSceneFloatAsState(value = 1f, key = QuickSettings.SharedValues.TilesSquishiness)
// ############## SCROLLING ################
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposeAwareExtensions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposeAwareExtensions.kt
index a7de1eede1f4..0de4650f1248 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposeAwareExtensions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposeAwareExtensions.kt
@@ -16,9 +16,6 @@
package com.android.systemui.scene.ui.composable
-import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.ui.unit.Density
-import androidx.compose.ui.unit.IntSize
import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.Edge as ComposeAwareEdge
import com.android.compose.animation.scene.SceneKey as ComposeAwareSceneKey
@@ -26,14 +23,12 @@ import com.android.compose.animation.scene.Swipe
import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.TransitionKey as ComposeAwareTransitionKey
import com.android.compose.animation.scene.UserAction as ComposeAwareUserAction
-import com.android.compose.animation.scene.UserActionDistance as ComposeAwareUserActionDistance
import com.android.compose.animation.scene.UserActionResult as ComposeAwareUserActionResult
import com.android.systemui.scene.shared.model.Direction
import com.android.systemui.scene.shared.model.Edge
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.TransitionKey
import com.android.systemui.scene.shared.model.UserAction
-import com.android.systemui.scene.shared.model.UserActionDistance
import com.android.systemui.scene.shared.model.UserActionResult
// TODO(b/293899074): remove this file once we can use the types from SceneTransitionLayout.
@@ -82,22 +77,5 @@ fun UserActionResult.asComposeAware(): ComposeAwareUserActionResult {
return ComposeAwareUserActionResult(
toScene = composeUnaware.toScene.asComposeAware(),
transitionKey = composeUnaware.transitionKey?.asComposeAware(),
- distance = composeUnaware.distance?.asComposeAware(),
)
}
-
-fun UserActionDistance.asComposeAware(): ComposeAwareUserActionDistance {
- val composeUnware = this
- return object : ComposeAwareUserActionDistance {
- override fun Density.absoluteDistance(
- fromSceneSize: IntSize,
- orientation: Orientation,
- ): Float {
- return composeUnware.absoluteDistance(
- fromSceneWidth = fromSceneSize.width,
- fromSceneHeight = fromSceneSize.height,
- isHorizontal = orientation == Orientation.Horizontal,
- )
- }
- }
-}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
index f90f29d8b9dd..9ca751e81eed 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
@@ -19,9 +19,12 @@ package com.android.systemui.scene.ui.composable
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.animateSceneFloatAsState
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.ui.composable.QuickSettings
import com.android.systemui.scene.shared.model.Direction
import com.android.systemui.scene.shared.model.Edge
import com.android.systemui.scene.shared.model.SceneKey
@@ -63,6 +66,10 @@ constructor(
override fun SceneScope.Content(
modifier: Modifier,
) {
+ animateSceneFloatAsState(
+ value = QuickSettings.SharedValues.SquishinessValues.GoneSceneStarting,
+ key = QuickSettings.SharedValues.TilesSquishiness,
+ )
Spacer(modifier.fillMaxSize())
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index 5006beb01fb4..9779d7170d0d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -78,6 +78,7 @@ fun SceneContainer(
val state: MutableSceneTransitionLayoutState = remember {
MutableSceneTransitionLayoutState(
initialScene = currentSceneKey.asComposeAware(),
+ canChangeScene = { toScene -> viewModel.canChangeScene(toScene.asComposeUnaware()) },
transitions = SceneContainerTransitions,
)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToShadeTransition.kt
index 6f115d88dbe2..5c6e1c89ad65 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToShadeTransition.kt
@@ -13,7 +13,10 @@ fun TransitionBuilder.goneToShadeTransition(
) {
spec = tween(durationMillis = DefaultDuration.times(durationScale).inWholeMilliseconds.toInt())
- fractionRange(start = .58f) { fade(ShadeHeader.Elements.CollapsedContent) }
+ fractionRange(start = .58f) { fade(ShadeHeader.Elements.Clock) }
+ fractionRange(start = .58f) { fade(ShadeHeader.Elements.CollapsedContentStart) }
+ fractionRange(start = .58f) { fade(ShadeHeader.Elements.CollapsedContentEnd) }
+ fractionRange(start = .58f) { fade(ShadeHeader.Elements.PrivacyChip) }
translate(QuickSettings.Elements.Content, y = -ShadeHeader.Dimensions.CollapsedHeight * .66f)
translate(Notifications.Elements.NotificationScrim, Edge.Top, false)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToShadeTransition.kt
index e71f99669df2..48ab68a6f097 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToShadeTransition.kt
@@ -1,11 +1,11 @@
package com.android.systemui.scene.ui.composable.transitions
import androidx.compose.animation.core.tween
-import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.TransitionBuilder
import com.android.systemui.notifications.ui.composable.Notifications
import com.android.systemui.qs.ui.composable.QuickSettings
import com.android.systemui.shade.ui.composable.Shade
+import com.android.systemui.shade.ui.composable.ShadeHeader
import kotlin.time.Duration.Companion.milliseconds
fun TransitionBuilder.lockscreenToShadeTransition(
@@ -13,15 +13,12 @@ fun TransitionBuilder.lockscreenToShadeTransition(
) {
spec = tween(durationMillis = DefaultDuration.times(durationScale).inWholeMilliseconds.toInt())
- fractionRange(end = 0.5f) {
- fade(Shade.Elements.BackgroundScrim)
- translate(
- QuickSettings.Elements.CollapsedGrid,
- Edge.Top,
- startsOutsideLayoutBounds = false,
- )
+ fractionRange(end = 0.5f) { fade(Shade.Elements.BackgroundScrim) }
+ translate(QuickSettings.Elements.Content, y = -ShadeHeader.Dimensions.CollapsedHeight * .66f)
+ fractionRange(start = 0.5f) {
+ fade(QuickSettings.Elements.Content)
+ fade(Notifications.Elements.NotificationScrim)
}
- fractionRange(start = 0.5f) { fade(Notifications.Elements.NotificationScrim) }
}
private val DefaultDuration = 500.milliseconds
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromShadeToQuickSettingsTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromShadeToQuickSettingsTransition.kt
index d5c2a03b3f9f..ffb6f3109015 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromShadeToQuickSettingsTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromShadeToQuickSettingsTransition.kt
@@ -13,10 +13,20 @@ fun TransitionBuilder.shadeToQuickSettingsTransition() {
translate(Notifications.Elements.NotificationScrim, Edge.Bottom)
timestampRange(endMillis = 83) { fade(QuickSettings.Elements.FooterActions) }
- translate(ShadeHeader.Elements.CollapsedContent, y = ShadeHeader.Dimensions.CollapsedHeight)
- translate(ShadeHeader.Elements.ExpandedContent, y = (-ShadeHeader.Dimensions.ExpandedHeight))
+ translate(
+ ShadeHeader.Elements.CollapsedContentStart,
+ y = ShadeHeader.Dimensions.CollapsedHeight
+ )
+ translate(ShadeHeader.Elements.CollapsedContentEnd, y = ShadeHeader.Dimensions.CollapsedHeight)
+ translate(
+ ShadeHeader.Elements.ExpandedContent,
+ y = -(ShadeHeader.Dimensions.ExpandedHeight - ShadeHeader.Dimensions.CollapsedHeight)
+ )
+ translate(ShadeHeader.Elements.ShadeCarrierGroup, y = -ShadeHeader.Dimensions.CollapsedHeight)
- fractionRange(end = .14f) { fade(ShadeHeader.Elements.CollapsedContent) }
+ fractionRange(end = .14f) { fade(ShadeHeader.Elements.CollapsedContentStart) }
+ fractionRange(end = .14f) { fade(ShadeHeader.Elements.CollapsedContentEnd) }
fractionRange(start = .58f) { fade(ShadeHeader.Elements.ExpandedContent) }
+ fractionRange(start = .58f) { fade(ShadeHeader.Elements.ShadeCarrierGroup) }
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
index b11edf7b47b7..d7911eac8a61 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
@@ -19,7 +19,9 @@ package com.android.systemui.shade.ui.composable
import android.view.ContextThemeWrapper
import android.view.ViewGroup
+import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
@@ -48,8 +50,10 @@ import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.android.compose.animation.scene.ElementKey
+import com.android.compose.animation.scene.LowestZIndexScenePicker
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.ValueKey
+import com.android.compose.animation.scene.animateElementFloatAsState
import com.android.compose.animation.scene.animateSceneFloatAsState
import com.android.compose.windowsizeclass.LocalWindowSizeClass
import com.android.settingslib.Utils
@@ -57,9 +61,12 @@ import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation
import com.android.systemui.common.ui.compose.windowinsets.LocalDisplayCutout
+import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.res.R
import com.android.systemui.scene.ui.composable.QuickSettings
import com.android.systemui.scene.ui.composable.Shade as ShadeKey
+import com.android.systemui.shade.ui.composable.ShadeHeader.Dimensions.CollapsedHeight
+import com.android.systemui.shade.ui.composable.ShadeHeader.Values.ClockScale
import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
import com.android.systemui.statusbar.phone.StatusBarIconController
import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager
@@ -72,13 +79,21 @@ import com.android.systemui.statusbar.policy.Clock
object ShadeHeader {
object Elements {
val ExpandedContent = ElementKey("ShadeHeaderExpandedContent")
- val CollapsedContent = ElementKey("ShadeHeaderCollapsedContent")
+ val CollapsedContentStart = ElementKey("ShadeHeaderCollapsedContentStart")
+ val CollapsedContentEnd = ElementKey("ShadeHeaderCollapsedContentEnd")
+ val PrivacyChip = ElementKey("PrivacyChip", scenePicker = LowestZIndexScenePicker)
+ val Clock = ElementKey("ShadeHeaderClock", scenePicker = LowestZIndexScenePicker)
+ val ShadeCarrierGroup = ElementKey("ShadeCarrierGroup")
}
object Keys {
val transitionProgress = ValueKey("ShadeHeaderTransitionProgress")
}
+ object Values {
+ val ClockScale = ValueKey("ShadeHeaderClockScale")
+ }
+
object Dimensions {
val CollapsedHeight = 48.dp
val ExpandedHeight = 120.dp
@@ -106,58 +121,70 @@ fun SceneScope.CollapsedShadeHeader(
cutoutLocation != CutoutLocation.CENTER || formatProgress.value > 0.5f
}
}
+ val isPrivacyChipVisible by viewModel.isPrivacyChipVisible.collectAsState()
// This layout assumes it is globally positioned at (0, 0) and is the
// same size as the screen.
Layout(
- modifier = modifier.element(ShadeHeader.Elements.CollapsedContent),
+ modifier = modifier,
contents =
listOf(
{
Row {
- AndroidView(
- factory = { context ->
- Clock(
- ContextThemeWrapper(context, R.style.TextAppearance_QS_Status),
- null
- )
- },
+ Clock(
+ scale = 1f,
+ viewModel = viewModel,
modifier = Modifier.align(Alignment.CenterVertically),
)
Spacer(modifier = Modifier.width(5.dp))
VariableDayDate(
viewModel = viewModel,
- modifier = Modifier.align(Alignment.CenterVertically),
+ modifier =
+ Modifier.element(ShadeHeader.Elements.CollapsedContentStart)
+ .align(Alignment.CenterVertically),
)
}
},
{
- Row(horizontalArrangement = Arrangement.End) {
- SystemIconContainer {
- when (LocalWindowSizeClass.current.widthSizeClass) {
- WindowWidthSizeClass.Medium,
- WindowWidthSizeClass.Expanded ->
- ShadeCarrierGroup(
- viewModel = viewModel,
- modifier = Modifier.align(Alignment.CenterVertically),
- )
- }
- StatusIcons(
+ if (isPrivacyChipVisible) {
+ Box(modifier = Modifier.height(CollapsedHeight).fillMaxWidth()) {
+ PrivacyChip(
viewModel = viewModel,
- createTintedIconManager = createTintedIconManager,
- statusBarIconController = statusBarIconController,
- useExpandedFormat = useExpandedFormat,
- modifier =
- Modifier.align(Alignment.CenterVertically)
- .padding(end = 6.dp)
- .weight(1f, fill = false)
- )
- BatteryIcon(
- createBatteryMeterViewController = createBatteryMeterViewController,
- useExpandedFormat = useExpandedFormat,
- modifier = Modifier.align(Alignment.CenterVertically),
+ modifier = Modifier.align(Alignment.CenterEnd),
)
}
+ } else {
+ Row(
+ horizontalArrangement = Arrangement.End,
+ modifier = Modifier.element(ShadeHeader.Elements.CollapsedContentEnd)
+ ) {
+ SystemIconContainer {
+ when (LocalWindowSizeClass.current.widthSizeClass) {
+ WindowWidthSizeClass.Medium,
+ WindowWidthSizeClass.Expanded ->
+ ShadeCarrierGroup(
+ viewModel = viewModel,
+ modifier = Modifier.align(Alignment.CenterVertically),
+ )
+ }
+ StatusIcons(
+ viewModel = viewModel,
+ createTintedIconManager = createTintedIconManager,
+ statusBarIconController = statusBarIconController,
+ useExpandedFormat = useExpandedFormat,
+ modifier =
+ Modifier.align(Alignment.CenterVertically)
+ .padding(end = 6.dp)
+ .weight(1f, fill = false)
+ )
+ BatteryIcon(
+ createBatteryMeterViewController =
+ createBatteryMeterViewController,
+ useExpandedFormat = useExpandedFormat,
+ modifier = Modifier.align(Alignment.CenterVertically),
+ )
+ }
+ }
}
},
),
@@ -223,72 +250,106 @@ fun SceneScope.ExpandedShadeHeader(
.unsafeCompositionState(initialValue = 1f)
val useExpandedFormat by
remember(formatProgress) { derivedStateOf { formatProgress.value > 0.5f } }
+ val isPrivacyChipVisible by viewModel.isPrivacyChipVisible.collectAsState()
- Column(
- verticalArrangement = Arrangement.Bottom,
- modifier =
- modifier
- .element(ShadeHeader.Elements.ExpandedContent)
- .fillMaxWidth()
- .defaultMinSize(minHeight = ShadeHeader.Dimensions.ExpandedHeight)
- ) {
- Row {
- AndroidView(
- factory = { context ->
- Clock(ContextThemeWrapper(context, R.style.TextAppearance_QS_Status), null)
- },
- modifier =
- Modifier.align(Alignment.CenterVertically)
- // use graphicsLayer instead of Modifier.scale to anchor transform to
- // the (start, top) corner
- .graphicsLayer(
- scaleX = 2.57f,
- scaleY = 2.57f,
- transformOrigin =
- TransformOrigin(
- when (LocalLayoutDirection.current) {
- LayoutDirection.Ltr -> 0f
- LayoutDirection.Rtl -> 1f
- },
- 0.5f
- )
- ),
- )
- Spacer(modifier = Modifier.weight(1f))
- ShadeCarrierGroup(
- viewModel = viewModel,
- modifier = Modifier.align(Alignment.CenterVertically),
- )
- }
- Spacer(modifier = Modifier.width(5.dp))
- Row {
- VariableDayDate(
- viewModel = viewModel,
- modifier = Modifier.widthIn(max = 90.dp).align(Alignment.CenterVertically),
- )
- Spacer(modifier = Modifier.weight(1f))
- SystemIconContainer {
- StatusIcons(
+ Box(modifier = modifier) {
+ if (isPrivacyChipVisible) {
+ Box(modifier = Modifier.height(CollapsedHeight).fillMaxWidth()) {
+ PrivacyChip(
viewModel = viewModel,
- createTintedIconManager = createTintedIconManager,
- statusBarIconController = statusBarIconController,
- useExpandedFormat = useExpandedFormat,
- modifier =
- Modifier.align(Alignment.CenterVertically)
- .padding(end = 6.dp)
- .weight(1f, fill = false),
+ modifier = Modifier.align(Alignment.CenterEnd),
)
- BatteryIcon(
- useExpandedFormat = useExpandedFormat,
- createBatteryMeterViewController = createBatteryMeterViewController,
- modifier = Modifier.align(Alignment.CenterVertically),
+ }
+ }
+ Column(
+ verticalArrangement = Arrangement.Bottom,
+ modifier =
+ Modifier.fillMaxWidth()
+ .defaultMinSize(minHeight = ShadeHeader.Dimensions.ExpandedHeight)
+ ) {
+ Box(modifier = Modifier.fillMaxWidth()) {
+ Box {
+ Clock(
+ scale = 2.57f,
+ viewModel = viewModel,
+ modifier = Modifier.align(Alignment.CenterStart),
+ )
+ }
+ Box(
+ modifier =
+ Modifier.element(ShadeHeader.Elements.ShadeCarrierGroup).fillMaxWidth()
+ ) {
+ ShadeCarrierGroup(
+ viewModel = viewModel,
+ modifier = Modifier.align(Alignment.CenterEnd),
+ )
+ }
+ }
+ Spacer(modifier = Modifier.width(5.dp))
+ Row(modifier = Modifier.element(ShadeHeader.Elements.ExpandedContent)) {
+ VariableDayDate(
+ viewModel = viewModel,
+ modifier = Modifier.widthIn(max = 90.dp).align(Alignment.CenterVertically),
)
+ Spacer(modifier = Modifier.weight(1f))
+ SystemIconContainer {
+ StatusIcons(
+ viewModel = viewModel,
+ createTintedIconManager = createTintedIconManager,
+ statusBarIconController = statusBarIconController,
+ useExpandedFormat = useExpandedFormat,
+ modifier =
+ Modifier.align(Alignment.CenterVertically)
+ .padding(end = 6.dp)
+ .weight(1f, fill = false),
+ )
+ BatteryIcon(
+ useExpandedFormat = useExpandedFormat,
+ createBatteryMeterViewController = createBatteryMeterViewController,
+ modifier = Modifier.align(Alignment.CenterVertically),
+ )
+ }
}
}
}
}
@Composable
+private fun SceneScope.Clock(
+ scale: Float,
+ viewModel: ShadeHeaderViewModel,
+ modifier: Modifier,
+) {
+ val layoutDirection = LocalLayoutDirection.current
+
+ Element(key = ShadeHeader.Elements.Clock, modifier = modifier) {
+ val animatedScale by animateElementFloatAsState(scale, ClockScale, canOverflow = false)
+ AndroidView(
+ factory = { context ->
+ Clock(ContextThemeWrapper(context, R.style.TextAppearance_QS_Status), null)
+ },
+ modifier =
+ modifier
+ // use graphicsLayer instead of Modifier.scale to anchor transform
+ // to the (start, top) corner
+ .graphicsLayer {
+ scaleX = animatedScale
+ scaleY = animatedScale
+ transformOrigin =
+ TransformOrigin(
+ when (layoutDirection) {
+ LayoutDirection.Ltr -> 0f
+ LayoutDirection.Rtl -> 1f
+ },
+ 0.5f
+ )
+ }
+ .clickable { viewModel.onClockClicked() }
+ )
+ }
+}
+
+@Composable
private fun BatteryIcon(
createBatteryMeterViewController: (ViewGroup, StatusBarLocation) -> BatteryMeterViewController,
useExpandedFormat: Boolean,
@@ -359,7 +420,14 @@ private fun SceneScope.StatusIcons(
) {
val carrierIconSlots =
listOf(stringResource(id = com.android.internal.R.string.status_bar_mobile))
+ val cameraSlot = stringResource(id = com.android.internal.R.string.status_bar_camera)
+ val micSlot = stringResource(id = com.android.internal.R.string.status_bar_microphone)
+ val locationSlot = stringResource(id = com.android.internal.R.string.status_bar_location)
+
val isSingleCarrier by viewModel.isSingleCarrier.collectAsState()
+ val isPrivacyChipEnabled by viewModel.isPrivacyChipEnabled.collectAsState()
+ val isMicCameraIndicationEnabled by viewModel.isMicCameraIndicationEnabled.collectAsState()
+ val isLocationIndicationEnabled by viewModel.isLocationIndicationEnabled.collectAsState()
AndroidView(
factory = { context ->
@@ -382,6 +450,25 @@ private fun SceneScope.StatusIcons(
} else {
iconContainer.addIgnoredSlots(carrierIconSlots)
}
+
+ if (isPrivacyChipEnabled) {
+ if (isMicCameraIndicationEnabled) {
+ iconContainer.addIgnoredSlot(cameraSlot)
+ iconContainer.addIgnoredSlot(micSlot)
+ } else {
+ iconContainer.removeIgnoredSlot(cameraSlot)
+ iconContainer.removeIgnoredSlot(micSlot)
+ }
+ if (isLocationIndicationEnabled) {
+ iconContainer.addIgnoredSlot(locationSlot)
+ } else {
+ iconContainer.removeIgnoredSlot(locationSlot)
+ }
+ } else {
+ iconContainer.removeIgnoredSlot(cameraSlot)
+ iconContainer.removeIgnoredSlot(micSlot)
+ iconContainer.removeIgnoredSlot(locationSlot)
+ }
},
modifier = modifier,
)
@@ -394,7 +481,28 @@ private fun SystemIconContainer(
) {
// TODO(b/298524053): add hover state for this container
Row(
- modifier = modifier.height(ShadeHeader.Dimensions.CollapsedHeight),
+ modifier = modifier.height(CollapsedHeight),
content = content,
)
}
+
+@Composable
+private fun SceneScope.PrivacyChip(
+ viewModel: ShadeHeaderViewModel,
+ modifier: Modifier = Modifier,
+) {
+ val privacyList by viewModel.privacyItems.collectAsState()
+
+ AndroidView(
+ factory = { context ->
+ val view =
+ OngoingPrivacyChip(context, null).also { privacyChip ->
+ privacyChip.privacyList = privacyList
+ privacyChip.setOnClickListener { viewModel.onPrivacyChipClicked(privacyChip) }
+ }
+ view
+ },
+ update = { it.privacyList = privacyList },
+ modifier = modifier.element(ShadeHeader.Elements.PrivacyChip),
+ )
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
index 25df3e49b618..2e0ce42ee713 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
@@ -28,6 +28,8 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
@@ -40,14 +42,16 @@ import androidx.compose.ui.unit.dp
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.LowestZIndexScenePicker
import com.android.compose.animation.scene.SceneScope
+import com.android.compose.animation.scene.animateSceneFloatAsState
+import com.android.compose.modifiers.thenIf
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.media.controls.ui.MediaCarouselController
-import com.android.systemui.media.controls.ui.MediaHierarchyManager
-import com.android.systemui.media.controls.ui.MediaHost
-import com.android.systemui.media.controls.ui.MediaHostState
import com.android.systemui.media.controls.ui.composable.MediaCarousel
+import com.android.systemui.media.controls.ui.controller.MediaCarouselController
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL
import com.android.systemui.notifications.ui.composable.NotificationScrollingStack
import com.android.systemui.qs.ui.composable.QuickSettings
@@ -73,7 +77,6 @@ import kotlinx.coroutines.flow.stateIn
object Shade {
object Elements {
- val QuickSettings = ElementKey("ShadeQuickSettings")
val MediaCarousel = ElementKey("ShadeMediaCarousel")
val BackgroundScrim =
ElementKey("ShadeBackgroundScrim", scenePicker = LowestZIndexScenePicker)
@@ -160,6 +163,9 @@ private fun SceneScope.ShadeScene(
val density = LocalDensity.current
val layoutWidth = remember { mutableStateOf(0) }
val maxNotifScrimTop = remember { mutableStateOf(0f) }
+ val tileSquishiness by
+ animateSceneFloatAsState(value = 1f, key = QuickSettings.SharedValues.TilesSquishiness)
+ val isClickable by viewModel.isClickable.collectAsState()
Box(
modifier =
@@ -175,8 +181,9 @@ private fun SceneScope.ShadeScene(
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier =
- Modifier.fillMaxWidth()
- .clickable(onClick = { viewModel.onContentClicked() })
+ Modifier.fillMaxWidth().thenIf(isClickable) {
+ Modifier.clickable(onClick = { viewModel.onContentClicked() })
+ }
) {
CollapsedShadeHeader(
viewModel = viewModel.shadeHeaderViewModel,
@@ -190,7 +197,11 @@ private fun SceneScope.ShadeScene(
)
QuickSettings(
viewModel.qsSceneAdapter,
- { viewModel.qsSceneAdapter.qqsHeight },
+ {
+ (viewModel.qsSceneAdapter.qqsHeight * tileSquishiness)
+ .roundToInt()
+ },
+ squishiness = tileSquishiness,
)
if (viewModel.isMediaVisible()) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt b/packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt
index 7d692cc17015..d66bada04297 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone
import android.content.Context
+import androidx.annotation.GravityInt
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
@@ -45,14 +46,17 @@ import com.android.compose.theme.PlatformTheme
* @param context the [Context] in which the dialog will be constructed.
* @param dismissOnDeviceLock whether the dialog should be automatically dismissed when the device
* is locked (true by default).
+ * @param dialogGravity is one of the [android.view.Gravity] and determines dialog position on the
+ * screen.
*/
fun SystemUIDialogFactory.create(
context: Context = this.applicationContext,
theme: Int = SystemUIDialog.DEFAULT_THEME,
dismissOnDeviceLock: Boolean = SystemUIDialog.DEFAULT_DISMISS_ON_DEVICE_LOCK,
+ @GravityInt dialogGravity: Int? = null,
content: @Composable (SystemUIDialog) -> Unit,
): ComponentSystemUIDialog {
- val dialog = create(context, theme, dismissOnDeviceLock)
+ val dialog = create(context, theme, dismissOnDeviceLock, dialogGravity)
// Create the dialog so that it is properly constructed before we set the Compose content.
// Otherwise, the ComposeView won't render properly.
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/AncModule.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/AncModule.kt
new file mode 100644
index 000000000000..ccb5d367c357
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/AncModule.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.anc
+
+import com.android.systemui.volume.panel.component.anc.domain.AncAvailabilityCriteria
+import com.android.systemui.volume.panel.component.anc.ui.composable.AncPopup
+import com.android.systemui.volume.panel.component.anc.ui.viewmodel.AncViewModel
+import com.android.systemui.volume.panel.component.button.ui.composable.ButtonComponent
+import com.android.systemui.volume.panel.component.shared.model.VolumePanelComponents
+import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
+import com.android.systemui.volume.panel.shared.model.VolumePanelUiComponent
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+import dagger.multibindings.IntoMap
+import dagger.multibindings.StringKey
+
+/** Dagger module, that provides Active Noise Cancellation Volume Panel UI functionality. */
+@Module
+interface AncModule {
+
+ @Binds
+ @IntoMap
+ @StringKey(VolumePanelComponents.ANC)
+ fun bindComponentAvailabilityCriteria(
+ criteria: AncAvailabilityCriteria
+ ): ComponentAvailabilityCriteria
+
+ companion object {
+
+ @Provides
+ @IntoMap
+ @StringKey(VolumePanelComponents.ANC)
+ fun provideVolumePanelUiComponent(
+ viewModel: AncViewModel,
+ popup: AncPopup,
+ ): VolumePanelUiComponent = ButtonComponent(viewModel.button, popup::show)
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncPopup.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncPopup.kt
new file mode 100644
index 000000000000..8ac84ff819eb
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncPopup.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.anc.ui.composable
+
+import android.content.Context
+import android.view.View
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.SideEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.viewinterop.AndroidView
+import androidx.slice.Slice
+import androidx.slice.widget.SliceView
+import com.android.systemui.animation.Expandable
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.volume.panel.component.anc.ui.viewmodel.AncViewModel
+import com.android.systemui.volume.panel.component.popup.ui.composable.VolumePanelPopup
+import javax.inject.Inject
+
+/** ANC popup up displaying ANC control [Slice]. */
+class AncPopup
+@Inject
+constructor(
+ private val volumePanelPopup: VolumePanelPopup,
+ private val viewModel: AncViewModel,
+) {
+
+ /** Shows a popup with the [expandable] animation. */
+ fun show(expandable: Expandable) {
+ volumePanelPopup.show(expandable, { Title() }, { Content(it) })
+ }
+
+ @Composable
+ private fun Title() {
+ Text(
+ text = stringResource(R.string.volume_panel_noise_control_title),
+ style = MaterialTheme.typography.titleMedium,
+ textAlign = TextAlign.Center,
+ maxLines = 1,
+ )
+ }
+
+ @Composable
+ private fun Content(dialog: SystemUIDialog) {
+ val slice: Slice? by viewModel.slice.collectAsState()
+
+ if (slice == null) {
+ SideEffect { dialog.dismiss() }
+ return
+ }
+
+ AndroidView<SliceView>(
+ modifier = Modifier.fillMaxWidth(),
+ factory = { context: Context ->
+ SliceView(context).apply {
+ mode = SliceView.MODE_LARGE
+ isScrollable = false
+ importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
+ setShowTitleItems(true)
+ addOnLayoutChangeListener(
+ OnWidthChangedLayoutListener(viewModel::changeSliceWidth)
+ )
+ }
+ },
+ update = { sliceView: SliceView -> sliceView.slice = slice }
+ )
+ }
+
+ private class OnWidthChangedLayoutListener(private val widthChanged: (Int) -> Unit) :
+ View.OnLayoutChangeListener {
+ override fun onLayoutChange(
+ v: View?,
+ left: Int,
+ top: Int,
+ right: Int,
+ bottom: Int,
+ oldLeft: Int,
+ oldTop: Int,
+ oldRight: Int,
+ oldBottom: Int
+ ) {
+ val newWidth = right - left
+ val oldWidth = oldRight - oldLeft
+ if (oldWidth != newWidth) {
+ widthChanged(newWidth)
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/bottombar/BottomBarModule.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/bottombar/BottomBarModule.kt
index 43d545368536..236aee217f16 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/bottombar/BottomBarModule.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/bottombar/BottomBarModule.kt
@@ -32,7 +32,7 @@ interface BottomBarModule {
@Binds
@IntoMap
@StringKey(VolumePanelComponents.BOTTOM_BAR)
- fun bindMediaVolumeSliderComponent(component: BottomBarComponent): VolumePanelUiComponent
+ fun bindVolumePanelUiComponent(component: BottomBarComponent): VolumePanelUiComponent
@Binds
@IntoMap
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/bottombar/ui/BottomBarComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/bottombar/ui/BottomBarComponent.kt
index 03c07f714541..d40126198c33 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/bottombar/ui/BottomBarComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/bottombar/ui/BottomBarComponent.kt
@@ -20,6 +20,8 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@@ -45,11 +47,17 @@ constructor(
@Composable
override fun VolumePanelComposeScope.Content(modifier: Modifier) {
Row(
- modifier = modifier.height(48.dp).fillMaxWidth(),
+ modifier = modifier.height(if (isLargeScreen) 54.dp else 48.dp).fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
- PlatformOutlinedButton(onClick = viewModel::onSettingsClicked) {
+ PlatformOutlinedButton(
+ onClick = viewModel::onSettingsClicked,
+ colors =
+ ButtonDefaults.outlinedButtonColors(
+ contentColor = MaterialTheme.colorScheme.onSurface,
+ ),
+ ) {
Text(text = stringResource(R.string.volume_panel_dialog_settings_button))
}
PlatformButton(onClick = viewModel::onDoneClicked) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt
new file mode 100644
index 000000000000..5f7bd47f2a1b
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.button.ui.composable
+
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.dp
+import com.android.compose.animation.Expandable
+import com.android.systemui.animation.Expandable
+import com.android.systemui.common.ui.compose.Icon
+import com.android.systemui.volume.panel.component.button.ui.viewmodel.ButtonViewModel
+import com.android.systemui.volume.panel.ui.composable.ComposeVolumePanelUiComponent
+import com.android.systemui.volume.panel.ui.composable.VolumePanelComposeScope
+import kotlinx.coroutines.flow.StateFlow
+
+/** [ComposeVolumePanelUiComponent] implementing a clickable button from a bottom row. */
+class ButtonComponent(
+ private val viewModelFlow: StateFlow<ButtonViewModel?>,
+ private val onClick: (Expandable) -> Unit
+) : ComposeVolumePanelUiComponent {
+
+ @Composable
+ override fun VolumePanelComposeScope.Content(modifier: Modifier) {
+ val viewModelByState by viewModelFlow.collectAsState()
+ val viewModel = viewModelByState ?: return
+
+ Column(
+ modifier = modifier,
+ verticalArrangement = Arrangement.spacedBy(12.dp),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ Expandable(
+ modifier = Modifier.height(64.dp).fillMaxWidth(),
+ color = MaterialTheme.colorScheme.primaryContainer,
+ shape = RoundedCornerShape(28.dp),
+ contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
+ borderStroke = BorderStroke(8.dp, MaterialTheme.colorScheme.surface),
+ onClick = onClick,
+ ) {
+ Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
+ Icon(modifier = Modifier.size(24.dp), icon = viewModel.icon)
+ }
+ }
+ Text(
+ text = viewModel.label.toString(),
+ style = MaterialTheme.typography.labelMedium,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
index 228111dae8d9..dfee684c417f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
@@ -22,6 +22,7 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedIconToggleButton
@@ -39,6 +40,7 @@ import com.android.systemui.volume.panel.ui.composable.ComposeVolumePanelUiCompo
import com.android.systemui.volume.panel.ui.composable.VolumePanelComposeScope
import kotlinx.coroutines.flow.StateFlow
+/** [ComposeVolumePanelUiComponent] implementing a toggleable button from a bottom row. */
class ToggleButtonComponent(
private val viewModelFlow: StateFlow<ToggleButtonViewModel?>,
private val onCheckedChange: (isChecked: Boolean) -> Unit
@@ -57,6 +59,7 @@ class ToggleButtonComponent(
modifier = Modifier.height(64.dp).fillMaxWidth(),
checked = viewModel.isChecked,
onCheckedChange = onCheckedChange,
+ shape = RoundedCornerShape(28.dp),
colors =
IconButtonDefaults.outlinedIconToggleButtonColors(
containerColor = MaterialTheme.colorScheme.surface,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/MediaOutputModule.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/MediaOutputModule.kt
new file mode 100644
index 000000000000..c73656eb1ec5
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/MediaOutputModule.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.mediaoutput
+
+import com.android.systemui.volume.panel.component.mediaoutput.domain.MediaOutputAvailabilityCriteria
+import com.android.systemui.volume.panel.component.mediaoutput.ui.composable.MediaOutputComponent
+import com.android.systemui.volume.panel.component.shared.model.VolumePanelComponents
+import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
+import com.android.systemui.volume.panel.shared.model.VolumePanelUiComponent
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoMap
+import dagger.multibindings.StringKey
+
+/** Dagger module, that provides media output Volume Panel UI functionality. */
+@Module
+interface MediaOutputModule {
+
+ @Binds
+ @IntoMap
+ @StringKey(VolumePanelComponents.MEDIA_OUTPUT)
+ fun bindVolumePanelUiComponent(component: MediaOutputComponent): VolumePanelUiComponent
+
+ @Binds
+ @IntoMap
+ @StringKey(VolumePanelComponents.MEDIA_OUTPUT)
+ fun bindComponentAvailabilityCriteria(
+ criteria: MediaOutputAvailabilityCriteria
+ ): ComponentAvailabilityCriteria
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt
new file mode 100644
index 000000000000..d49fed5d6e10
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.mediaoutput.ui.composable
+
+import androidx.compose.animation.AnimatedContent
+import androidx.compose.animation.core.snap
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.core.updateTransition
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.animation.scaleIn
+import androidx.compose.animation.scaleOut
+import androidx.compose.animation.slideInVertically
+import androidx.compose.animation.slideOutVertically
+import androidx.compose.animation.togetherWith
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.dp
+import com.android.compose.animation.Expandable
+import com.android.systemui.common.ui.compose.Icon
+import com.android.systemui.common.ui.compose.toColor
+import com.android.systemui.volume.panel.component.mediaoutput.ui.viewmodel.ConnectedDeviceViewModel
+import com.android.systemui.volume.panel.component.mediaoutput.ui.viewmodel.DeviceIconViewModel
+import com.android.systemui.volume.panel.component.mediaoutput.ui.viewmodel.MediaOutputViewModel
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import com.android.systemui.volume.panel.ui.composable.ComposeVolumePanelUiComponent
+import com.android.systemui.volume.panel.ui.composable.VolumePanelComposeScope
+import javax.inject.Inject
+
+@VolumePanelScope
+class MediaOutputComponent
+@Inject
+constructor(
+ private val viewModel: MediaOutputViewModel,
+) : ComposeVolumePanelUiComponent {
+
+ @Composable
+ override fun VolumePanelComposeScope.Content(modifier: Modifier) {
+ val connectedDeviceViewModel: ConnectedDeviceViewModel? by
+ viewModel.connectedDeviceViewModel.collectAsState()
+ val deviceIconViewModel: DeviceIconViewModel? by
+ viewModel.deviceIconViewModel.collectAsState()
+
+ Expandable(
+ modifier = Modifier.fillMaxWidth().height(80.dp),
+ color = MaterialTheme.colorScheme.surface,
+ shape = RoundedCornerShape(28.dp),
+ onClick = { viewModel.onBarClick(it) },
+ ) {
+ Row {
+ connectedDeviceViewModel?.let { ConnectedDeviceText(it) }
+
+ deviceIconViewModel?.let { ConnectedDeviceIcon(it) }
+ }
+ }
+ }
+
+ @Composable
+ private fun RowScope.ConnectedDeviceText(connectedDeviceViewModel: ConnectedDeviceViewModel) {
+ Column(
+ modifier =
+ Modifier.weight(1f)
+ .padding(start = 24.dp, top = 20.dp, bottom = 20.dp)
+ .fillMaxHeight(),
+ verticalArrangement = Arrangement.spacedBy(4.dp),
+ ) {
+ Text(
+ connectedDeviceViewModel.label.toString(),
+ style = MaterialTheme.typography.labelMedium,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ )
+ connectedDeviceViewModel.deviceName?.let {
+ Text(
+ it.toString(),
+ style = MaterialTheme.typography.titleMedium,
+ color = MaterialTheme.colorScheme.onSurface,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ )
+ }
+ }
+ }
+
+ @Composable
+ private fun ConnectedDeviceIcon(deviceIconViewModel: DeviceIconViewModel) {
+ val transition = updateTransition(deviceIconViewModel, label = "MediaOutputIconTransition")
+ Box(
+ modifier = Modifier.padding(16.dp).fillMaxHeight().aspectRatio(1f),
+ contentAlignment = Alignment.Center
+ ) {
+ transition.AnimatedContent(
+ contentKey = { it.backgroundColor },
+ transitionSpec = {
+ if (targetState is DeviceIconViewModel.IsPlaying) {
+ scaleIn(
+ initialScale = 0.9f,
+ animationSpec = isPlayingInIconBackgroundSpec(),
+ ) + fadeIn(animationSpec = isPlayingInIconBackgroundSpec()) togetherWith
+ fadeOut(animationSpec = snap())
+ } else {
+ fadeIn(animationSpec = snap(delayMillis = 900)) togetherWith
+ scaleOut(
+ targetScale = 0.9f,
+ animationSpec = isPlayingOutSpec(),
+ ) + fadeOut(animationSpec = isPlayingOutSpec())
+ }
+ }
+ ) { targetViewModel ->
+ Expandable(
+ modifier = Modifier.fillMaxSize(),
+ color = targetViewModel.backgroundColor.toColor(),
+ shape = RoundedCornerShape(12.dp),
+ onClick = { viewModel.onDeviceClick(it) },
+ ) {}
+ }
+ transition.AnimatedContent(
+ contentKey = { it.icon },
+ transitionSpec = {
+ if (targetState is DeviceIconViewModel.IsPlaying) {
+ fadeIn(animationSpec = snap(delayMillis = 700)) togetherWith
+ slideOutVertically(
+ targetOffsetY = { it },
+ animationSpec = isPlayingInIconSpec(),
+ ) + fadeOut(animationSpec = isNotPlayingOutIconSpec())
+ } else {
+ slideInVertically(
+ initialOffsetY = { it },
+ animationSpec = isNotPlayingInIconSpec(),
+ ) + fadeIn(animationSpec = isNotPlayingInIconSpec()) togetherWith
+ fadeOut(animationSpec = isPlayingOutSpec())
+ }
+ }
+ ) {
+ Icon(
+ icon = it.icon,
+ tint = it.iconColor.toColor(),
+ modifier = Modifier.padding(12.dp).fillMaxSize(),
+ )
+ }
+ }
+ }
+}
+
+private fun <T> isPlayingOutSpec() = tween<T>(durationMillis = 400, delayMillis = 500)
+
+private fun <T> isPlayingInIconSpec() = tween<T>(durationMillis = 400, delayMillis = 300)
+
+private fun <T> isPlayingInIconBackgroundSpec() = tween<T>(durationMillis = 400, delayMillis = 700)
+
+private fun <T> isNotPlayingOutIconSpec() = tween<T>(durationMillis = 400, delayMillis = 300)
+
+private fun <T> isNotPlayingInIconSpec() = tween<T>(durationMillis = 400, delayMillis = 900)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopup.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopup.kt
new file mode 100644
index 000000000000..89251939f77a
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopup.kt
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.popup.ui.composable
+
+import android.view.Gravity
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.material3.IconButtonDefaults
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.android.compose.PlatformIconButton
+import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.animation.Expandable
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.statusbar.phone.SystemUIDialogFactory
+import com.android.systemui.statusbar.phone.create
+import javax.inject.Inject
+
+/** Volume panel bottom popup menu. */
+class VolumePanelPopup
+@Inject
+constructor(
+ private val dialogFactory: SystemUIDialogFactory,
+ private val dialogTransitionAnimator: DialogTransitionAnimator,
+) {
+
+ /**
+ * Shows a popup with the [expandable] animation.
+ *
+ * @param title is shown on the top of the popup
+ * @param content is the popup body
+ */
+ fun show(
+ expandable: Expandable,
+ title: @Composable (SystemUIDialog) -> Unit,
+ content: @Composable (SystemUIDialog) -> Unit,
+ ) {
+ val dialog =
+ dialogFactory.create(
+ theme = R.style.Theme_VolumePanelActivity_Popup,
+ dialogGravity = Gravity.BOTTOM,
+ ) {
+ PopupComposable(it, title, content)
+ }
+ val controller = expandable.dialogTransitionController()
+ if (controller == null) {
+ dialog.show()
+ } else {
+ dialogTransitionAnimator.show(dialog, controller)
+ }
+ }
+
+ @Composable
+ private fun PopupComposable(
+ dialog: SystemUIDialog,
+ title: @Composable (SystemUIDialog) -> Unit,
+ content: @Composable (SystemUIDialog) -> Unit,
+ ) {
+ Box(Modifier.fillMaxWidth()) {
+ Column(
+ modifier = Modifier.fillMaxWidth().padding(vertical = 20.dp),
+ verticalArrangement = Arrangement.spacedBy(20.dp),
+ ) {
+ Box(
+ modifier =
+ Modifier.padding(horizontal = 80.dp).fillMaxWidth().wrapContentHeight(),
+ contentAlignment = Alignment.Center
+ ) {
+ title(dialog)
+ }
+
+ Box(
+ modifier =
+ Modifier.padding(horizontal = 16.dp).fillMaxWidth().wrapContentHeight(),
+ contentAlignment = Alignment.Center
+ ) {
+ content(dialog)
+ }
+ }
+
+ PlatformIconButton(
+ modifier = Modifier.align(Alignment.TopEnd).size(64.dp).padding(20.dp),
+ iconResource = R.drawable.ic_close,
+ contentDescription = null,
+ onClick = { dialog.dismiss() },
+ colors =
+ IconButtonDefaults.iconButtonColors(
+ contentColor = MaterialTheme.colorScheme.outline
+ )
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/VolumeSlidersModule.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/VolumeSlidersModule.kt
new file mode 100644
index 000000000000..453ff026a8a0
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/VolumeSlidersModule.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.volume
+
+import com.android.systemui.volume.panel.component.shared.model.VolumePanelComponents
+import com.android.systemui.volume.panel.component.volume.ui.composable.VolumeSlidersComponent
+import com.android.systemui.volume.panel.domain.AlwaysAvailableCriteria
+import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
+import com.android.systemui.volume.panel.shared.model.VolumePanelUiComponent
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoMap
+import dagger.multibindings.StringKey
+
+@Module
+interface VolumeSlidersModule {
+
+ @Binds
+ @IntoMap
+ @StringKey(VolumePanelComponents.VOLUME_SLIDERS)
+ fun bindVolumePanelUiComponent(component: VolumeSlidersComponent): VolumePanelUiComponent
+
+ @Binds
+ @IntoMap
+ @StringKey(VolumePanelComponents.VOLUME_SLIDERS)
+ fun bindComponentAvailabilityCriteria(
+ criteria: AlwaysAvailableCriteria
+ ): ComponentAvailabilityCriteria
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
new file mode 100644
index 000000000000..a197a4b0fd55
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.volume.ui.composable
+
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.EnterTransition
+import androidx.compose.animation.ExitTransition
+import androidx.compose.animation.ExperimentalAnimationApi
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.core.updateTransition
+import androidx.compose.animation.expandVertically
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.animation.scaleIn
+import androidx.compose.animation.scaleOut
+import androidx.compose.animation.shrinkVertically
+import androidx.compose.animation.slideInVertically
+import androidx.compose.animation.slideOutVertically
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.IconButtonDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+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.res.painterResource
+import androidx.compose.ui.unit.dp
+import com.android.compose.PlatformSliderColors
+import com.android.systemui.res.R
+import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.SliderViewModel
+
+private const val EXPAND_DURATION_MILLIS = 500
+private const val COLLAPSE_DURATION_MILLIS = 300
+
+/** Volume sliders laid out in a collapsable column */
+@OptIn(ExperimentalAnimationApi::class)
+@Composable
+fun ColumnVolumeSliders(
+ viewModels: List<SliderViewModel>,
+ sliderColors: PlatformSliderColors,
+ isExpandable: Boolean,
+ modifier: Modifier = Modifier,
+) {
+ require(viewModels.isNotEmpty())
+ var isExpanded: Boolean by remember { mutableStateOf(false) }
+ val transition = updateTransition(isExpanded, label = "CollapsableSliders")
+ Column(modifier = modifier) {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.spacedBy(8.dp),
+ ) {
+ val sliderViewModel: SliderViewModel = viewModels.first()
+ val sliderState by viewModels.first().slider.collectAsState()
+ VolumeSlider(
+ modifier = Modifier.weight(1f),
+ state = sliderState,
+ onValueChangeFinished = { sliderViewModel.onValueChangeFinished(sliderState, it) },
+ sliderColors = sliderColors,
+ )
+
+ if (isExpandable) {
+ ExpandButton(
+ isExpanded = isExpanded,
+ onExpandedChanged = { isExpanded = it },
+ sliderColors,
+ Modifier,
+ )
+ }
+ }
+ transition.AnimatedVisibility(
+ visible = { it },
+ enter =
+ expandVertically(
+ animationSpec = tween(durationMillis = EXPAND_DURATION_MILLIS),
+ expandFrom = Alignment.CenterVertically,
+ ),
+ exit =
+ shrinkVertically(
+ animationSpec = tween(durationMillis = COLLAPSE_DURATION_MILLIS),
+ shrinkTowards = Alignment.CenterVertically,
+ ),
+ ) {
+ Column(modifier = Modifier.fillMaxWidth()) {
+ for (index in 1..viewModels.lastIndex) {
+ val sliderViewModel: SliderViewModel = viewModels[index]
+ val sliderState by sliderViewModel.slider.collectAsState()
+ transition.AnimatedVisibility(
+ visible = { it },
+ enter = enterTransition(index = index, totalCount = viewModels.size),
+ exit = exitTransition(index = index, totalCount = viewModels.size)
+ ) {
+ VolumeSlider(
+ modifier = Modifier.fillMaxWidth().padding(top = 16.dp),
+ state = sliderState,
+ onValueChangeFinished = {
+ sliderViewModel.onValueChangeFinished(sliderState, it)
+ },
+ sliderColors = sliderColors,
+ )
+ }
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun ExpandButton(
+ isExpanded: Boolean,
+ onExpandedChanged: (Boolean) -> Unit,
+ sliderColors: PlatformSliderColors,
+ modifier: Modifier = Modifier,
+) {
+ IconButton(
+ modifier = modifier.size(64.dp),
+ onClick = { onExpandedChanged(!isExpanded) },
+ colors =
+ IconButtonDefaults.filledIconButtonColors(
+ containerColor = sliderColors.indicatorColor,
+ contentColor = sliderColors.iconColor
+ ),
+ ) {
+ Icon(
+ painter =
+ painterResource(
+ if (isExpanded) {
+ R.drawable.ic_filled_arrow_down
+ } else {
+ R.drawable.ic_filled_arrow_up
+ }
+ ),
+ contentDescription = null,
+ )
+ }
+}
+
+private fun enterTransition(index: Int, totalCount: Int): EnterTransition {
+ val enterDelay = ((totalCount - index + 1) * 10).coerceAtLeast(0)
+ val enterDuration = (EXPAND_DURATION_MILLIS - enterDelay).coerceAtLeast(100)
+ return slideInVertically(
+ initialOffsetY = { (it * 0.25).toInt() },
+ animationSpec = tween(durationMillis = enterDuration, delayMillis = enterDelay),
+ ) +
+ scaleIn(
+ initialScale = 0.9f,
+ animationSpec = tween(durationMillis = enterDuration, delayMillis = enterDelay),
+ ) +
+ expandVertically(
+ initialHeight = { (it * 0.65).toInt() },
+ animationSpec = tween(durationMillis = enterDuration, delayMillis = enterDelay),
+ clip = false,
+ expandFrom = Alignment.CenterVertically,
+ ) +
+ fadeIn(
+ animationSpec = tween(durationMillis = enterDuration, delayMillis = enterDelay),
+ )
+}
+
+private fun exitTransition(index: Int, totalCount: Int): ExitTransition {
+ val exitDuration = (COLLAPSE_DURATION_MILLIS - (totalCount - index + 1) * 10).coerceAtLeast(100)
+ return slideOutVertically(
+ targetOffsetY = { (it * 0.25).toInt() },
+ animationSpec = tween(durationMillis = exitDuration),
+ ) +
+ scaleOut(
+ targetScale = 0.9f,
+ animationSpec = tween(durationMillis = exitDuration),
+ ) +
+ shrinkVertically(
+ targetHeight = { (it * 0.65).toInt() },
+ animationSpec = tween(durationMillis = exitDuration),
+ clip = false,
+ shrinkTowards = Alignment.CenterVertically,
+ ) +
+ fadeOut(animationSpec = tween(durationMillis = exitDuration))
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/GridVolumeSliders.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/GridVolumeSliders.kt
new file mode 100644
index 000000000000..910ee7285bdb
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/GridVolumeSliders.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.volume.ui.composable
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.android.compose.PlatformSliderColors
+import com.android.compose.grid.VerticalGrid
+import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.SliderViewModel
+
+@Composable
+fun GridVolumeSliders(
+ viewModels: List<SliderViewModel>,
+ sliderColors: PlatformSliderColors,
+ modifier: Modifier = Modifier,
+) {
+ require(viewModels.isNotEmpty())
+ VerticalGrid(
+ modifier = modifier,
+ columns = 2,
+ verticalSpacing = 16.dp,
+ horizontalSpacing = 24.dp,
+ ) {
+ for (sliderViewModel in viewModels) {
+ val sliderState = sliderViewModel.slider.collectAsState().value
+ VolumeSlider(
+ modifier = Modifier.fillMaxWidth(),
+ state = sliderState,
+ onValueChangeFinished = { sliderViewModel.onValueChangeFinished(sliderState, it) },
+ sliderColors = sliderColors,
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
new file mode 100644
index 000000000000..5925b1482e77
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.volume.ui.composable
+
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.animateContentSize
+import androidx.compose.animation.expandVertically
+import androidx.compose.animation.shrinkVertically
+import androidx.compose.foundation.layout.Column
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import com.android.compose.PlatformSlider
+import com.android.compose.PlatformSliderColors
+import com.android.systemui.common.ui.compose.Icon
+import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.SliderState
+
+@Composable
+fun VolumeSlider(
+ state: SliderState,
+ onValueChangeFinished: (Float) -> Unit,
+ modifier: Modifier = Modifier,
+ sliderColors: PlatformSliderColors,
+) {
+ var value by remember { mutableFloatStateOf(state.value) }
+ PlatformSlider(
+ modifier = modifier,
+ value = value,
+ valueRange = state.valueRange,
+ onValueChange = { value = it },
+ onValueChangeFinished = { onValueChangeFinished(value) },
+ enabled = state.isEnabled,
+ icon = { isDragging ->
+ if (isDragging) {
+ Text(text = value.toInt().toString())
+ } else {
+ state.icon?.let { Icon(icon = it) }
+ }
+ },
+ colors = sliderColors,
+ label = {
+ Column(modifier = Modifier.animateContentSize()) {
+ Text(state.label, style = MaterialTheme.typography.titleMedium)
+
+ state.disabledMessage?.let { message ->
+ AnimatedVisibility(
+ !state.isEnabled,
+ enter = expandVertically { it },
+ exit = shrinkVertically { it },
+ ) {
+ Text(text = message, style = MaterialTheme.typography.bodySmall)
+ }
+ }
+ }
+ }
+ )
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlidersComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlidersComponent.kt
new file mode 100644
index 000000000000..6213dc5f63c9
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlidersComponent.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.volume.ui.composable
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import com.android.compose.PlatformSliderDefaults
+import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.SliderViewModel
+import com.android.systemui.volume.panel.component.volume.ui.viewmodel.AudioVolumeComponentViewModel
+import com.android.systemui.volume.panel.ui.composable.ComposeVolumePanelUiComponent
+import com.android.systemui.volume.panel.ui.composable.VolumePanelComposeScope
+import javax.inject.Inject
+
+class VolumeSlidersComponent
+@Inject
+constructor(
+ private val viewModel: AudioVolumeComponentViewModel,
+) : ComposeVolumePanelUiComponent {
+
+ @Composable
+ override fun VolumePanelComposeScope.Content(modifier: Modifier) {
+ val sliderViewModels: List<SliderViewModel> by viewModel.sliderViewModels.collectAsState()
+ if (sliderViewModels.isEmpty()) {
+ return
+ }
+ if (isLargeScreen) {
+ GridVolumeSliders(
+ viewModels = sliderViewModels,
+ sliderColors = PlatformSliderDefaults.defaultPlatformSliderColors(),
+ modifier = modifier.fillMaxWidth(),
+ )
+ } else {
+ ColumnVolumeSliders(
+ viewModels = sliderViewModels,
+ sliderColors = PlatformSliderDefaults.defaultPlatformSliderColors(),
+ isExpandable = true,
+ modifier = modifier.fillMaxWidth(),
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/HorizontalVolumePanelContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/HorizontalVolumePanelContent.kt
new file mode 100644
index 000000000000..0a651c898aba
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/HorizontalVolumePanelContent.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.ui.composable
+
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.android.systemui.volume.panel.ui.layout.ComponentsLayout
+
+@Composable
+fun VolumePanelComposeScope.HorizontalVolumePanelContent(
+ layout: ComponentsLayout,
+ modifier: Modifier = Modifier,
+) {
+ val spacing = 20.dp
+ Row(modifier = modifier, horizontalArrangement = Arrangement.spacedBy(space = spacing)) {
+ Column(
+ modifier = Modifier.weight(1f),
+ verticalArrangement = Arrangement.spacedBy(spacing)
+ ) {
+ for (component in layout.contentComponents) {
+ AnimatedVisibility(component.isVisible) {
+ with(component.component as ComposeVolumePanelUiComponent) { Content(Modifier) }
+ }
+ }
+ }
+
+ Column(
+ modifier = Modifier.weight(1f),
+ verticalArrangement = Arrangement.spacedBy(space = spacing, alignment = Alignment.Top)
+ ) {
+ for (component in layout.headerComponents) {
+ AnimatedVisibility(visible = component.isVisible) {
+ with(component.component as ComposeVolumePanelUiComponent) { Content(Modifier) }
+ }
+ }
+ Row(
+ modifier = Modifier.fillMaxWidth().wrapContentHeight(),
+ horizontalArrangement = Arrangement.spacedBy(spacing),
+ ) {
+ for (component in layout.footerComponents) {
+ AnimatedVisibility(
+ visible = component.isVisible,
+ modifier = Modifier.weight(1f),
+ ) {
+ with(component.component as ComposeVolumePanelUiComponent) {
+ Content(Modifier)
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt
index e8d59660cebf..4d073798c70c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt
@@ -36,6 +36,11 @@ fun VolumePanelComposeScope.VerticalVolumePanelContent(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(20.dp),
) {
+ for (component in layout.headerComponents) {
+ AnimatedVisibility(component.isVisible) {
+ with(component.component as ComposeVolumePanelUiComponent) { Content(Modifier) }
+ }
+ }
for (component in layout.contentComponents) {
AnimatedVisibility(component.isVisible) {
with(component.component as ComposeVolumePanelUiComponent) { Content(Modifier) }
@@ -44,11 +49,16 @@ fun VolumePanelComposeScope.VerticalVolumePanelContent(
if (layout.footerComponents.isNotEmpty()) {
Row(
modifier = Modifier.fillMaxWidth().wrapContentHeight(),
- horizontalArrangement = Arrangement.spacedBy(20.dp)
+ horizontalArrangement = Arrangement.spacedBy(if (isLargeScreen) 28.dp else 20.dp),
) {
for (component in layout.footerComponents) {
- with(component.component as ComposeVolumePanelUiComponent) {
- Content(Modifier.weight(1f))
+ AnimatedVisibility(
+ visible = component.isVisible,
+ modifier = Modifier.weight(1f),
+ ) {
+ with(component.component as ComposeVolumePanelUiComponent) {
+ Content(Modifier)
+ }
}
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelComposeScope.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelComposeScope.kt
index c70c6b1ad861..af6909102cf3 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelComposeScope.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelComposeScope.kt
@@ -16,17 +16,21 @@
package com.android.systemui.volume.panel.ui.composable
+import android.content.res.Configuration
import android.content.res.Configuration.Orientation
import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelState
class VolumePanelComposeScope(private val state: VolumePanelState) {
- /**
- * Layout orientation of the panel. It doesn't necessarily aligns with the device orientation,
- * because in some cases we want to show bigger version of a portrait orientation when the
- * device is in landscape.
- */
+ /** Layout orientation of the panel. This aligns with the device orientation. */
@Orientation
val orientation: Int
get() = state.orientation
+
+ /** Is true when Volume Panel is using large-screen layout and false the otherwise. */
+ val isLargeScreen: Boolean
+ get() = state.isLargeScreen
}
+
+val VolumePanelComposeScope.isPortrait: Boolean
+ get() = orientation == Configuration.ORIENTATION_PORTRAIT
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
index 60d03fc6a107..8a1e6a8b74f4 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
@@ -17,19 +17,17 @@
package com.android.systemui.volume.panel.ui.composable
import android.content.res.Configuration
-import androidx.compose.animation.AnimatedVisibility
-import androidx.compose.animation.core.MutableTransitionState
-import androidx.compose.animation.slideInVertically
-import androidx.compose.animation.slideOutVertically
-import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
@@ -37,61 +35,70 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.alpha
import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.unit.dp
import com.android.compose.theme.PlatformTheme
import com.android.systemui.res.R
import com.android.systemui.volume.panel.ui.layout.ComponentsLayout
import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelState
import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel
+private val padding = 24.dp
+
@Composable
fun VolumePanelRoot(
viewModel: VolumePanelViewModel,
- onDismissAnimationFinished: () -> Unit,
modifier: Modifier = Modifier,
+ onDismiss: () -> Unit
) {
+ LaunchedEffect(viewModel) {
+ viewModel.volumePanelState.collect {
+ if (!it.isVisible) {
+ onDismiss()
+ }
+ }
+ }
+
PlatformTheme(isSystemInDarkTheme()) {
val state: VolumePanelState by viewModel.volumePanelState.collectAsState()
val components by viewModel.componentsLayout.collectAsState(null)
- val transitionState =
- remember { MutableTransitionState(false) }.apply { targetState = state.isVisible }
-
- LaunchedEffect(transitionState.targetState, transitionState.isIdle) {
- if (!transitionState.targetState && transitionState.isIdle) {
- onDismissAnimationFinished()
+ with(VolumePanelComposeScope(state)) {
+ var boxModifier = modifier.fillMaxSize().clickable(onClick = onDismiss)
+ if (!isPortrait) {
+ boxModifier = boxModifier.padding(horizontal = 48.dp)
}
- }
-
- Box(
- modifier = modifier.fillMaxSize(),
- contentAlignment = Alignment.BottomCenter,
- ) {
- Spacer(
- modifier =
- Modifier.fillMaxSize()
- .alpha(0.32f)
- .background(MaterialTheme.colorScheme.scrim)
- .clickable(onClick = { viewModel.dismissPanel() })
- )
- AnimatedVisibility(
- visibleState = transitionState,
- enter = slideInVertically { it },
- exit = slideOutVertically { it },
+ Box(
+ modifier = boxModifier,
+ contentAlignment = Alignment.BottomCenter,
) {
val radius = dimensionResource(R.dimen.volume_panel_corner_radius)
Surface(
+ modifier =
+ Modifier.clickable(
+ interactionSource = null,
+ indication = null,
+ onClick = {
+ // prevent windowCloseOnTouchOutside from dismissing when tapped on
+ // the panel itself.
+ },
+ ),
shape = RoundedCornerShape(topStart = radius, topEnd = radius),
color = MaterialTheme.colorScheme.surfaceContainer,
) {
- Column {
- components?.let { componentsState ->
- with(VolumePanelComposeScope(state)) { Components(componentsState) }
- }
+ components?.let { componentsState ->
+ Components(
+ componentsState,
+ Modifier.padding(
+ start = padding,
+ top = padding,
+ end = padding,
+ bottom = 20.dp,
+ )
+ .navigationBarsPadding()
+ )
}
}
}
@@ -100,27 +107,37 @@ fun VolumePanelRoot(
}
@Composable
-private fun VolumePanelComposeScope.Components(state: ComponentsLayout) {
- if (orientation == Configuration.ORIENTATION_PORTRAIT) {
- VerticalVolumePanelContent(
- state,
- modifier = Modifier.padding(dimensionResource(R.dimen.volume_panel_content_padding)),
- )
- } else {
- TODO("Add landscape layout")
+private fun VolumePanelComposeScope.Components(
+ layout: ComponentsLayout,
+ modifier: Modifier = Modifier
+) {
+ var columnModifier = modifier.widthIn(max = 800.dp)
+ if (!isLargeScreen && orientation != Configuration.ORIENTATION_PORTRAIT) {
+ columnModifier = columnModifier.heightIn(max = 332.dp)
+ }
+ Column(modifier = columnModifier, verticalArrangement = Arrangement.spacedBy(padding)) {
+ if (orientation == Configuration.ORIENTATION_PORTRAIT || isLargeScreen) {
+ VerticalVolumePanelContent(layout)
+ } else {
+ HorizontalVolumePanelContent(layout)
+ }
+ BottomBar(layout = layout, modifier = Modifier)
}
+}
- val horizontalPadding = dimensionResource(R.dimen.volume_panel_bottom_bar_horizontal_padding)
- if (state.bottomBarComponent.isVisible) {
- with(state.bottomBarComponent.component as ComposeVolumePanelUiComponent) {
- Content(
- Modifier.navigationBarsPadding()
- .padding(
- start = horizontalPadding,
- end = horizontalPadding,
- bottom = dimensionResource(R.dimen.volume_panel_bottom_bar_bottom_padding),
- )
- )
+@Composable
+private fun VolumePanelComposeScope.BottomBar(
+ layout: ComponentsLayout,
+ modifier: Modifier = Modifier
+) {
+ if (layout.bottomBarComponent.isVisible) {
+ Box(
+ modifier = modifier.fillMaxWidth(),
+ contentAlignment = Alignment.Center,
+ ) {
+ with(layout.bottomBarComponent.component as ComposeVolumePanelUiComponent) {
+ Content(Modifier)
+ }
}
}
}
diff --git a/packages/SystemUI/compose/scene/Android.bp b/packages/SystemUI/compose/scene/Android.bp
index 3424085049cc..af1172bddfc8 100644
--- a/packages/SystemUI/compose/scene/Android.bp
+++ b/packages/SystemUI/compose/scene/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index 828e34da378d..2e781e69bb4a 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -29,13 +29,17 @@ import androidx.compose.ui.geometry.lerp
import androidx.compose.ui.graphics.CompositingStrategy
import androidx.compose.ui.graphics.drawscope.ContentDrawScope
import androidx.compose.ui.graphics.drawscope.scale
-import androidx.compose.ui.layout.IntermediateMeasureScope
+import androidx.compose.ui.layout.ApproachLayoutModifierNode
+import androidx.compose.ui.layout.ApproachMeasureScope
+import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.layout.LookaheadScope
import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasureResult
import androidx.compose.ui.layout.Placeable
-import androidx.compose.ui.layout.intermediateLayout
import androidx.compose.ui.node.DrawModifierNode
import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.testTag
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.round
@@ -91,23 +95,7 @@ internal fun Modifier.element(
layoutImpl: SceneTransitionLayoutImpl,
scene: Scene,
key: ElementKey,
-): Modifier {
- return this.then(ElementModifier(layoutImpl, scene, key))
- // TODO(b/311132415): Move this into ElementNode once we can create a delegate
- // IntermediateLayoutModifierNode.
- .intermediateLayout { measurable, constraints ->
- // TODO(b/311132415): No need to fetch the element and sceneState from the map anymore
- // once this is merged into ElementNode.
- val element = layoutImpl.elements.getValue(key)
- val sceneState = element.sceneStates.getValue(scene.key)
-
- val placeable = measure(layoutImpl, scene, element, sceneState, measurable, constraints)
- layout(placeable.width, placeable.height) {
- place(layoutImpl, scene, element, sceneState, placeable, placementScope = this)
- }
- }
- .testTag(key.testTag)
-}
+): Modifier = this.then(ElementModifier(layoutImpl, scene, key)).testTag(key.testTag)
/**
* An element associated to [ElementNode]. Note that this element does not support updates as its
@@ -129,7 +117,7 @@ internal class ElementNode(
private var layoutImpl: SceneTransitionLayoutImpl,
private var scene: Scene,
private var key: ElementKey,
-) : Modifier.Node(), DrawModifierNode {
+) : Modifier.Node(), DrawModifierNode, ApproachLayoutModifierNode {
private var _element: Element? = null
private val element: Element
get() = _element!!
@@ -197,6 +185,31 @@ internal class ElementNode(
maybePruneMaps(layoutImpl, prevElement, prevSceneState)
}
+ override fun isMeasurementApproachComplete(lookaheadSize: IntSize): Boolean {
+ // TODO(b/324191441): Investigate whether making this check more complex (checking if this
+ // element is shared or transformed) would lead to better performance.
+ return layoutImpl.state.currentTransition == null
+ }
+
+ override fun Placeable.PlacementScope.isPlacementApproachComplete(
+ lookaheadCoordinates: LayoutCoordinates
+ ): Boolean {
+ // TODO(b/324191441): Investigate whether making this check more complex (checking if this
+ // element is shared or transformed) would lead to better performance.
+ return layoutImpl.state.currentTransition == null
+ }
+
+ @ExperimentalComposeUiApi
+ override fun ApproachMeasureScope.approachMeasure(
+ measurable: Measurable,
+ constraints: Constraints,
+ ): MeasureResult {
+ val placeable = measure(layoutImpl, scene, element, sceneState, measurable, constraints)
+ return layout(placeable.width, placeable.height) {
+ place(layoutImpl, scene, element, sceneState, placeable, placementScope = this)
+ }
+ }
+
override fun ContentDrawScope.draw() {
val drawScale = getDrawScale(layoutImpl, element, scene)
if (drawScale == Scale.Default) {
@@ -368,7 +381,7 @@ private fun elementAlpha(
}
@OptIn(ExperimentalComposeUiApi::class)
-private fun IntermediateMeasureScope.measure(
+private fun ApproachMeasureScope.measure(
layoutImpl: SceneTransitionLayoutImpl,
scene: Scene,
element: Element,
@@ -431,7 +444,7 @@ private fun getDrawScale(
}
@OptIn(ExperimentalComposeUiApi::class)
-private fun IntermediateMeasureScope.place(
+private fun ApproachMeasureScope.place(
layoutImpl: SceneTransitionLayoutImpl,
scene: Scene,
element: Element,
@@ -439,6 +452,8 @@ private fun IntermediateMeasureScope.place(
placeable: Placeable,
placementScope: Placeable.PlacementScope,
) {
+ this as LookaheadScope
+
with(placementScope) {
// Update the offset (relative to the SceneTransitionLayout) this element has in this scene
// when idle.
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
index c8fbad4f4eef..c40856061152 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
@@ -27,7 +27,6 @@ import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.round
@@ -56,14 +55,17 @@ internal class SceneGestureHandler(
if (isDrivingTransition || force) {
layoutState.startTransition(newTransition, newTransition.key)
- // Initialize SwipeTransition.swipeSpec. Note that this must be called right after
- // layoutState.startTransition() is called, because it computes the
- // layoutState.transformationSpec().
+ // Initialize SwipeTransition.transformationSpec and .swipeSpec. Note that this must be
+ // called right after layoutState.startTransition() is called, because it computes the
+ // current layoutState.transformationSpec().
+ val transformationSpec = layoutState.transformationSpec
+ newTransition.transformationSpec = transformationSpec
newTransition.swipeSpec =
- layoutState.transformationSpec.swipeSpec ?: layoutState.transitions.defaultSwipeSpec
+ transformationSpec.swipeSpec ?: layoutState.transitions.defaultSwipeSpec
} else {
- // We were not driving the transition and we don't force the update, so the spec won't
- // be used and it doesn't matter which one we set here.
+ // We were not driving the transition and we don't force the update, so the specs won't
+ // be used and it doesn't matter which ones we set here.
+ newTransition.transformationSpec = TransformationSpec.Empty
newTransition.swipeSpec = SceneTransitions.DefaultSwipeSpec
}
@@ -285,16 +287,21 @@ internal class SceneGestureHandler(
): Pair<Scene, Float> {
val toScene = swipeTransition._toScene
val fromScene = swipeTransition._fromScene
- val absoluteDistance = swipeTransition.distance.absoluteValue
+ val distance = swipeTransition.distance()
- // If the swipe was not committed, don't do anything.
- if (swipeTransition._currentScene != toScene) {
+ // If the swipe was not committed or if the swipe distance is not computed yet, don't do
+ // anything.
+ if (
+ swipeTransition._currentScene != toScene ||
+ distance == SwipeTransition.DistanceUnspecified
+ ) {
return fromScene to 0f
}
// If the offset is past the distance then let's change fromScene so that the user can swipe
// to the next screen or go back to the previous one.
val offset = swipeTransition.dragOffset
+ val absoluteDistance = distance.absoluteValue
return if (offset <= -absoluteDistance && swipes!!.upOrLeftResult?.toScene == toScene.key) {
toScene to absoluteDistance
} else if (
@@ -347,21 +354,44 @@ internal class SceneGestureHandler(
// Compute the destination scene (and therefore offset) to settle in.
val offset = swipeTransition.dragOffset
- val distance = swipeTransition.distance
+ val distance = swipeTransition.distance()
+ var targetScene: Scene
+ var targetOffset: Float
if (
- shouldCommitSwipe(
- offset,
- distance,
- velocity,
- wasCommitted = swipeTransition._currentScene == toScene,
- )
+ distance != SwipeTransition.DistanceUnspecified &&
+ shouldCommitSwipe(
+ offset,
+ distance,
+ velocity,
+ wasCommitted = swipeTransition._currentScene == toScene,
+ )
) {
- // Animate to the next scene
- animateTo(targetScene = toScene, targetOffset = distance)
+ targetScene = toScene
+ targetOffset = distance
} else {
- // Animate to the initial scene
- animateTo(targetScene = fromScene, targetOffset = 0f)
+ targetScene = fromScene
+ targetOffset = 0f
+ }
+
+ if (
+ targetScene != swipeTransition._currentScene &&
+ !layoutState.canChangeScene(targetScene.key)
+ ) {
+ // We wanted to change to a new scene but we are not allowed to, so we animate back
+ // to the current scene.
+ targetScene = swipeTransition._currentScene
+ targetOffset =
+ if (targetScene == fromScene) {
+ 0f
+ } else {
+ check(distance != SwipeTransition.DistanceUnspecified) {
+ "distance is equal to ${SwipeTransition.DistanceUnspecified}"
+ }
+ distance
+ }
}
+
+ animateTo(targetScene = targetScene, targetOffset = targetOffset)
} else {
// We are doing an overscroll animation between scenes. In this case, we can also start
// from the idle position.
@@ -445,22 +475,20 @@ private fun SwipeTransition(
): SwipeTransition {
val upOrLeftResult = swipes.upOrLeftResult
val downOrRightResult = swipes.downOrRightResult
- val userActionDistance = result.distance ?: DefaultSwipeDistance
- val absoluteDistance =
- with(userActionDistance) {
- layoutImpl.density.absoluteDistance(fromScene.targetSize, orientation)
+ val isUpOrLeft =
+ when (result) {
+ upOrLeftResult -> true
+ downOrRightResult -> false
+ else -> error("Unknown result $result ($upOrLeftResult $downOrRightResult)")
}
return SwipeTransition(
key = result.transitionKey,
_fromScene = fromScene,
_toScene = layoutImpl.scene(result.toScene),
- distance =
- when (result) {
- upOrLeftResult -> -absoluteDistance
- downOrRightResult -> absoluteDistance
- else -> error("Unknown result $result ($upOrLeftResult $downOrRightResult)")
- },
+ userActionDistanceScope = layoutImpl.userActionDistanceScope,
+ orientation = orientation,
+ isUpOrLeft = isUpOrLeft,
)
}
@@ -468,11 +496,9 @@ private class SwipeTransition(
val key: TransitionKey?,
val _fromScene: Scene,
val _toScene: Scene,
- /**
- * The signed distance between [fromScene] and [toScene]. It is negative if [fromScene] is above
- * or to the left of [toScene]
- */
- val distance: Float,
+ private val userActionDistanceScope: UserActionDistanceScope,
+ private val orientation: Orientation,
+ private val isUpOrLeft: Boolean,
) : TransitionState.Transition(_fromScene.key, _toScene.key) {
var _currentScene by mutableStateOf(_fromScene)
override val currentScene: SceneKey
@@ -480,7 +506,16 @@ private class SwipeTransition(
override val progress: Float
get() {
+ // Important: If we are going to return early because distance is equal to 0, we should
+ // still make sure we read the offset before returning so that the calling code still
+ // subscribes to the offset value.
val offset = if (isAnimatingOffset) offsetAnimatable.value else dragOffset
+
+ val distance = distance()
+ if (distance == DistanceUnspecified) {
+ return 0f
+ }
+
return offset / distance
}
@@ -504,9 +539,50 @@ private class SwipeTransition(
/** Job to check that there is at most one offset animation in progress. */
private var offsetAnimationJob: Job? = null
+ /**
+ * The [TransformationSpecImpl] associated to this transition.
+ *
+ * Note: This is lateinit because this [SwipeTransition] is needed by
+ * [BaseSceneTransitionLayoutState] to compute the [TransitionSpec], and it will be set right
+ * after [BaseSceneTransitionLayoutState.startTransition] is called with this transition.
+ */
+ lateinit var transformationSpec: TransformationSpecImpl
+
/** The spec to use when animating this transition to either [fromScene] or [toScene]. */
lateinit var swipeSpec: SpringSpec<Float>
+ private var lastDistance = DistanceUnspecified
+
+ /**
+ * The signed distance between [fromScene] and [toScene]. It is negative if [fromScene] is above
+ * or to the left of [toScene].
+ *
+ * Note that this distance can be equal to [DistanceUnspecified] during the first frame of a
+ * transition when the distance depends on the size or position of an element that is composed
+ * in the scene we are going to.
+ */
+ fun distance(): Float {
+ if (lastDistance != DistanceUnspecified) {
+ return lastDistance
+ }
+
+ val absoluteDistance =
+ with(transformationSpec.distance ?: DefaultSwipeDistance) {
+ userActionDistanceScope.absoluteDistance(
+ _fromScene.targetSize,
+ orientation,
+ )
+ }
+
+ if (absoluteDistance <= 0f) {
+ return DistanceUnspecified
+ }
+
+ val distance = if (isUpOrLeft) -absoluteDistance else absoluteDistance
+ lastDistance = distance
+ return distance
+ }
+
/** Ends any previous [offsetAnimationJob] and runs the new [job]. */
private fun startOffsetAnimation(job: () -> Job) {
cancelOffsetAnimation()
@@ -549,6 +625,7 @@ private class SwipeTransition(
}
isAnimatingOffset = true
+ val animationSpec = transformationSpec
offsetAnimatable.animateTo(
targetValue = targetOffset,
animationSpec = swipeSpec,
@@ -557,10 +634,14 @@ private class SwipeTransition(
finishOffsetAnimation()
}
+
+ companion object {
+ const val DistanceUnspecified = 0f
+ }
}
private object DefaultSwipeDistance : UserActionDistance {
- override fun Density.absoluteDistance(
+ override fun UserActionDistanceScope.absoluteDistance(
fromSceneSize: IntSize,
orientation: Orientation,
): Float {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index e1f8a0959f6f..1e3842a1de68 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -25,6 +25,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Density
@@ -394,36 +395,52 @@ class UserActionResult(
/** The scene we should be transitioning to during the [UserAction]. */
val toScene: SceneKey,
- /**
- * The distance the action takes to animate from 0% to 100%.
- *
- * If `null`, a default distance will be used that depends on the [UserAction] performed.
- */
- val distance: UserActionDistance? = null,
-
/** The key of the transition that should be used. */
val transitionKey: TransitionKey? = null,
-) {
- constructor(
- toScene: SceneKey,
- distance: Dp,
- transitionKey: TransitionKey? = null,
- ) : this(toScene, FixedDistance(distance), transitionKey)
-}
+)
interface UserActionDistance {
/**
* Return the **absolute** distance of the user action given the size of the scene we are
* animating from and the [orientation].
+ *
+ * Note: This function will be called for each drag event until it returns a value > 0f. This
+ * for instance allows you to return 0f or a negative value until the first layout pass of a
+ * scene, so that you can use the size and position of elements in the scene we are
+ * transitioning to when computing this absolute distance.
*/
- fun Density.absoluteDistance(fromSceneSize: IntSize, orientation: Orientation): Float
+ fun UserActionDistanceScope.absoluteDistance(
+ fromSceneSize: IntSize,
+ orientation: Orientation
+ ): Float
+}
+
+interface UserActionDistanceScope : Density {
+ /**
+ * Return the *target* size of [this] element in the given [scene], i.e. the size of the element
+ * when idle, or `null` if the element is not composed and measured in that scene (yet).
+ */
+ fun ElementKey.targetSize(scene: SceneKey): IntSize?
+
+ /**
+ * Return the *target* offset of [this] element in the given [scene], i.e. the size of the
+ * element when idle, or `null` if the element is not composed and placed in that scene (yet).
+ */
+ fun ElementKey.targetOffset(scene: SceneKey): Offset?
+
+ /**
+ * Return the *target* size of [this] scene, i.e. the size of the scene when idle, or `null` if
+ * the scene was never composed.
+ */
+ fun SceneKey.targetSize(): IntSize?
}
/** The user action has a fixed [absoluteDistance]. */
-private class FixedDistance(private val distance: Dp) : UserActionDistance {
- override fun Density.absoluteDistance(fromSceneSize: IntSize, orientation: Orientation): Float {
- return distance.toPx()
- }
+class FixedDistance(private val distance: Dp) : UserActionDistance {
+ override fun UserActionDistanceScope.absoluteDistance(
+ fromSceneSize: IntSize,
+ orientation: Orientation,
+ ): Float = distance.toPx()
}
/**
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
index 8c5a4720e7fb..039a5b0c9523 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
@@ -96,9 +96,18 @@ internal class SceneTransitionLayoutImpl(
?: mutableMapOf<ValueKey, MutableMap<ElementKey?, SnapshotStateMap<SceneKey, *>>>()
.also { _sharedValues = it }
+ // TODO(b/317958526): Lazily allocate scene gesture handlers the first time they are needed.
private val horizontalGestureHandler: SceneGestureHandler
private val verticalGestureHandler: SceneGestureHandler
+ private var _userActionDistanceScope: UserActionDistanceScope? = null
+ internal val userActionDistanceScope: UserActionDistanceScope
+ get() =
+ _userActionDistanceScope
+ ?: UserActionDistanceScopeImpl(layoutImpl = this).also {
+ _userActionDistanceScope = it
+ }
+
init {
updateScenes(builder)
@@ -232,7 +241,12 @@ internal class SceneTransitionLayoutImpl(
scene(state.transitionState.currentScene).userActions[Back]?.let { result ->
// TODO(b/290184746): Handle predictive back and use result.distance if
// specified.
- BackHandler { with(state) { coroutineScope.onChangeScene(result.toScene) } }
+ BackHandler {
+ val targetScene = result.toScene
+ if (state.canChangeScene(targetScene)) {
+ with(state) { coroutineScope.onChangeScene(targetScene) }
+ }
+ }
}
Box {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index a8da55101548..662f33f3e88b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -101,13 +101,30 @@ sealed interface MutableSceneTransitionLayoutState : SceneTransitionLayoutState
): TransitionState.Transition?
}
-/** Return a [MutableSceneTransitionLayoutState] initially idle at [initialScene]. */
+/**
+ * Return a [MutableSceneTransitionLayoutState] initially idle at [initialScene].
+ *
+ * @param initialScene the initial scene to which this state is initialized.
+ * @param transitions the [SceneTransitions] used when this state is transitioning between scenes.
+ * @param canChangeScene whether we can transition to the given scene. This is called when the user
+ * commits a transition to a new scene because of a [UserAction]. If [canChangeScene] returns
+ * `true`, then the gesture will be committed and we will animate to the other scene. Otherwise,
+ * the gesture will be cancelled and we will animate back to the current scene.
+ * @param stateLinks the [StateLink] connecting this [SceneTransitionLayoutState] to other
+ * [SceneTransitionLayoutState]s.
+ */
fun MutableSceneTransitionLayoutState(
initialScene: SceneKey,
transitions: SceneTransitions = SceneTransitions.Empty,
+ canChangeScene: (SceneKey) -> Boolean = { true },
stateLinks: List<StateLink> = emptyList(),
): MutableSceneTransitionLayoutState {
- return MutableSceneTransitionLayoutStateImpl(initialScene, transitions, stateLinks)
+ return MutableSceneTransitionLayoutStateImpl(
+ initialScene,
+ transitions,
+ canChangeScene,
+ stateLinks,
+ )
}
/**
@@ -120,18 +137,32 @@ fun MutableSceneTransitionLayoutState(
* This is called when the user commits a transition to a new scene because of a [UserAction], for
* instance by triggering back navigation or by swiping to a new scene.
* @param transitions the definition of the transitions used to animate a change of scene.
+ * @param canChangeScene whether we can transition to the given scene. This is called when the user
+ * commits a transition to a new scene because of a [UserAction]. If [canChangeScene] returns
+ * `true`, then [onChangeScene] will be called right afterwards with the same [SceneKey]. If it
+ * returns `false`, the user action will be cancelled and we will animate back to the current
+ * scene.
+ * @param stateLinks the [StateLink] connecting this [SceneTransitionLayoutState] to other
+ * [SceneTransitionLayoutState]s.
*/
@Composable
fun updateSceneTransitionLayoutState(
currentScene: SceneKey,
onChangeScene: (SceneKey) -> Unit,
transitions: SceneTransitions = SceneTransitions.Empty,
+ canChangeScene: (SceneKey) -> Boolean = { true },
stateLinks: List<StateLink> = emptyList(),
): SceneTransitionLayoutState {
return remember {
- HoistedSceneTransitionLayoutScene(currentScene, transitions, onChangeScene, stateLinks)
+ HoistedSceneTransitionLayoutState(
+ currentScene,
+ transitions,
+ onChangeScene,
+ canChangeScene,
+ stateLinks,
+ )
}
- .apply { update(currentScene, onChangeScene, transitions, stateLinks) }
+ .apply { update(currentScene, onChangeScene, canChangeScene, transitions, stateLinks) }
}
@Stable
@@ -208,6 +239,9 @@ internal abstract class BaseSceneTransitionLayoutState(
private val activeTransitionLinks = mutableMapOf<StateLink, LinkedTransition>()
+ /** Whether we can transition to the given [scene]. */
+ internal abstract fun canChangeScene(scene: SceneKey): Boolean
+
/**
* Called when the [current scene][TransitionState.currentScene] should be changed to [scene].
*
@@ -330,25 +364,30 @@ internal abstract class BaseSceneTransitionLayoutState(
* A [SceneTransitionLayout] whose current scene/source of truth is hoisted (its current value comes
* from outside).
*/
-internal class HoistedSceneTransitionLayoutScene(
+internal class HoistedSceneTransitionLayoutState(
initialScene: SceneKey,
override var transitions: SceneTransitions,
private var changeScene: (SceneKey) -> Unit,
+ private var canChangeScene: (SceneKey) -> Boolean,
stateLinks: List<StateLink> = emptyList(),
) : BaseSceneTransitionLayoutState(initialScene, stateLinks) {
private val targetSceneChannel = Channel<SceneKey>(Channel.CONFLATED)
- override fun CoroutineScope.onChangeScene(scene: SceneKey) = changeScene(scene)
+ override fun canChangeScene(scene: SceneKey): Boolean = canChangeScene.invoke(scene)
+
+ override fun CoroutineScope.onChangeScene(scene: SceneKey) = changeScene.invoke(scene)
@Composable
fun update(
currentScene: SceneKey,
onChangeScene: (SceneKey) -> Unit,
+ canChangeScene: (SceneKey) -> Boolean,
transitions: SceneTransitions,
stateLinks: List<StateLink>,
) {
SideEffect {
this.changeScene = onChangeScene
+ this.canChangeScene = canChangeScene
this.transitions = transitions
this.stateLinks = stateLinks
@@ -361,7 +400,7 @@ internal class HoistedSceneTransitionLayoutScene(
// late.
val newKey = targetSceneChannel.tryReceive().getOrNull() ?: newKey
animateToScene(
- layoutState = this@HoistedSceneTransitionLayoutScene,
+ layoutState = this@HoistedSceneTransitionLayoutState,
target = newKey,
transitionKey = null,
)
@@ -374,6 +413,7 @@ internal class HoistedSceneTransitionLayoutScene(
internal class MutableSceneTransitionLayoutStateImpl(
initialScene: SceneKey,
override var transitions: SceneTransitions,
+ private val canChangeScene: (SceneKey) -> Boolean = { true },
stateLinks: List<StateLink> = emptyList(),
) : MutableSceneTransitionLayoutState, BaseSceneTransitionLayoutState(initialScene, stateLinks) {
override fun setTargetScene(
@@ -388,6 +428,8 @@ internal class MutableSceneTransitionLayoutStateImpl(
)
}
+ override fun canChangeScene(scene: SceneKey): Boolean = canChangeScene.invoke(scene)
+
override fun CoroutineScope.onChangeScene(scene: SceneKey) {
setTargetScene(scene, coroutineScope = this)
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
index b8f9359463de..8ee23b6ffe8e 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitions.kt
@@ -163,6 +163,14 @@ interface TransformationSpec {
*/
val swipeSpec: SpringSpec<Float>?
+ /**
+ * The distance it takes for this transition to animate from 0% to 100% when it is driven by a
+ * [UserAction].
+ *
+ * If `null`, a default distance will be used that depends on the [UserAction] performed.
+ */
+ val distance: UserActionDistance?
+
/** The list of [Transformation] applied to elements during this transition. */
val transformations: List<Transformation>
@@ -171,6 +179,7 @@ interface TransformationSpec {
TransformationSpecImpl(
progressSpec = snap(),
swipeSpec = null,
+ distance = null,
transformations = emptyList(),
)
internal val EmptyProvider = { Empty }
@@ -193,6 +202,7 @@ internal class TransitionSpecImpl(
TransformationSpecImpl(
progressSpec = reverse.progressSpec,
swipeSpec = reverse.swipeSpec,
+ distance = reverse.distance,
transformations = reverse.transformations.map { it.reversed() }
)
}
@@ -209,6 +219,7 @@ internal class TransitionSpecImpl(
internal class TransformationSpecImpl(
override val progressSpec: AnimationSpec<Float>,
override val swipeSpec: SpringSpec<Float>?,
+ override val distance: UserActionDistance?,
override val transformations: List<Transformation>,
) : TransformationSpec {
private val cache = mutableMapOf<ElementKey, MutableMap<SceneKey, ElementTransformations>>()
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
index d93911d2de42..8a09b00a63ae 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
@@ -91,6 +91,14 @@ interface TransitionBuilder : PropertyTransformationBuilder {
var swipeSpec: SpringSpec<Float>?
/**
+ * The distance it takes for this transition to animate from 0% to 100% when it is driven by a
+ * [UserAction].
+ *
+ * If `null`, a default distance will be used that depends on the [UserAction] performed.
+ */
+ var distance: UserActionDistance?
+
+ /**
* Define a progress-based range for the transformations inside [builder].
*
* For instance, the following will fade `Foo` during the first half of the transition then it
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
index 9b16d46bfcc8..78289997885f 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDslImpl.kt
@@ -77,6 +77,7 @@ private class SceneTransitionsBuilderImpl : SceneTransitionsBuilder {
return TransformationSpecImpl(
progressSpec = impl.spec,
swipeSpec = impl.swipeSpec,
+ distance = impl.distance,
transformations = impl.transformations,
)
}
@@ -91,6 +92,7 @@ internal class TransitionBuilderImpl : TransitionBuilder {
val transformations = mutableListOf<Transformation>()
override var spec: AnimationSpec<Float> = spring(stiffness = Spring.StiffnessLow)
override var swipeSpec: SpringSpec<Float>? = null
+ override var distance: UserActionDistance? = null
private var range: TransformationRange? = null
private var reversed = false
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt
new file mode 100644
index 000000000000..228d19f09cff
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 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.compose.animation.scene
+
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.unit.IntSize
+
+internal class UserActionDistanceScopeImpl(
+ private val layoutImpl: SceneTransitionLayoutImpl,
+) : UserActionDistanceScope {
+ override val density: Float
+ get() = layoutImpl.density.density
+
+ override val fontScale: Float
+ get() = layoutImpl.density.fontScale
+
+ override fun ElementKey.targetSize(scene: SceneKey): IntSize? {
+ return layoutImpl.elements[this]?.sceneStates?.get(scene)?.targetSize.takeIf {
+ it != Element.SizeUnspecified
+ }
+ }
+
+ override fun ElementKey.targetOffset(scene: SceneKey): Offset? {
+ return layoutImpl.elements[this]?.sceneStates?.get(scene)?.targetOffset.takeIf {
+ it != Offset.Unspecified
+ }
+ }
+
+ override fun SceneKey.targetSize(): IntSize? {
+ return layoutImpl.scenes[this]?.targetSize.takeIf { it != IntSize.Zero }
+ }
+}
diff --git a/packages/SystemUI/compose/scene/tests/Android.bp b/packages/SystemUI/compose/scene/tests/Android.bp
index 13df35b7e1e8..59cc63aa5eef 100644
--- a/packages/SystemUI/compose/scene/tests/Android.bp
+++ b/packages/SystemUI/compose/scene/tests/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
index c0de87abbfe8..33be1dc83dc8 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
@@ -247,10 +247,9 @@ class ElementTest {
}
@Test
- fun elementIsReusedInSameSceneAndBetweenScenes() {
+ fun elementIsReusedBetweenScenes() {
var currentScene by mutableStateOf(TestScenes.SceneA)
var sceneCState by mutableStateOf(0)
- var sceneDState by mutableStateOf(0)
val key = TestElements.Foo
var nullableLayoutImpl: SceneTransitionLayoutImpl? = null
@@ -268,19 +267,6 @@ class ElementTest {
scene(TestScenes.SceneC) {
when (sceneCState) {
0 -> Row(Modifier.element(key)) {}
- 1 -> Column(Modifier.element(key)) {}
- else -> {
- /* Nothing */
- }
- }
- }
- scene(TestScenes.SceneD) {
- // We should be able to extract the modifier before assigning it to different
- // nodes.
- val childModifier = Modifier.element(key)
- when (sceneDState) {
- 0 -> Row(childModifier) {}
- 1 -> Column(childModifier) {}
else -> {
/* Nothing */
}
@@ -313,35 +299,10 @@ class ElementTest {
assertThat(layoutImpl.elements.getValue(key)).isSameInstanceAs(element)
assertThat(element.sceneStates.keys).containsExactly(TestScenes.SceneC)
- // Scene C, state 1: the same element is reused.
+ // Scene C, state 1: the element is removed from the map.
sceneCState = 1
rule.waitForIdle()
- assertThat(layoutImpl.elements.keys).containsExactly(key)
- assertThat(layoutImpl.elements.getValue(key)).isSameInstanceAs(element)
- assertThat(element.sceneStates.keys).containsExactly(TestScenes.SceneC)
-
- // Scene D, state 0: the same element is reused.
- currentScene = TestScenes.SceneD
- sceneDState = 0
- rule.waitForIdle()
-
- assertThat(layoutImpl.elements.keys).containsExactly(key)
- assertThat(layoutImpl.elements.getValue(key)).isSameInstanceAs(element)
- assertThat(element.sceneStates.keys).containsExactly(TestScenes.SceneD)
-
- // Scene D, state 1: the same element is reused.
- sceneDState = 1
- rule.waitForIdle()
-
- assertThat(layoutImpl.elements.keys).containsExactly(key)
- assertThat(layoutImpl.elements.getValue(key)).isSameInstanceAs(element)
- assertThat(element.sceneStates.keys).containsExactly(TestScenes.SceneD)
-
- // Scene D, state 2: the element is removed from the map.
- sceneDState = 2
- rule.waitForIdle()
-
assertThat(element.sceneStates).isEmpty()
assertThat(layoutImpl.elements).isEmpty()
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
index c91d29880ffb..fe53d5b45420 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
@@ -51,8 +51,13 @@ class SceneGestureHandlerTest {
private class TestGestureScope(
private val testScope: MonotonicClockTestScope,
) {
+ var canChangeScene: (SceneKey) -> Boolean = { true }
private val layoutState =
- MutableSceneTransitionLayoutStateImpl(SceneA, EmptyTestTransitions)
+ MutableSceneTransitionLayoutStateImpl(
+ SceneA,
+ EmptyTestTransitions,
+ canChangeScene = { canChangeScene(it) },
+ )
val mutableUserActionsA = mutableMapOf(Swipe.Up to SceneB, Swipe.Down to SceneC)
val mutableUserActionsB = mutableMapOf(Swipe.Up to SceneC, Swipe.Down to SceneA)
@@ -890,4 +895,41 @@ class SceneGestureHandlerTest {
)
assertThat(transitionState).isNotSameInstanceAs(firstTransition)
}
+
+ @Test
+ fun blockTransition() = runGestureTest {
+ assertIdle(SceneA)
+
+ // Swipe up to scene B.
+ onDragStarted(overSlop = up(0.1f))
+ assertTransition(currentScene = SceneA, fromScene = SceneA, toScene = SceneB)
+
+ // Block the transition when the user release their finger.
+ canChangeScene = { false }
+ onDragStopped(velocity = -velocityThreshold)
+ advanceUntilIdle()
+ assertIdle(SceneA)
+ }
+
+ @Test
+ fun blockInterceptedTransition() = runGestureTest {
+ assertIdle(SceneA)
+
+ // Swipe up to B.
+ onDragStarted(overSlop = up(0.1f))
+ assertTransition(currentScene = SceneA, fromScene = SceneA, toScene = SceneB)
+ onDragStopped(velocity = -velocityThreshold)
+ assertTransition(currentScene = SceneB, fromScene = SceneA, toScene = SceneB)
+
+ // Intercept the transition and swipe down back to scene A.
+ assertThat(sceneGestureHandler.shouldImmediatelyIntercept(startedPosition = null)).isTrue()
+ onDragStartedImmediately()
+
+ // Block the transition when the user release their finger.
+ canChangeScene = { false }
+ onDragStopped(velocity = velocityThreshold)
+
+ advanceUntilIdle()
+ assertIdle(SceneB)
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
index 543ed0482430..99372a5d084b 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
@@ -16,9 +16,11 @@
package com.android.compose.animation.scene
+import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@@ -33,6 +35,7 @@ import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performTouchInput
import androidx.compose.ui.test.swipeWithVelocity
import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
@@ -61,8 +64,10 @@ class SwipeToSceneTest {
@get:Rule val rule = createComposeRule()
- private fun layoutState(initialScene: SceneKey = TestScenes.SceneA) =
- MutableSceneTransitionLayoutState(initialScene, EmptyTestTransitions)
+ private fun layoutState(
+ initialScene: SceneKey = TestScenes.SceneA,
+ transitions: SceneTransitions = EmptyTestTransitions,
+ ) = MutableSceneTransitionLayoutState(initialScene, transitions)
/** The content under test. */
@Composable
@@ -370,8 +375,16 @@ class SwipeToSceneTest {
// detected as a drag event.
var touchSlop = 0f
- val layoutState = layoutState()
val verticalSwipeDistance = 50.dp
+ val layoutState =
+ layoutState(
+ transitions =
+ transitions {
+ from(TestScenes.SceneA, to = TestScenes.SceneB) {
+ distance = FixedDistance(verticalSwipeDistance)
+ }
+ }
+ )
assertThat(verticalSwipeDistance).isNotEqualTo(LayoutHeight)
rule.setContent {
@@ -383,14 +396,7 @@ class SwipeToSceneTest {
) {
scene(
TestScenes.SceneA,
- userActions =
- mapOf(
- Swipe.Down to
- UserActionResult(
- toScene = TestScenes.SceneB,
- distance = verticalSwipeDistance,
- )
- ),
+ userActions = mapOf(Swipe.Down to TestScenes.SceneB),
) {
Spacer(Modifier.fillMaxSize())
}
@@ -548,4 +554,64 @@ class SwipeToSceneTest {
assertThat(state.isTransitioning(from = TestScenes.SceneA, to = TestScenes.SceneB)).isTrue()
assertThat(state.transformationSpec.transformations).hasSize(2)
}
+
+ @Test
+ fun dynamicSwipeDistance() {
+ val swipeDistance =
+ object : UserActionDistance {
+ override fun UserActionDistanceScope.absoluteDistance(
+ fromSceneSize: IntSize,
+ orientation: Orientation,
+ ): Float {
+ // Foo is going to have a vertical offset of 50dp. Let's make the swipe distance
+ // the difference between the bottom of the scene and the bottom of the element,
+ // so that we use the offset and size of the element as well as the size of the
+ // scene.
+ val fooSize = TestElements.Foo.targetSize(TestScenes.SceneB) ?: return 0f
+ val fooOffset = TestElements.Foo.targetOffset(TestScenes.SceneB) ?: return 0f
+ val sceneSize = TestScenes.SceneB.targetSize() ?: return 0f
+ return sceneSize.height - fooOffset.y - fooSize.height
+ }
+ }
+
+ val state =
+ MutableSceneTransitionLayoutState(
+ TestScenes.SceneA,
+ transitions {
+ from(TestScenes.SceneA, to = TestScenes.SceneB) { distance = swipeDistance }
+ }
+ )
+
+ val layoutSize = 200.dp
+ val fooYOffset = 50.dp
+ val fooSize = 25.dp
+
+ var touchSlop = 0f
+ rule.setContent {
+ touchSlop = LocalViewConfiguration.current.touchSlop
+
+ SceneTransitionLayout(state, Modifier.size(layoutSize)) {
+ scene(TestScenes.SceneA, userActions = mapOf(Swipe.Up to TestScenes.SceneB)) {
+ Box(Modifier.fillMaxSize())
+ }
+ scene(TestScenes.SceneB) {
+ Box(Modifier.fillMaxSize()) {
+ Box(Modifier.offset(y = fooYOffset).element(TestElements.Foo).size(fooSize))
+ }
+ }
+ }
+ }
+
+ // Swipe up by half the expected distance to get to 50% progress.
+ val expectedDistance = layoutSize - fooYOffset - fooSize
+ rule.onRoot().performTouchInput {
+ val middle = (layoutSize / 2).toPx()
+ down(Offset(middle, middle))
+ moveBy(Offset(0f, -touchSlop - (expectedDistance / 2f).toPx()), delayMillis = 1_000)
+ }
+
+ rule.waitForIdle()
+ assertThat(state.isTransitioning(from = TestScenes.SceneA, to = TestScenes.SceneB)).isTrue()
+ assertThat(state.currentTransition!!.progress).isWithin(0.01f).of(0.5f)
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/utils/Android.bp b/packages/SystemUI/compose/scene/tests/utils/Android.bp
index ff8fe7597e95..9089e6a4b4b6 100644
--- a/packages/SystemUI/compose/scene/tests/utils/Android.bp
+++ b/packages/SystemUI/compose/scene/tests/utils/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index 3dfe65a4f736..51c008ab686a 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -36,7 +36,7 @@ import com.android.systemui.plugins.clocks.ClockMetadata
import com.android.systemui.plugins.clocks.ClockProvider
import com.android.systemui.plugins.clocks.ClockProviderPlugin
import com.android.systemui.plugins.clocks.ClockSettings
-import com.android.systemui.util.Assert
+import com.android.systemui.util.ThreadAssert
import java.io.PrintWriter
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicBoolean
@@ -89,6 +89,7 @@ open class ClockRegistry(
val keepAllLoaded: Boolean,
subTag: String,
var isTransitClockEnabled: Boolean = false,
+ val assert: ThreadAssert = ThreadAssert(),
) {
private val TAG = "${ClockRegistry::class.simpleName} ($subTag)"
private val logger: Logger =
@@ -286,7 +287,7 @@ open class ClockRegistry(
@OpenForTesting
open fun querySettings() {
- assertNotMainThread()
+ assert.isNotMainThread()
val result =
try {
val json =
@@ -313,7 +314,7 @@ open class ClockRegistry(
@OpenForTesting
open fun applySettings(value: ClockSettings?) {
- assertNotMainThread()
+ assert.isNotMainThread()
try {
value?.metadata?.put(KEY_TIMESTAMP, System.currentTimeMillis())
@@ -339,16 +340,6 @@ open class ClockRegistry(
settings = value
}
- @OpenForTesting
- protected open fun assertMainThread() {
- Assert.isMainThread()
- }
-
- @OpenForTesting
- protected open fun assertNotMainThread() {
- Assert.isNotMainThread()
- }
-
private var isClockChanged = AtomicBoolean(false)
private fun triggerOnCurrentClockChanged() {
val shouldSchedule = isClockChanged.compareAndSet(false, true)
@@ -357,7 +348,7 @@ open class ClockRegistry(
}
scope.launch(mainDispatcher) {
- assertMainThread()
+ assert.isMainThread()
isClockChanged.set(false)
clockChangeListeners.forEach { it.onCurrentClockChanged() }
}
@@ -371,7 +362,7 @@ open class ClockRegistry(
}
scope.launch(mainDispatcher) {
- assertMainThread()
+ assert.isMainThread()
isClockListChanged.set(false)
clockChangeListeners.forEach { it.onAvailableClocksChanged() }
}
@@ -585,7 +576,7 @@ open class ClockRegistry(
* Calling from main thread to make sure the access is thread safe.
*/
fun registerClockChangeListener(listener: ClockChangeListener) {
- assertMainThread()
+ assert.isMainThread()
clockChangeListeners.add(listener)
}
@@ -595,7 +586,7 @@ open class ClockRegistry(
* Calling from main thread to make sure the access is thread safe.
*/
fun unregisterClockChangeListener(listener: ClockChangeListener) {
- assertMainThread()
+ assert.isMainThread()
clockChangeListeners.remove(listener)
}
diff --git a/packages/SystemUI/customization/tests/utils/Android.bp b/packages/SystemUI/customization/tests/utils/Android.bp
index 6db141030cce..e015455a35bd 100644
--- a/packages/SystemUI/customization/tests/utils/Android.bp
+++ b/packages/SystemUI/customization/tests/utils/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
index c6327ffa25f1..c3857889a134 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
@@ -17,6 +17,7 @@
package com.android.keyguard
import android.testing.TestableLooper
+import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.ImageView
@@ -51,10 +52,14 @@ import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper
+// collectFlow in KeyguardPinBasedInputViewController.onViewAttached calls JavaAdapter.CollectFlow,
+// which calls View.onRepeatWhenAttached, which requires being run on main thread.
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
class KeyguardPasswordViewControllerTest : SysuiTestCase() {
@Mock private lateinit var keyguardPasswordView: KeyguardPasswordView
@Mock private lateinit var passwordEntry: EditText
+ private var passwordEntryLayoutParams =
+ ViewGroup.LayoutParams(/* width = */ 0, /* height = */ 0)
@Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock lateinit var securityMode: KeyguardSecurityModel.SecurityMode
@Mock lateinit var lockPatternUtils: LockPatternUtils
@@ -92,7 +97,7 @@ class KeyguardPasswordViewControllerTest : SysuiTestCase() {
whenever(keyguardPasswordView.findViewById<ImageView>(R.id.switch_ime_button))
.thenReturn(mock(ImageView::class.java))
`when`(keyguardPasswordView.resources).thenReturn(context.resources)
-
+ whenever(passwordEntry.layoutParams).thenReturn(passwordEntryLayoutParams)
val keyguardKeyboardInteractor = KeyguardKeyboardInteractor(FakeKeyboardRepository())
val fakeFeatureFlags = FakeFeatureFlags()
fakeFeatureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
index f86342c0c20e..0054d137bd2c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
@@ -24,6 +24,7 @@ import static org.mockito.Mockito.when;
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
+import android.view.ViewGroup;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -48,13 +49,17 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidJUnit4.class)
-@RunWithLooper
+// collectFlow in KeyguardPinBasedInputViewController.onViewAttached calls JavaAdapter.CollectFlow,
+// which calls View.onRepeatWhenAttached, which requires being run on main thread.
+@RunWithLooper(setAsMainLooper = true)
public class KeyguardPinBasedInputViewControllerTest extends SysuiTestCase {
@Mock
private KeyguardPinBasedInputView mPinBasedInputView;
@Mock
private PasswordTextView mPasswordEntry;
+ private final ViewGroup.LayoutParams mPasswordEntryLayoutParams =
+ new ViewGroup.LayoutParams(/* width= */ 0, /* height= */ 0);
@Mock
private BouncerKeyguardMessageArea mKeyguardMessageArea;
@Mock
@@ -103,6 +108,7 @@ public class KeyguardPinBasedInputViewControllerTest extends SysuiTestCase {
.thenReturn(mOkButton);
when(mPinBasedInputView.getResources()).thenReturn(getContext().getResources());
+ when(mPasswordEntry.getLayoutParams()).thenReturn(mPasswordEntryLayoutParams);
KeyguardKeyboardInteractor keyguardKeyboardInteractor =
new KeyguardKeyboardInteractor(new FakeKeyboardRepository());
FakeFeatureFlags featureFlags = new FakeFeatureFlags();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
index 059620502a49..b31f6f5b096b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
@@ -220,6 +220,7 @@ class FaceHelpMessageDeferralTest : SysuiTestCase() {
threshold,
logger,
dumpManager,
+ "0",
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index 259f3498d4c3..503463171ae7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -44,12 +44,18 @@ import com.android.systemui.biometrics.ui.viewmodel.DefaultUdfpsTouchOverlayView
import com.android.systemui.biometrics.ui.viewmodel.DeviceEntryUdfpsTouchOverlayViewModel
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.power.data.repository.FakePowerRepository
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.res.R
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.LockscreenShadeTransitionController
+import com.android.systemui.statusbar.phone.ScreenOffAnimationController
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.phone.SystemUIDialogManager
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController
@@ -58,6 +64,10 @@ import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
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
@@ -68,6 +78,7 @@ import org.mockito.ArgumentMatchers.eq
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
@@ -120,6 +131,9 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
@Mock private lateinit var shadeInteractor: ShadeInteractor
@Captor private lateinit var layoutParamsCaptor: ArgumentCaptor<WindowManager.LayoutParams>
@Mock private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor
+ private lateinit var powerRepository: FakePowerRepository
+ private lateinit var powerInteractor: PowerInteractor
+ private lateinit var testScope: TestScope
private val onTouch = { _: View, _: MotionEvent, _: Boolean -> true }
private var overlayParams: UdfpsOverlayParams = UdfpsOverlayParams()
@@ -127,6 +141,15 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
@Before
fun setup() {
+ testScope = TestScope(StandardTestDispatcher())
+ powerRepository = FakePowerRepository()
+ powerInteractor =
+ PowerInteractor(
+ powerRepository,
+ mock(FalsingCollector::class.java),
+ mock(ScreenOffAnimationController::class.java),
+ statusBarStateController,
+ )
whenever(inflater.inflate(R.layout.udfps_view, null, false)).thenReturn(udfpsView)
whenever(inflater.inflate(R.layout.udfps_bp_view, null))
.thenReturn(mock(UdfpsBpView::class.java))
@@ -178,6 +201,8 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
{ defaultUdfpsTouchOverlayViewModel },
shadeInteractor,
udfpsOverlayInteractor,
+ powerInteractor,
+ testScope,
)
block()
}
@@ -277,6 +302,66 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
}
}
+ @Test
+ fun showUdfpsOverlay_awake() =
+ testScope.runTest {
+ withReason(REASON_AUTH_KEYGUARD) {
+ mSetFlagsRule.enableFlags(Flags.FLAG_UDFPS_VIEW_PERFORMANCE)
+ powerRepository.updateWakefulness(
+ rawState = WakefulnessState.AWAKE,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.OTHER,
+ )
+ controllerOverlay.show(udfpsController, overlayParams)
+ runCurrent()
+ verify(windowManager).addView(any(), any())
+ }
+ }
+
+ @Test
+ fun showUdfpsOverlay_whileGoingToSleep() =
+ testScope.runTest {
+ withReason(REASON_AUTH_KEYGUARD) {
+ mSetFlagsRule.enableFlags(Flags.FLAG_UDFPS_VIEW_PERFORMANCE)
+ powerRepository.updateWakefulness(
+ rawState = WakefulnessState.STARTING_TO_SLEEP,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.OTHER,
+ )
+ controllerOverlay.show(udfpsController, overlayParams)
+ runCurrent()
+ verify(windowManager, never()).addView(any(), any())
+
+ // we hide to end the job that listens for the finishedGoingToSleep signal
+ controllerOverlay.hide()
+ }
+ }
+
+ @Test
+ fun showUdfpsOverlay_afterFinishedGoingToSleep() =
+ testScope.runTest {
+ withReason(REASON_AUTH_KEYGUARD) {
+ mSetFlagsRule.enableFlags(Flags.FLAG_UDFPS_VIEW_PERFORMANCE)
+ powerRepository.updateWakefulness(
+ rawState = WakefulnessState.STARTING_TO_SLEEP,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.OTHER,
+ )
+ controllerOverlay.show(udfpsController, overlayParams)
+ runCurrent()
+ verify(windowManager, never()).addView(any(), any())
+
+ powerRepository.updateWakefulness(
+ rawState = WakefulnessState.ASLEEP,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.OTHER,
+ )
+ runCurrent()
+ verify(windowManager)
+ .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture())
+ }
+ }
+
private fun showUdfpsOverlay() {
val didShow = controllerOverlay.show(udfpsController, overlayParams)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 529403a710b5..561cdbbf66ce 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -86,6 +86,7 @@ import com.android.systemui.biometrics.ui.viewmodel.DefaultUdfpsTouchOverlayView
import com.android.systemui.biometrics.ui.viewmodel.DeviceEntryUdfpsTouchOverlayViewModel;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
+import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
@@ -94,10 +95,15 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.power.data.repository.FakePowerRepository;
+import com.android.systemui.power.domain.interactor.PowerInteractor;
+import com.android.systemui.power.shared.model.WakeSleepReason;
+import com.android.systemui.power.shared.model.WakefulnessState;
import com.android.systemui.res.R;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
@@ -125,6 +131,8 @@ import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
import java.util.List;
+import kotlinx.coroutines.CoroutineScope;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
@RunWithLooper(setAsMainLooper = true)
@@ -238,6 +246,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
private ScreenLifecycle.Observer mScreenObserver;
private FingerprintSensorPropertiesInternal mOpticalProps;
private FingerprintSensorPropertiesInternal mUltrasonicProps;
+ private PowerInteractor mPowerInteractor;
+ private FakePowerRepository mPowerRepository;
@Mock
private InputManager mInputManager;
@Mock
@@ -253,6 +263,19 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Before
public void setUp() {
+ mPowerRepository = new FakePowerRepository();
+ mPowerInteractor = new PowerInteractor(
+ mPowerRepository,
+ mock(FalsingCollector.class),
+ mock(ScreenOffAnimationController.class),
+ mStatusBarStateController
+ );
+ mPowerRepository.updateWakefulness(
+ WakefulnessState.AWAKE,
+ WakeSleepReason.POWER_BUTTON,
+ WakeSleepReason.OTHER,
+ /* powerButtonLaunchGestureTriggered */ false
+ );
mContext.getOrCreateTestableResources()
.addOverride(com.android.internal.R.bool.config_ignoreUdfpsVote, false);
@@ -346,7 +369,9 @@ public class UdfpsControllerTest extends SysuiTestCase {
mKeyguardTransitionInteractor,
mDeviceEntryUdfpsTouchOverlayViewModel,
mDefaultUdfpsTouchOverlayViewModel,
- mUdfpsOverlayInteractor
+ mUdfpsOverlayInteractor,
+ mPowerInteractor,
+ mock(CoroutineScope.class)
);
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
index 55cfcc2c6d5b..ab551256cfc3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
@@ -210,6 +210,32 @@ class UdfpsKeyguardViewLegacyControllerWithCoroutinesTest :
}
@Test
+ fun shouldHandleTouchesOnDetach() =
+ testScope.runTest {
+ val shouldHandleTouches by collectLastValue(mUdfpsOverlayInteractor.shouldHandleTouches)
+
+ // GIVEN view is attached + on the keyguard
+ mController.onViewAttached()
+ captureStatusBarStateListeners()
+ sendStatusBarStateChanged(StatusBarState.KEYGUARD)
+ whenever(mView.setPauseAuth(true)).thenReturn(true)
+ whenever(mView.unpausedAlpha).thenReturn(0)
+
+ // WHEN panelViewExpansion changes to expanded
+ val job = mController.listenForBouncerExpansion(this)
+ keyguardBouncerRepository.setPrimaryShow(true)
+ keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE)
+ runCurrent()
+
+ mController.onViewDetached()
+
+ // THEN UDFPS auth is paused and should not handle touches
+ assertThat(mController.shouldPauseAuth()).isTrue()
+ assertThat(shouldHandleTouches!!).isFalse()
+
+ job.cancel()
+ }
+ @Test
fun fadeFromDialogSuggestedAlpha() =
testScope.runTest {
// GIVEN view is attached and status bar expansion is 1f
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
index a8fe16b12e1b..92396e0bcdef 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
@@ -20,6 +20,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.setCommunalAvailable
import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.dock.DockManager
@@ -33,6 +34,7 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent
@@ -50,7 +52,7 @@ class CommunalSceneStartableTest : SysuiTestCase() {
private lateinit var underTest: CommunalSceneStartable
@Before
- fun setUp() =
+ fun setUp() {
with(kosmos) {
underTest =
CommunalSceneStartable(
@@ -61,7 +63,15 @@ class CommunalSceneStartableTest : SysuiTestCase() {
bgScope = applicationCoroutineScope,
)
.apply { start() }
+
+ // Make communal available so that communalInteractor.desiredScene accurately reflects
+ // scene changes instead of just returning Blank.
+ with(kosmos.testScope) {
+ launch { setCommunalAvailable(true) }
+ testScheduler.runCurrent()
+ }
}
+ }
@Test
fun keyguardGoesAway_forceBlankScene() =
@@ -83,25 +93,6 @@ class CommunalSceneStartableTest : SysuiTestCase() {
}
@Test
- fun deviceDreaming_forceBlankScene() =
- with(kosmos) {
- testScope.runTest {
- val scene by collectLastValue(communalInteractor.desiredScene)
-
- communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
- assertThat(scene).isEqualTo(CommunalSceneKey.Communal)
-
- fakeKeyguardTransitionRepository.sendTransitionSteps(
- from = KeyguardState.GLANCEABLE_HUB,
- to = KeyguardState.DREAMING,
- testScope = this
- )
-
- assertThat(scene).isEqualTo(CommunalSceneKey.Blank)
- }
- }
-
- @Test
fun deviceDocked_forceCommunalScene() =
with(kosmos) {
testScope.runTest {
@@ -115,13 +106,6 @@ class CommunalSceneStartableTest : SysuiTestCase() {
testScope = this
)
assertThat(scene).isEqualTo(CommunalSceneKey.Communal)
-
- fakeKeyguardTransitionRepository.sendTransitionSteps(
- from = KeyguardState.GLANCEABLE_HUB,
- to = KeyguardState.DREAMING,
- testScope = this
- )
- assertThat(scene).isEqualTo(CommunalSceneKey.Blank)
}
}
@@ -249,4 +233,10 @@ class CommunalSceneStartableTest : SysuiTestCase() {
fakeDockManager.setDockEvent(DockManager.STATE_DOCKED)
runCurrent()
}
+
+ private suspend fun TestScope.enableCommunal() =
+ with(kosmos) {
+ setCommunalAvailable(true)
+ runCurrent()
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
index 45f98be2ca12..1cdc2b69034b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
@@ -21,8 +21,8 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.log.table.TableLogBuffer
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.util.mockito.KotlinArgumentCaptor
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
index 5b20ae5131b2..06b3806cb382 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
@@ -25,7 +25,6 @@ import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.data.repository.sceneContainerRepository
import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
-import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.flowOf
@@ -57,46 +56,6 @@ class CommunalRepositoryImplTest : SysuiTestCase() {
}
@Test
- fun isCommunalShowing_sceneContainerDisabled_onCommunalScene_true() =
- testScope.runTest {
- underTest.setDesiredScene(CommunalSceneKey.Communal)
-
- val isCommunalHubShowing by collectLastValue(underTest.isCommunalHubShowing)
- assertThat(isCommunalHubShowing).isTrue()
- }
-
- @Test
- fun isCommunalShowing_sceneContainerDisabled_onBlankScene_false() =
- testScope.runTest {
- underTest.setDesiredScene(CommunalSceneKey.Blank)
-
- val isCommunalHubShowing by collectLastValue(underTest.isCommunalHubShowing)
- assertThat(isCommunalHubShowing).isFalse()
- }
-
- @Test
- fun isCommunalShowing_sceneContainerEnabled_onCommunalScene_true() =
- testScope.runTest {
- underTest = createRepositoryImpl(true)
-
- sceneContainerRepository.changeScene(SceneKey.Communal)
-
- val isCommunalHubShowing by collectLastValue(underTest.isCommunalHubShowing)
- assertThat(isCommunalHubShowing).isTrue()
- }
-
- @Test
- fun isCommunalShowing_sceneContainerEnabled_onLockscreenScene_false() =
- testScope.runTest {
- underTest = createRepositoryImpl(true)
-
- sceneContainerRepository.changeScene(SceneKey.Lockscreen)
-
- val isCommunalHubShowing by collectLastValue(underTest.isCommunalHubShowing)
- assertThat(isCommunalHubShowing).isFalse()
- }
-
- @Test
fun transitionState_idleByDefault() =
testScope.runTest {
val transitionState by collectLastValue(underTest.transitionState)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
index 0aca16d9aeaa..6b2831991416 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
@@ -20,6 +20,7 @@ import android.app.admin.DevicePolicyManager
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL
import android.app.admin.devicePolicyManager
+import android.appwidget.AppWidgetProviderInfo
import android.content.Intent
import android.content.pm.UserInfo
import android.platform.test.annotations.DisableFlags
@@ -136,6 +137,29 @@ class CommunalSettingsRepositoryImplTest : SysuiTestCase() {
)
}
+ @EnableFlags(FLAG_COMMUNAL_HUB)
+ @Test
+ fun hubShowsWidgetCategoriesSetByUser() =
+ testScope.runTest {
+ kosmos.fakeSettings.putIntForUser(
+ CommunalSettingsRepositoryImpl.GLANCEABLE_HUB_CONTENT_SETTING,
+ AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN,
+ PRIMARY_USER.id
+ )
+ val setting by collectLastValue(underTest.getWidgetCategories(PRIMARY_USER))
+ assertThat(setting?.categories)
+ .isEqualTo(AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN)
+ }
+
+ @EnableFlags(FLAG_COMMUNAL_HUB)
+ @Test
+ fun hubShowsKeyguardWidgetsByDefault() =
+ testScope.runTest {
+ val setting by collectLastValue(underTest.getWidgetCategories(PRIMARY_USER))
+ assertThat(setting?.categories)
+ .isEqualTo(AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD)
+ }
+
private fun setKeyguardFeaturesDisabled(user: UserInfo, disabledFlags: Int) {
whenever(kosmos.devicePolicyManager.getKeyguardDisabledFeatures(nullable(), eq(user.id)))
.thenReturn(disabledFlags)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
index a54a00d539b8..a4c7abdbfc19 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
@@ -21,15 +21,16 @@ import android.appwidget.AppWidgetProviderInfo
import android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_CONFIGURATION_OPTIONAL
import android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE
import android.content.ComponentName
+import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.db.CommunalItemRank
import com.android.systemui.communal.data.db.CommunalWidgetDao
import com.android.systemui.communal.data.db.CommunalWidgetItem
-import com.android.systemui.communal.shared.CommunalWidgetHost
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.communal.widgets.CommunalAppWidgetHost
+import com.android.systemui.communal.widgets.CommunalWidgetHost
import com.android.systemui.communal.widgets.widgetConfiguratorFail
import com.android.systemui.communal.widgets.widgetConfiguratorSuccess
import com.android.systemui.coroutines.collectLastValue
@@ -136,14 +137,20 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
val provider = ComponentName("pkg_name", "cls_name")
val id = 1
val priority = 1
+ val user = UserHandle(0)
whenever(communalWidgetHost.getAppWidgetInfo(id))
.thenReturn(PROVIDER_INFO_REQUIRES_CONFIGURATION)
- whenever(communalWidgetHost.allocateIdAndBindWidget(any<ComponentName>()))
+ whenever(
+ communalWidgetHost.allocateIdAndBindWidget(
+ any<ComponentName>(),
+ any<UserHandle>()
+ )
+ )
.thenReturn(id)
- underTest.addWidget(provider, priority, kosmos.widgetConfiguratorSuccess)
+ underTest.addWidget(provider, user, priority, kosmos.widgetConfiguratorSuccess)
runCurrent()
- verify(communalWidgetHost).allocateIdAndBindWidget(provider)
+ verify(communalWidgetHost).allocateIdAndBindWidget(provider, user)
verify(communalWidgetDao).addWidget(id, provider, priority)
}
@@ -153,13 +160,20 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
val provider = ComponentName("pkg_name", "cls_name")
val id = 1
val priority = 1
+ val user = UserHandle(0)
whenever(communalWidgetHost.getAppWidgetInfo(id))
.thenReturn(PROVIDER_INFO_REQUIRES_CONFIGURATION)
- whenever(communalWidgetHost.allocateIdAndBindWidget(provider)).thenReturn(id)
- underTest.addWidget(provider, priority, kosmos.widgetConfiguratorFail)
+ whenever(
+ communalWidgetHost.allocateIdAndBindWidget(
+ any<ComponentName>(),
+ any<UserHandle>()
+ )
+ )
+ .thenReturn(id)
+ underTest.addWidget(provider, user, priority, kosmos.widgetConfiguratorFail)
runCurrent()
- verify(communalWidgetHost).allocateIdAndBindWidget(provider)
+ verify(communalWidgetHost).allocateIdAndBindWidget(provider, user)
verify(communalWidgetDao, never()).addWidget(id, provider, priority)
verify(appWidgetHost).deleteAppWidgetId(id)
}
@@ -170,13 +184,22 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
val provider = ComponentName("pkg_name", "cls_name")
val id = 1
val priority = 1
+ val user = UserHandle(0)
whenever(communalWidgetHost.getAppWidgetInfo(id))
.thenReturn(PROVIDER_INFO_REQUIRES_CONFIGURATION)
- whenever(communalWidgetHost.allocateIdAndBindWidget(provider)).thenReturn(id)
- underTest.addWidget(provider, priority) { throw IllegalStateException("some error") }
+ whenever(
+ communalWidgetHost.allocateIdAndBindWidget(
+ any<ComponentName>(),
+ any<UserHandle>()
+ )
+ )
+ .thenReturn(id)
+ underTest.addWidget(provider, user, priority) {
+ throw IllegalStateException("some error")
+ }
runCurrent()
- verify(communalWidgetHost).allocateIdAndBindWidget(provider)
+ verify(communalWidgetHost).allocateIdAndBindWidget(provider, user)
verify(communalWidgetDao, never()).addWidget(id, provider, priority)
verify(appWidgetHost).deleteAppWidgetId(id)
}
@@ -187,14 +210,20 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
val provider = ComponentName("pkg_name", "cls_name")
val id = 1
val priority = 1
+ val user = UserHandle(0)
whenever(communalWidgetHost.getAppWidgetInfo(id))
.thenReturn(PROVIDER_INFO_CONFIGURATION_OPTIONAL)
- whenever(communalWidgetHost.allocateIdAndBindWidget(any<ComponentName>()))
+ whenever(
+ communalWidgetHost.allocateIdAndBindWidget(
+ any<ComponentName>(),
+ any<UserHandle>()
+ )
+ )
.thenReturn(id)
- underTest.addWidget(provider, priority, kosmos.widgetConfiguratorFail)
+ underTest.addWidget(provider, user, priority, kosmos.widgetConfiguratorFail)
runCurrent()
- verify(communalWidgetHost).allocateIdAndBindWidget(provider)
+ verify(communalWidgetHost).allocateIdAndBindWidget(provider, user)
verify(communalWidgetDao).addWidget(id, provider, priority)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt
index 824733bcc47b..5a7cbf6e02ca 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt
@@ -67,7 +67,7 @@ class CommunalInteractorCommunalDisabledTest : SysuiTestCase() {
@Test
fun isCommunalEnabled_false() =
- testScope.runTest { assertThat(underTest.isCommunalEnabled).isFalse() }
+ testScope.runTest { assertThat(underTest.isCommunalEnabled.value).isFalse() }
@Test
fun isCommunalAvailable_whenStorageUnlock_false() =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index 3ac19e4672c3..cd296524c17c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -18,7 +18,9 @@
package com.android.systemui.communal.domain.interactor
import android.app.smartspace.SmartspaceTarget
+import android.appwidget.AppWidgetProviderInfo
import android.content.pm.UserInfo
+import android.os.UserHandle
import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED
import android.widget.RemoteViews
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -47,6 +49,12 @@ import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
+import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
import com.android.systemui.smartspace.data.repository.fakeSmartspaceRepository
import com.android.systemui.testKosmos
@@ -91,6 +99,8 @@ class CommunalInteractorTest : SysuiTestCase() {
private lateinit var keyguardRepository: FakeKeyguardRepository
private lateinit var communalPrefsRepository: FakeCommunalPrefsRepository
private lateinit var editWidgetsActivityStarter: EditWidgetsActivityStarter
+ private lateinit var sceneInteractor: SceneInteractor
+ private lateinit var userTracker: FakeUserTracker
private lateinit var underTest: CommunalInteractor
@@ -107,6 +117,8 @@ class CommunalInteractorTest : SysuiTestCase() {
keyguardRepository = kosmos.fakeKeyguardRepository
editWidgetsActivityStarter = kosmos.editWidgetsActivityStarter
communalPrefsRepository = kosmos.fakeCommunalPrefsRepository
+ sceneInteractor = kosmos.sceneInteractor
+ userTracker = kosmos.fakeUserTracker
whenever(mainUser.isMain).thenReturn(true)
whenever(secondaryUser.isMain).thenReturn(false)
@@ -123,7 +135,7 @@ class CommunalInteractorTest : SysuiTestCase() {
testScope.runTest {
userRepository.setSelectedUserInfo(mainUser)
runCurrent()
- assertThat(underTest.isCommunalEnabled).isTrue()
+ assertThat(underTest.isCommunalEnabled.value).isTrue()
}
@Test
@@ -201,25 +213,19 @@ class CommunalInteractorTest : SysuiTestCase() {
keyguardRepository.setKeyguardOccluded(false)
tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
- // Widgets are available.
- val widgets =
- listOf(
- CommunalWidgetContentModel(
- appWidgetId = 0,
- priority = 30,
- providerInfo = mock(),
- ),
- CommunalWidgetContentModel(
- appWidgetId = 1,
- priority = 20,
- providerInfo = mock(),
- ),
- CommunalWidgetContentModel(
- appWidgetId = 2,
- priority = 10,
- providerInfo = mock(),
- ),
- )
+ val userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK)
+ userRepository.setUserInfos(userInfos)
+ userTracker.set(
+ userInfos = userInfos,
+ selectedUserIndex = 0,
+ )
+ runCurrent()
+
+ // Widgets available.
+ val widget1 = createWidgetForUser(1, USER_INFO_WORK.id)
+ val widget2 = createWidgetForUser(2, MAIN_USER_INFO.id)
+ val widget3 = createWidgetForUser(3, MAIN_USER_INFO.id)
+ val widgets = listOf(widget1, widget2, widget3)
widgetRepository.setCommunalWidgets(widgets)
val widgetContent by collectLastValue(underTest.widgetContent)
@@ -449,6 +455,9 @@ class CommunalInteractorTest : SysuiTestCase() {
@Test
fun listensToSceneChange() =
testScope.runTest {
+ kosmos.setCommunalAvailable(true)
+ runCurrent()
+
var desiredScene = collectLastValue(underTest.desiredScene)
runCurrent()
assertThat(desiredScene()).isEqualTo(CommunalSceneKey.Blank)
@@ -473,6 +482,30 @@ class CommunalInteractorTest : SysuiTestCase() {
}
@Test
+ fun desiredScene_communalNotAvailable_returnsBlank() =
+ testScope.runTest {
+ kosmos.setCommunalAvailable(true)
+ runCurrent()
+
+ val desiredScene by collectLastValue(underTest.desiredScene)
+
+ underTest.onSceneChanged(CommunalSceneKey.Communal)
+ assertThat(desiredScene).isEqualTo(CommunalSceneKey.Communal)
+
+ kosmos.setCommunalAvailable(false)
+ runCurrent()
+
+ // Scene returns blank when communal is not available.
+ assertThat(desiredScene).isEqualTo(CommunalSceneKey.Blank)
+
+ kosmos.setCommunalAvailable(true)
+ runCurrent()
+
+ // After re-enabling, scene goes back to Communal.
+ assertThat(desiredScene).isEqualTo(CommunalSceneKey.Communal)
+ }
+
+ @Test
fun transitionProgress_onTargetScene_fullProgress() =
testScope.runTest {
val targetScene = CommunalSceneKey.Blank
@@ -600,6 +633,9 @@ class CommunalInteractorTest : SysuiTestCase() {
@Test
fun isCommunalShowing() =
testScope.runTest {
+ kosmos.setCommunalAvailable(true)
+ runCurrent()
+
var isCommunalShowing = collectLastValue(underTest.isCommunalShowing)
runCurrent()
assertThat(isCommunalShowing()).isEqualTo(false)
@@ -612,6 +648,59 @@ class CommunalInteractorTest : SysuiTestCase() {
}
@Test
+ fun isCommunalShowing_whenSceneContainerDisabled() =
+ testScope.runTest {
+ kosmos.setCommunalAvailable(true)
+ runCurrent()
+
+ // Verify default is false
+ val isCommunalShowing by collectLastValue(underTest.isCommunalShowing)
+ runCurrent()
+ assertThat(isCommunalShowing).isFalse()
+
+ // Verify scene changes with the flag doesn't have any impact
+ sceneInteractor.changeScene(SceneKey.Communal, loggingReason = "")
+ runCurrent()
+ assertThat(isCommunalShowing).isFalse()
+
+ // Verify scene changes (without the flag) to communal sets the value to true
+ underTest.onSceneChanged(CommunalSceneKey.Communal)
+ runCurrent()
+ assertThat(isCommunalShowing).isTrue()
+
+ // Verify scene changes (without the flag) to blank sets the value back to false
+ underTest.onSceneChanged(CommunalSceneKey.Blank)
+ runCurrent()
+ assertThat(isCommunalShowing).isFalse()
+ }
+
+ @Test
+ fun isCommunalShowing_whenSceneContainerEnabled() =
+ testScope.runTest {
+ kosmos.fakeSceneContainerFlags.enabled = true
+
+ // Verify default is false
+ val isCommunalShowing by collectLastValue(underTest.isCommunalShowing)
+ runCurrent()
+ assertThat(isCommunalShowing).isFalse()
+
+ // Verify scene changes without the flag doesn't have any impact
+ underTest.onSceneChanged(CommunalSceneKey.Communal)
+ runCurrent()
+ assertThat(isCommunalShowing).isFalse()
+
+ // Verify scene changes (with the flag) to communal sets the value to true
+ sceneInteractor.changeScene(SceneKey.Communal, loggingReason = "")
+ runCurrent()
+ assertThat(isCommunalShowing).isTrue()
+
+ // Verify scene changes (with the flag) to lockscreen sets the value to false
+ sceneInteractor.changeScene(SceneKey.Lockscreen, loggingReason = "")
+ runCurrent()
+ assertThat(isCommunalShowing).isFalse()
+ }
+
+ @Test
fun isIdleOnCommunal() =
testScope.runTest {
val transitionState =
@@ -710,6 +799,38 @@ class CommunalInteractorTest : SysuiTestCase() {
verify(editWidgetsActivityStarter).startActivity(widgetKey)
}
+ @Test
+ fun filterWidgets_whenUserProfileRemoved() =
+ testScope.runTest {
+ // Keyguard showing, and tutorial completed.
+ keyguardRepository.setKeyguardShowing(true)
+ keyguardRepository.setKeyguardOccluded(false)
+ tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+
+ // Only main user exists.
+ val userInfos = listOf(MAIN_USER_INFO)
+ userRepository.setUserInfos(userInfos)
+ userTracker.set(
+ userInfos = userInfos,
+ selectedUserIndex = 0,
+ )
+ runCurrent()
+
+ val widgetContent by collectLastValue(underTest.widgetContent)
+ // Given three widgets, and one of them is associated with pre-existing work profile.
+ val widget1 = createWidgetForUser(1, USER_INFO_WORK.id)
+ val widget2 = createWidgetForUser(2, MAIN_USER_INFO.id)
+ val widget3 = createWidgetForUser(3, MAIN_USER_INFO.id)
+ val widgets = listOf(widget1, widget2, widget3)
+ widgetRepository.setCommunalWidgets(widgets)
+
+ // One widget is filtered out and the remaining two link to main user id.
+ assertThat(checkNotNull(widgetContent).size).isEqualTo(2)
+ widgetContent!!.forEachIndexed { _, model ->
+ assertThat(model.providerInfo.profile?.identifier).isEqualTo(MAIN_USER_INFO.id)
+ }
+ }
+
private fun smartspaceTimer(id: String, timestamp: Long = 0L): SmartspaceTarget {
val timer = mock(SmartspaceTarget::class.java)
whenever(timer.smartspaceTargetId).thenReturn(id)
@@ -718,4 +839,17 @@ class CommunalInteractorTest : SysuiTestCase() {
whenever(timer.creationTimeMillis).thenReturn(timestamp)
return timer
}
+
+ private fun createWidgetForUser(appWidgetId: Int, userId: Int): CommunalWidgetContentModel =
+ mock<CommunalWidgetContentModel> {
+ whenever(this.appWidgetId).thenReturn(appWidgetId)
+ val providerInfo = mock<AppWidgetProviderInfo>()
+ whenever(providerInfo.profile).thenReturn(UserHandle(userId))
+ whenever(this.providerInfo).thenReturn(providerInfo)
+ }
+
+ private companion object {
+ val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
+ val USER_INFO_WORK = UserInfo(10, "work", UserInfo.FLAG_PROFILE)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
index ceb7fac1046f..8b785927ba5e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
@@ -16,7 +16,6 @@
package com.android.systemui.communal.domain.interactor
-import android.content.pm.UserInfo
import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED
import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_NOT_STARTED
import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_STARTED
@@ -24,10 +23,9 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.SysuiTestCase
-import com.android.systemui.communal.data.repository.FakeCommunalRepository
import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
-import com.android.systemui.communal.data.repository.fakeCommunalRepository
import com.android.systemui.communal.data.repository.fakeCommunalTutorialRepository
+import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
@@ -38,13 +36,11 @@ import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.data.repository.fakeUserRepository
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class CommunalTutorialInteractorTest : SysuiTestCase() {
@@ -54,7 +50,6 @@ class CommunalTutorialInteractorTest : SysuiTestCase() {
private lateinit var underTest: CommunalTutorialInteractor
private lateinit var keyguardRepository: FakeKeyguardRepository
private lateinit var communalTutorialRepository: FakeCommunalTutorialRepository
- private lateinit var communalRepository: FakeCommunalRepository
private lateinit var communalInteractor: CommunalInteractor
private lateinit var userRepository: FakeUserRepository
@@ -62,11 +57,9 @@ class CommunalTutorialInteractorTest : SysuiTestCase() {
fun setUp() {
keyguardRepository = kosmos.fakeKeyguardRepository
communalTutorialRepository = kosmos.fakeCommunalTutorialRepository
- communalRepository = kosmos.fakeCommunalRepository
communalInteractor = kosmos.communalInteractor
userRepository = kosmos.fakeUserRepository
- userRepository.setUserInfos(listOf(MAIN_USER_INFO))
kosmos.fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true)
mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
@@ -77,7 +70,7 @@ class CommunalTutorialInteractorTest : SysuiTestCase() {
fun tutorialUnavailable_whenKeyguardNotVisible() =
testScope.runTest {
val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
- setCommunalAvailable(true)
+ kosmos.setCommunalAvailable(true)
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
keyguardRepository.setKeyguardShowing(false)
assertThat(isTutorialAvailable).isFalse()
@@ -87,10 +80,7 @@ class CommunalTutorialInteractorTest : SysuiTestCase() {
fun tutorialUnavailable_whenTutorialIsCompleted() =
testScope.runTest {
val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
- setCommunalAvailable(true)
- keyguardRepository.setKeyguardShowing(true)
- keyguardRepository.setKeyguardOccluded(false)
- communalRepository.setIsCommunalHubShowing(false)
+ goToCommunal()
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
assertThat(isTutorialAvailable).isFalse()
}
@@ -99,7 +89,7 @@ class CommunalTutorialInteractorTest : SysuiTestCase() {
fun tutorialUnavailable_whenCommunalNotAvailable() =
testScope.runTest {
val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
- setCommunalAvailable(false)
+ kosmos.setCommunalAvailable(false)
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
keyguardRepository.setKeyguardShowing(true)
assertThat(isTutorialAvailable).isFalse()
@@ -109,10 +99,7 @@ class CommunalTutorialInteractorTest : SysuiTestCase() {
fun tutorialAvailable_whenTutorialNotStarted() =
testScope.runTest {
val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
- setCommunalAvailable(true)
- keyguardRepository.setKeyguardShowing(true)
- keyguardRepository.setKeyguardOccluded(false)
- communalRepository.setIsCommunalHubShowing(false)
+ kosmos.setCommunalAvailable(true)
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
assertThat(isTutorialAvailable).isTrue()
}
@@ -121,10 +108,7 @@ class CommunalTutorialInteractorTest : SysuiTestCase() {
fun tutorialAvailable_whenTutorialIsStarted() =
testScope.runTest {
val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
- setCommunalAvailable(true)
- keyguardRepository.setKeyguardShowing(true)
- keyguardRepository.setKeyguardOccluded(false)
- communalRepository.setIsCommunalHubShowing(true)
+ goToCommunal()
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
assertThat(isTutorialAvailable).isTrue()
}
@@ -134,10 +118,9 @@ class CommunalTutorialInteractorTest : SysuiTestCase() {
testScope.runTest {
val tutorialSettingState by
collectLastValue(communalTutorialRepository.tutorialSettingState)
- userRepository.setSelectedUserInfo(MAIN_USER_INFO)
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
- communalRepository.setIsCommunalHubShowing(true)
+ goToCommunal()
assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_STARTED)
}
@@ -147,10 +130,10 @@ class CommunalTutorialInteractorTest : SysuiTestCase() {
testScope.runTest {
val tutorialSettingState by
collectLastValue(communalTutorialRepository.tutorialSettingState)
- userRepository.setSelectedUserInfo(MAIN_USER_INFO)
+
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
- communalRepository.setIsCommunalHubShowing(true)
+ goToCommunal()
assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_STARTED)
}
@@ -160,10 +143,9 @@ class CommunalTutorialInteractorTest : SysuiTestCase() {
testScope.runTest {
val tutorialSettingState by
collectLastValue(communalTutorialRepository.tutorialSettingState)
- userRepository.setSelectedUserInfo(MAIN_USER_INFO)
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
- communalRepository.setIsCommunalHubShowing(true)
+ goToCommunal()
assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
}
@@ -173,10 +155,10 @@ class CommunalTutorialInteractorTest : SysuiTestCase() {
testScope.runTest {
val tutorialSettingState by
collectLastValue(communalTutorialRepository.tutorialSettingState)
- userRepository.setSelectedUserInfo(MAIN_USER_INFO)
+ kosmos.setCommunalAvailable(true)
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
- communalRepository.setIsCommunalHubShowing(false)
+ communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_NOT_STARTED)
}
@@ -186,11 +168,10 @@ class CommunalTutorialInteractorTest : SysuiTestCase() {
testScope.runTest {
val tutorialSettingState by
collectLastValue(communalTutorialRepository.tutorialSettingState)
- userRepository.setSelectedUserInfo(MAIN_USER_INFO)
- communalRepository.setIsCommunalHubShowing(true)
+ goToCommunal()
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
- communalRepository.setIsCommunalHubShowing(false)
+ communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
}
@@ -200,26 +181,16 @@ class CommunalTutorialInteractorTest : SysuiTestCase() {
testScope.runTest {
val tutorialSettingState by
collectLastValue(communalTutorialRepository.tutorialSettingState)
- userRepository.setSelectedUserInfo(MAIN_USER_INFO)
- communalRepository.setIsCommunalHubShowing(true)
+ goToCommunal()
communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
- communalRepository.setIsCommunalHubShowing(false)
+ communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
}
- private suspend fun setCommunalAvailable(available: Boolean) {
- if (available) {
- keyguardRepository.setIsEncryptedOrLockdown(false)
- userRepository.setSelectedUserInfo(MAIN_USER_INFO)
- keyguardRepository.setKeyguardShowing(true)
- } else {
- keyguardRepository.setIsEncryptedOrLockdown(true)
- }
- }
-
- private companion object {
- val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
+ private suspend fun goToCommunal() {
+ kosmos.setCommunalAvailable(true)
+ communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
index cf727cf5e180..5ee88cb92fa0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
@@ -17,6 +17,9 @@
package com.android.systemui.communal.view.viewmodel
import android.app.smartspace.SmartspaceTarget
+import android.appwidget.AppWidgetProviderInfo
+import android.content.pm.UserInfo
+import android.os.UserHandle
import android.provider.Settings
import android.widget.RemoteViews
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -30,6 +33,7 @@ import com.android.systemui.communal.data.repository.fakeCommunalMediaRepository
import com.android.systemui.communal.data.repository.fakeCommunalTutorialRepository
import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.log.CommunalUiEvent
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
@@ -37,7 +41,8 @@ import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.logcatLogBuffer
-import com.android.systemui.media.controls.ui.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
import com.android.systemui.smartspace.data.repository.fakeSmartspaceRepository
import com.android.systemui.testKosmos
@@ -58,6 +63,7 @@ import org.mockito.MockitoAnnotations
class CommunalEditModeViewModelTest : SysuiTestCase() {
@Mock private lateinit var mediaHost: MediaHost
@Mock private lateinit var uiEventLogger: UiEventLogger
+ @Mock private lateinit var providerInfo: AppWidgetProviderInfo
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
@@ -77,10 +83,16 @@ class CommunalEditModeViewModelTest : SysuiTestCase() {
widgetRepository = kosmos.fakeCommunalWidgetRepository
smartspaceRepository = kosmos.fakeSmartspaceRepository
mediaRepository = kosmos.fakeCommunalMediaRepository
+ kosmos.fakeUserTracker.set(
+ userInfos = listOf(MAIN_USER_INFO),
+ selectedUserIndex = 0,
+ )
+ whenever(providerInfo.profile).thenReturn(UserHandle(MAIN_USER_INFO.id))
underTest =
CommunalEditModeViewModel(
kosmos.communalInteractor,
+ kosmos.communalSettingsInteractor,
mediaHost,
uiEventLogger,
logcatLogBuffer("CommunalEditModeViewModelTest"),
@@ -98,12 +110,12 @@ class CommunalEditModeViewModelTest : SysuiTestCase() {
CommunalWidgetContentModel(
appWidgetId = 0,
priority = 30,
- providerInfo = mock(),
+ providerInfo = providerInfo,
),
CommunalWidgetContentModel(
appWidgetId = 1,
priority = 20,
- providerInfo = mock(),
+ providerInfo = providerInfo,
),
)
widgetRepository.setCommunalWidgets(widgets)
@@ -154,12 +166,12 @@ class CommunalEditModeViewModelTest : SysuiTestCase() {
CommunalWidgetContentModel(
appWidgetId = 0,
priority = 30,
- providerInfo = mock(),
+ providerInfo = providerInfo,
),
CommunalWidgetContentModel(
appWidgetId = 1,
priority = 20,
- providerInfo = mock(),
+ providerInfo = providerInfo,
),
)
widgetRepository.setCommunalWidgets(widgets)
@@ -203,4 +215,8 @@ class CommunalEditModeViewModelTest : SysuiTestCase() {
underTest.onReorderWidgetCancel()
verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_CANCEL)
}
+
+ private companion object {
+ val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
index b299ca7ee804..1e523dd2a9cc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
@@ -17,7 +17,9 @@
package com.android.systemui.communal.view.viewmodel
import android.app.smartspace.SmartspaceTarget
+import android.appwidget.AppWidgetProviderInfo
import android.content.pm.UserInfo
+import android.os.UserHandle
import android.provider.Settings
import android.widget.RemoteViews
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -43,15 +45,15 @@ import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.logcatLogBuffer
-import com.android.systemui.media.controls.ui.MediaHierarchyManager
-import com.android.systemui.media.controls.ui.MediaHost
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
import com.android.systemui.smartspace.data.repository.fakeSmartspaceRepository
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.data.repository.fakeUserRepository
-import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -71,6 +73,7 @@ import org.mockito.MockitoAnnotations
class CommunalViewModelTest : SysuiTestCase() {
@Mock private lateinit var mediaHost: MediaHost
@Mock private lateinit var user: UserInfo
+ @Mock private lateinit var providerInfo: AppWidgetProviderInfo
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
@@ -98,6 +101,12 @@ class CommunalViewModelTest : SysuiTestCase() {
kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
+ kosmos.fakeUserTracker.set(
+ userInfos = listOf(MAIN_USER_INFO),
+ selectedUserIndex = 0,
+ )
+ whenever(providerInfo.profile).thenReturn(UserHandle(MAIN_USER_INFO.id))
+
underTest =
CommunalViewModel(
testScope,
@@ -147,12 +156,12 @@ class CommunalViewModelTest : SysuiTestCase() {
CommunalWidgetContentModel(
appWidgetId = 0,
priority = 30,
- providerInfo = mock(),
+ providerInfo = providerInfo,
),
CommunalWidgetContentModel(
appWidgetId = 1,
priority = 20,
- providerInfo = mock(),
+ providerInfo = providerInfo,
),
)
widgetRepository.setCommunalWidgets(widgets)
@@ -225,4 +234,8 @@ class CommunalViewModelTest : SysuiTestCase() {
userRepository.setUserInfos(listOf(user))
userRepository.setSelectedUserInfo(user)
}
+
+ private companion object {
+ val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
index 8488843905f7..2c9d72c423bc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartableTest.kt
@@ -16,7 +16,9 @@
package com.android.systemui.communal.widgets
+import android.appwidget.AppWidgetProviderInfo
import android.content.pm.UserInfo
+import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
@@ -32,6 +34,7 @@ import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
+import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.fakeUserRepository
import com.android.systemui.util.mockito.mock
@@ -65,7 +68,7 @@ class CommunalAppWidgetHostStartableTest : SysuiTestCase() {
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- kosmos.fakeUserRepository.setUserInfos(listOf(MAIN_USER_INFO))
+ kosmos.fakeUserRepository.setUserInfos(listOf(MAIN_USER_INFO, USER_INFO_WORK))
kosmos.fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true)
mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
@@ -76,6 +79,7 @@ class CommunalAppWidgetHostStartableTest : SysuiTestCase() {
CommunalAppWidgetHostStartable(
appWidgetHost,
kosmos.communalInteractor,
+ kosmos.fakeUserTracker,
kosmos.applicationCoroutineScope,
kosmos.testDispatcher,
)
@@ -170,6 +174,46 @@ class CommunalAppWidgetHostStartableTest : SysuiTestCase() {
}
}
+ @Test
+ fun removeWidgetsForDeletedProfile_whenCommunalIsAvailable() =
+ with(kosmos) {
+ testScope.runTest {
+ // Communal is available and work profile is configured.
+ setCommunalAvailable(true)
+ kosmos.fakeUserTracker.set(
+ userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK),
+ selectedUserIndex = 0,
+ )
+ val widget1 = createWidgetForUser(1, USER_INFO_WORK.id)
+ val widget2 = createWidgetForUser(2, MAIN_USER_INFO.id)
+ val widget3 = createWidgetForUser(3, MAIN_USER_INFO.id)
+ val widgets = listOf(widget1, widget2, widget3)
+ fakeCommunalWidgetRepository.setCommunalWidgets(widgets)
+
+ underTest.start()
+ runCurrent()
+
+ val communalWidgets by
+ collectLastValue(fakeCommunalWidgetRepository.communalWidgets)
+ assertThat(communalWidgets).containsExactly(widget1, widget2, widget3)
+
+ // Unlock the device and remove work profile.
+ fakeKeyguardRepository.setKeyguardShowing(false)
+ kosmos.fakeUserTracker.set(
+ userInfos = listOf(MAIN_USER_INFO),
+ selectedUserIndex = 0,
+ )
+ runCurrent()
+
+ // Communal becomes available.
+ fakeKeyguardRepository.setKeyguardShowing(true)
+ runCurrent()
+
+ // Widget created for work profile is removed.
+ assertThat(communalWidgets).containsExactly(widget2, widget3)
+ }
+ }
+
private suspend fun setCommunalAvailable(available: Boolean) =
with(kosmos) {
fakeKeyguardRepository.setIsEncryptedOrLockdown(false)
@@ -179,7 +223,16 @@ class CommunalAppWidgetHostStartableTest : SysuiTestCase() {
fakeSettings.putIntForUser(GLANCEABLE_HUB_ENABLED, settingsValue, MAIN_USER_INFO.id)
}
+ private fun createWidgetForUser(appWidgetId: Int, userId: Int): CommunalWidgetContentModel =
+ mock<CommunalWidgetContentModel> {
+ whenever(this.appWidgetId).thenReturn(appWidgetId)
+ val providerInfo = mock<AppWidgetProviderInfo>()
+ whenever(providerInfo.profile).thenReturn(UserHandle(userId))
+ whenever(this.providerInfo).thenReturn(providerInfo)
+ }
+
private companion object {
val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
+ val USER_INFO_WORK = UserInfo(10, "work", UserInfo.FLAG_PROFILE)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalWidgetHostTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalWidgetHostTest.kt
new file mode 100644
index 000000000000..12611cbd8c63
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalWidgetHostTest.kt
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2024 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.communal.widgets
+
+import android.appwidget.AppWidgetManager
+import android.content.ComponentName
+import android.content.pm.UserInfo
+import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.log.logcatLogBuffer
+import com.android.systemui.testKosmos
+import com.android.systemui.user.data.model.SelectedUserModel
+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.user.domain.interactor.selectedUserInteractor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.nullable
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import java.util.Optional
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+class CommunalWidgetHostTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+
+ @Mock private lateinit var appWidgetManager: AppWidgetManager
+ @Mock private lateinit var appWidgetHost: CommunalAppWidgetHost
+ private val selectedUserInteractor: SelectedUserInteractor by lazy {
+ kosmos.selectedUserInteractor
+ }
+
+ private lateinit var underTest: CommunalWidgetHost
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ underTest =
+ CommunalWidgetHost(
+ Optional.of(appWidgetManager),
+ appWidgetHost,
+ selectedUserInteractor,
+ logcatLogBuffer("CommunalWidgetHostTest"),
+ )
+ }
+
+ @Test
+ fun allocateIdAndBindWidget_withCurrentUser() =
+ testScope.runTest {
+ val provider = ComponentName("pkg_name", "cls_name")
+ val widgetId = 1
+ val userId by collectLastValue(selectedUserInteractor.selectedUser)
+ selectUser()
+ runCurrent()
+
+ val user = UserHandle(checkNotNull(userId))
+ whenever(appWidgetHost.allocateAppWidgetId()).thenReturn(widgetId)
+ whenever(
+ appWidgetManager.bindAppWidgetIdIfAllowed(
+ any<Int>(),
+ any<UserHandle>(),
+ any<ComponentName>(),
+ nullable()
+ )
+ )
+ .thenReturn(true)
+
+ // bind the widget with the current user when no user is explicitly set
+ val result = underTest.allocateIdAndBindWidget(provider)
+
+ verify(appWidgetHost).allocateAppWidgetId()
+ verify(appWidgetManager).bindAppWidgetIdIfAllowed(widgetId, user, provider, null)
+ assertThat(result).isEqualTo(widgetId)
+ }
+
+ @Test
+ fun allocateIdAndBindWidget_onSuccess() =
+ testScope.runTest {
+ val provider = ComponentName("pkg_name", "cls_name")
+ val widgetId = 1
+ val user = UserHandle(0)
+
+ whenever(appWidgetHost.allocateAppWidgetId()).thenReturn(widgetId)
+ whenever(
+ appWidgetManager.bindAppWidgetIdIfAllowed(
+ any<Int>(),
+ any<UserHandle>(),
+ any<ComponentName>(),
+ nullable()
+ )
+ )
+ .thenReturn(true)
+
+ // provider and user handle are both set
+ val result = underTest.allocateIdAndBindWidget(provider, user)
+
+ verify(appWidgetHost).allocateAppWidgetId()
+ verify(appWidgetManager).bindAppWidgetIdIfAllowed(widgetId, user, provider, null)
+ assertThat(result).isEqualTo(widgetId)
+ }
+
+ @Test
+ fun allocateIdAndBindWidget_onFailure() =
+ testScope.runTest {
+ val provider = ComponentName("pkg_name", "cls_name")
+ val widgetId = 1
+ val user = UserHandle(0)
+
+ whenever(appWidgetHost.allocateAppWidgetId()).thenReturn(widgetId)
+ // failed to bind widget
+ whenever(
+ appWidgetManager.bindAppWidgetIdIfAllowed(
+ any<Int>(),
+ any<UserHandle>(),
+ any<ComponentName>(),
+ nullable()
+ )
+ )
+ .thenReturn(false)
+ val result = underTest.allocateIdAndBindWidget(provider, user)
+
+ verify(appWidgetHost).allocateAppWidgetId()
+ verify(appWidgetManager).bindAppWidgetIdIfAllowed(widgetId, user, provider, null)
+ verify(appWidgetHost).deleteAppWidgetId(widgetId)
+ assertThat(result).isNull()
+ }
+
+ private fun selectUser() {
+ kosmos.fakeUserRepository.selectedUser.value =
+ SelectedUserModel(
+ userInfo = UserInfo(0, "Current user", 0),
+ selectionStatus = SelectionStatus.SELECTION_COMPLETE
+ )
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
index 8a35ef11a364..c670506d9f04 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
@@ -8,10 +8,9 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.complication.ComplicationHostViewController
-import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel
+import com.android.systemui.dreams.ui.viewmodel.DreamOverlayViewModel
import com.android.systemui.log.core.FakeLogBuffer
import com.android.systemui.statusbar.BlurUtils
-import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -46,8 +45,7 @@ class DreamOverlayAnimationsControllerTest : SysuiTestCase() {
@Mock private lateinit var hostViewController: ComplicationHostViewController
@Mock private lateinit var statusBarViewController: DreamOverlayStatusBarViewController
@Mock private lateinit var stateController: DreamOverlayStateController
- @Mock private lateinit var configController: ConfigurationController
- @Mock private lateinit var transitionViewModel: DreamingToLockscreenTransitionViewModel
+ @Mock private lateinit var transitionViewModel: DreamOverlayViewModel
private val logBuffer = FakeLogBuffer.Factory.create()
private lateinit var controller: DreamOverlayAnimationsController
@@ -62,7 +60,6 @@ class DreamOverlayAnimationsControllerTest : SysuiTestCase() {
stateController,
DREAM_BLUR_RADIUS,
transitionViewModel,
- configController,
DREAM_IN_BLUR_ANIMATION_DURATION,
DREAM_IN_COMPLICATIONS_ANIMATION_DURATION,
DREAM_IN_TRANSLATION_Y_DISTANCE,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
index f6c056698967..fb46ed9d54ed 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
@@ -175,6 +175,21 @@ class LightRevealScrimRepositoryTest : SysuiTestCase() {
animatorTestRule.advanceTimeBy(500L)
assertEquals(1.0f, value)
}
+
+ @Test
+ @TestableLooper.RunWithLooper(setAsMainLooper = true)
+ fun revealAmount_startingRevealTwiceWontRerunAnimator() =
+ runTest(UnconfinedTestDispatcher()) {
+ val value by collectLastValue(underTest.revealAmount)
+ underTest.startRevealAmountAnimator(true)
+ assertEquals(0.0f, value)
+ animatorTestRule.advanceTimeBy(250L)
+ assertEquals(0.5f, value)
+ underTest.startRevealAmountAnimator(true)
+ animatorTestRule.advanceTimeBy(250L)
+ assertEquals(1.0f, value)
+ }
+
@Test
@TestableLooper.RunWithLooper(setAsMainLooper = true)
fun revealAmount_emitsTo0AfterAnimationStartedReversed() =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 0ebcf5608bff..63abc8f34668 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -27,7 +27,6 @@ import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.coroutines.collectValues
import com.android.systemui.dock.DockManager
import com.android.systemui.dock.DockManagerFake
import com.android.systemui.flags.FakeFeatureFlags
@@ -50,7 +49,6 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.FakeSharedPreferences
@@ -59,7 +57,6 @@ import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.FakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
@@ -83,7 +80,6 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var launchAnimator: DialogTransitionAnimator
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
- @Mock private lateinit var shadeInteractor: ShadeInteractor
@Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger
private lateinit var underTest: KeyguardQuickAffordanceInteractor
@@ -183,7 +179,6 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
underTest =
KeyguardQuickAffordanceInteractor(
keyguardInteractor = withDeps.keyguardInteractor,
- shadeInteractor = shadeInteractor,
lockPatternUtils = lockPatternUtils,
keyguardStateController = keyguardStateController,
userTracker = userTracker,
@@ -198,8 +193,6 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
backgroundDispatcher = testDispatcher,
appContext = context,
)
-
- whenever(shadeInteractor.anyExpansion).thenReturn(MutableStateFlow(0f))
}
@Test
@@ -346,25 +339,6 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
}
@Test
- fun quickAffordance_updateOncePerShadeExpansion() =
- testScope.runTest {
- val shadeExpansion = MutableStateFlow(0f)
- whenever(shadeInteractor.anyExpansion).thenReturn(shadeExpansion)
-
- val collectedValue by
- collectValues(
- underTest.quickAffordance(KeyguardQuickAffordancePosition.BOTTOM_START)
- )
-
- val initialSize = collectedValue.size
- for (i in 0..10) {
- shadeExpansion.value = i / 10f
- }
-
- assertThat(collectedValue.size).isEqualTo(initialSize + 1)
- }
-
- @Test
fun quickAffordanceAlwaysVisible_notVisible_restrictedByPolicyManager() =
testScope.runTest {
whenever(devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
index 9368097c0ac9..3484025f8d80 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
@@ -197,7 +197,16 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() {
runCurrent()
}
- assertThat(startedSteps).isEqualTo(listOf(steps[0], steps[3], steps[6]))
+ assertThat(startedSteps)
+ .isEqualTo(
+ listOf(
+ // The initial transition will also get sent when collect started
+ TransitionStep(OFF, LOCKSCREEN, 0f, STARTED),
+ steps[0],
+ steps[3],
+ steps[6]
+ )
+ )
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
index 9b302ae60e21..2b6e6c7c1575 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
@@ -22,7 +22,6 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.fakeLightRevealScrimRepository
import com.android.systemui.keyguard.data.repository.FakeLightRevealScrimRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
-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.kosmos.testScope
@@ -33,16 +32,11 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito
-import org.mockito.Mockito.anyBoolean
-import org.mockito.Mockito.never
-import org.mockito.Mockito.reset
-import org.mockito.Mockito.verify
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
@@ -103,41 +97,4 @@ class LightRevealScrimInteractorTest : SysuiTestCase() {
job.cancel()
}
-
- @Test
- fun lightRevealEffect_startsAnimationOnlyForDifferentStateTargets() =
- testScope.runTest {
- runCurrent()
- reset(fakeLightRevealScrimRepository)
-
- fakeKeyguardTransitionRepository.sendTransitionStep(
- TransitionStep(
- transitionState = TransitionState.STARTED,
- from = KeyguardState.OFF,
- to = KeyguardState.OFF
- )
- )
- runCurrent()
- verify(fakeLightRevealScrimRepository, never()).startRevealAmountAnimator(anyBoolean())
-
- fakeKeyguardTransitionRepository.sendTransitionStep(
- TransitionStep(
- transitionState = TransitionState.STARTED,
- from = KeyguardState.DOZING,
- to = KeyguardState.LOCKSCREEN
- )
- )
- runCurrent()
- verify(fakeLightRevealScrimRepository).startRevealAmountAnimator(true)
-
- fakeKeyguardTransitionRepository.sendTransitionStep(
- TransitionStep(
- transitionState = TransitionState.STARTED,
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.DOZING
- )
- )
- runCurrent()
- verify(fakeLightRevealScrimRepository).startRevealAmountAnimator(false)
- }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelTest.kt
index 837a9db6eea7..d33c10e7e663 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelTest.kt
@@ -20,6 +20,7 @@ package com.android.systemui.keyguard.ui.viewmodel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
@@ -66,6 +67,25 @@ class AodAlphaViewModelTest : SysuiTestCase() {
}
@Test
+ fun alpha_WhenNotGone_clockMigrationFlagIsOff_emitsKeyguardAlpha() =
+ testScope.runTest {
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
+ val alpha by collectLastValue(underTest.alpha)
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ testScope = testScope,
+ )
+
+ keyguardRepository.setKeyguardAlpha(0.5f)
+ assertThat(alpha).isEqualTo(0.5f)
+
+ keyguardRepository.setKeyguardAlpha(0.8f)
+ assertThat(alpha).isEqualTo(0.8f)
+ }
+
+ @Test
fun alpha_WhenGoneToAod() =
testScope.runTest {
val alpha by collectLastValue(underTest.alpha)
@@ -112,6 +132,7 @@ class AodAlphaViewModelTest : SysuiTestCase() {
@Test
fun alpha_whenGone_equalsZero() =
testScope.runTest {
+ mSetFlagsRule.enableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
val alpha by collectLastValue(underTest.alpha)
keyguardTransitionRepository.sendTransitionStep(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
index 74fa46519629..b0f59fe68f11 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt
@@ -127,7 +127,7 @@ class AodBurnInViewModelTest : SysuiTestCase() {
@Test
fun translationAndScale_whenFullyDozing() =
testScope.runTest {
- burnInParameters = burnInParameters.copy(statusViewTop = 100)
+ burnInParameters = burnInParameters.copy(minViewY = 100)
val translationX by collectLastValue(underTest.translationX(burnInParameters))
val translationY by collectLastValue(underTest.translationY(burnInParameters))
val scale by collectLastValue(underTest.scale(burnInParameters))
@@ -182,11 +182,77 @@ class AodBurnInViewModelTest : SysuiTestCase() {
}
@Test
- fun translationAndScale_whenFullyDozing_staysOutOfTopInset() =
+ fun translationAndScale_whenFullyDozing_MigrationFlagOff_staysOutOfTopInset() =
testScope.runTest {
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
+
+ burnInParameters =
+ burnInParameters.copy(
+ minViewY = 100,
+ topInset = 80,
+ )
+ val translationX by collectLastValue(underTest.translationX(burnInParameters))
+ val translationY by collectLastValue(underTest.translationY(burnInParameters))
+ val scale by collectLastValue(underTest.scale(burnInParameters))
+
+ // Set to dozing (on AOD)
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ value = 1f,
+ transitionState = TransitionState.FINISHED
+ ),
+ validateStep = false,
+ )
+
+ // Trigger a change to the burn-in model
+ burnInFlow.value =
+ BurnInModel(
+ translationX = 20,
+ translationY = -30,
+ scale = 0.5f,
+ )
+ assertThat(translationX).isEqualTo(20)
+ // -20 instead of -30, due to inset of 80
+ assertThat(translationY).isEqualTo(-20)
+ assertThat(scale)
+ .isEqualTo(
+ BurnInScaleViewModel(
+ scale = 0.5f,
+ scaleClockOnly = true,
+ )
+ )
+
+ // Set to the beginning of GONE->AOD transition
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ value = 0f,
+ transitionState = TransitionState.STARTED
+ ),
+ validateStep = false,
+ )
+ assertThat(translationX).isEqualTo(0)
+ assertThat(translationY).isEqualTo(0)
+ assertThat(scale)
+ .isEqualTo(
+ BurnInScaleViewModel(
+ scale = 1f,
+ scaleClockOnly = true,
+ )
+ )
+ }
+
+ @Test
+ fun translationAndScale_whenFullyDozing_MigrationFlagOn_staysOutOfTopInset() =
+ testScope.runTest {
+ mSetFlagsRule.enableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
+
burnInParameters =
burnInParameters.copy(
- statusViewTop = 100,
+ minViewY = 100,
topInset = 80,
)
val translationX by collectLastValue(underTest.translationX(burnInParameters))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt
index c381749ec6d3..31b67b43adc4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt
@@ -27,6 +27,7 @@ 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.kosmos.testScope
+import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -42,6 +43,7 @@ class AodToLockscreenTransitionViewModelTest : SysuiTestCase() {
val kosmos = testKosmos()
val testScope = kosmos.testScope
val repository = kosmos.fakeKeyguardTransitionRepository
+ val shadeRepository = kosmos.fakeShadeRepository
val fingerprintPropertyRepository = kosmos.fingerprintPropertyRepository
val underTest = kosmos.aodToLockscreenTransitionViewModel
@@ -59,6 +61,38 @@ class AodToLockscreenTransitionViewModelTest : SysuiTestCase() {
}
@Test
+ fun notificationAlpha_whenShadeIsExpanded_equalsOne() =
+ testScope.runTest {
+ val alpha by collectLastValue(underTest.notificationAlpha)
+
+ shadeRepository.setQsExpansion(0.5f)
+ runCurrent()
+
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(alpha).isEqualTo(1f)
+ repository.sendTransitionStep(step(0.5f))
+ assertThat(alpha).isEqualTo(1f)
+ repository.sendTransitionStep(step(1f))
+ assertThat(alpha).isEqualTo(1f)
+ }
+
+ @Test
+ fun notificationAlpha_whenShadeIsNotExpanded_usesTransitionValue() =
+ testScope.runTest {
+ val alpha by collectLastValue(underTest.notificationAlpha)
+
+ shadeRepository.setQsExpansion(0f)
+ runCurrent()
+
+ repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ assertThat(alpha).isEqualTo(0f)
+ repository.sendTransitionStep(step(0.5f))
+ assertThat(alpha).isEqualTo(0.5f)
+ repository.sendTransitionStep(step(1f))
+ assertThat(alpha).isEqualTo(1f)
+ }
+
+ @Test
fun lockscreenAlphaStartsFromViewStateAccessorAlpha() =
testScope.runTest {
val viewState = ViewStateAccessor(alpha = { 0.5f })
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModelTest.kt
new file mode 100644
index 000000000000..aba21c946e46
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModelTest.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2024 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.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+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.kosmos.testScope
+import com.android.systemui.res.R
+import com.android.systemui.testKosmos
+import com.google.common.collect.Range
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DreamingToGlanceableHubTransitionViewModelTest : SysuiTestCase() {
+ val kosmos = testKosmos()
+ val testScope = kosmos.testScope
+
+ val configurationRepository by lazy { kosmos.fakeConfigurationRepository }
+ val underTest by lazy { kosmos.dreamingToGlanceableHubTransitionViewModel }
+
+ @Test
+ fun dreamOverlayAlpha() =
+ testScope.runTest {
+ val values by collectValues(underTest.dreamOverlayAlpha)
+ assertThat(values).isEmpty()
+
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+ listOf(
+ // Should start running here...
+ step(0f, TransitionState.STARTED),
+ step(0f),
+ step(0.1f),
+ step(0.5f),
+ // Up to here...
+ step(1f),
+ ),
+ testScope,
+ )
+
+ assertThat(values).hasSize(4)
+ values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
+ }
+
+ @Test
+ fun dreamOverlayTranslationX() =
+ testScope.runTest {
+ configurationRepository.setDimensionPixelSize(
+ R.dimen.dreaming_to_hub_transition_dream_overlay_translation_x,
+ -100
+ )
+
+ val values by collectValues(underTest.dreamOverlayTranslationX)
+ assertThat(values).isEmpty()
+
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+ listOf(
+ step(0f, TransitionState.STARTED),
+ step(0.3f),
+ step(0.6f),
+ ),
+ testScope,
+ )
+
+ assertThat(values).hasSize(3)
+ values.forEach { assertThat(it).isIn(Range.closed(-100f, 0f)) }
+ }
+
+ private fun step(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return TransitionStep(
+ from = KeyguardState.DREAMING,
+ to = KeyguardState.GLANCEABLE_HUB,
+ value = value,
+ transitionState = state,
+ ownerName = "DreamingToGlanceableHubTransitionViewModelTest"
+ )
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModelTest.kt
new file mode 100644
index 000000000000..11890c74a418
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModelTest.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2024 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.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+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.kosmos.testScope
+import com.android.systemui.res.R
+import com.android.systemui.testKosmos
+import com.google.common.collect.Range
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class GlanceableHubToDreamingTransitionViewModelTest : SysuiTestCase() {
+ val kosmos = testKosmos()
+ val testScope = kosmos.testScope
+
+ val configurationRepository by lazy { kosmos.fakeConfigurationRepository }
+ val underTest by lazy { kosmos.glanceableHubToDreamingTransitionViewModel }
+
+ @Test
+ fun dreamOverlayAlpha() =
+ testScope.runTest {
+ val values by collectValues(underTest.dreamOverlayAlpha)
+ assertThat(values).isEmpty()
+
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+ listOf(
+ step(0f, TransitionState.STARTED),
+ step(0f),
+ // Should start running here...
+ step(0.1f),
+ step(0.5f),
+ // Up to here...
+ step(1f),
+ ),
+ testScope,
+ )
+
+ assertThat(values).hasSize(2)
+ values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
+ }
+
+ @Test
+ fun dreamOverlayTranslationX() =
+ testScope.runTest {
+ configurationRepository.setDimensionPixelSize(
+ R.dimen.hub_to_dreaming_transition_dream_overlay_translation_x,
+ 100
+ )
+
+ val values by collectValues(underTest.dreamOverlayTranslationX)
+ assertThat(values).isEmpty()
+
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+ listOf(
+ step(0f, TransitionState.STARTED),
+ step(0.3f),
+ step(0.6f),
+ ),
+ testScope,
+ )
+
+ assertThat(values).hasSize(3)
+ values.forEach { assertThat(it).isIn(Range.closed(-100f, 0f)) }
+ }
+
+ private fun step(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return TransitionStep(
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.DREAMING,
+ value = value,
+ transitionState = state,
+ ownerName = GlanceableHubToDreamingTransitionViewModelTest::class.java.simpleName
+ )
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelTest.kt
new file mode 100644
index 000000000000..64125f139a2e
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelTest.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2024 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.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+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.kosmos.testScope
+import com.android.systemui.res.R
+import com.android.systemui.testKosmos
+import com.google.common.collect.Range
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class GlanceableHubToLockscreenTransitionViewModelTest : SysuiTestCase() {
+ val kosmos = testKosmos()
+ val testScope = kosmos.testScope
+
+ val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+ val configurationRepository = kosmos.fakeConfigurationRepository
+ val underTest by lazy { kosmos.glanceableHubToLockscreenTransitionViewModel }
+
+ @Test
+ fun lockscreenFadeIn() =
+ testScope.runTest {
+ val values by collectValues(underTest.keyguardAlpha)
+ assertThat(values).containsExactly(0f)
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ listOf(
+ step(0f, TransitionState.STARTED),
+ // Should start running here...
+ step(0.1f),
+ step(0.2f),
+ step(0.3f),
+ step(0.4f),
+ // ...up to here
+ step(0.5f),
+ step(0.6f),
+ step(0.7f),
+ step(0.8f),
+ step(1f),
+ ),
+ testScope,
+ )
+
+ assertThat(values).hasSize(5)
+ values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
+ }
+
+ @Test
+ fun lockscreenTranslationX() =
+ testScope.runTest {
+ configurationRepository.setDimensionPixelSize(
+ R.dimen.hub_to_lockscreen_transition_lockscreen_translation_x,
+ 100
+ )
+ val values by collectValues(underTest.keyguardTranslationX)
+ assertThat(values).isEmpty()
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ listOf(
+ step(0f, TransitionState.STARTED),
+ step(0f),
+ step(0.3f),
+ step(0.5f),
+ step(1f),
+ ),
+ testScope,
+ )
+
+ assertThat(values).hasSize(5)
+ values.forEach { assertThat(it.value).isIn(Range.closed(-100f, 0f)) }
+ }
+
+ private fun step(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return TransitionStep(
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.LOCKSCREEN,
+ value = value,
+ transitionState = state,
+ ownerName = this::class.java.simpleName
+ )
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index e04cbfd88bb3..503fd34ce2c2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -39,6 +39,7 @@ 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.kosmos.testScope
+import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor
import com.android.systemui.statusbar.phone.dozeParameters
import com.android.systemui.statusbar.phone.screenOffAnimationController
@@ -72,6 +73,7 @@ class KeyguardRootViewModelTest : SysuiTestCase() {
private val deviceEntryRepository = kosmos.fakeDeviceEntryRepository
private val notificationsKeyguardInteractor = kosmos.notificationsKeyguardInteractor
private val dozeParameters = kosmos.dozeParameters
+ private val shadeRepository = kosmos.fakeShadeRepository
private val underTest by lazy { kosmos.keyguardRootViewModel }
private val viewState = ViewStateAccessor()
@@ -308,4 +310,22 @@ class KeyguardRootViewModelTest : SysuiTestCase() {
assertThat(alpha).isEqualTo(1.0f)
}
+
+ @Test
+ fun alpha_emitsOnShadeExpansion() =
+ testScope.runTest {
+ val alpha by collectLastValue(underTest.alpha(viewState))
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ testScope,
+ )
+
+ shadeRepository.setQsExpansion(0f)
+ assertThat(alpha).isEqualTo(1f)
+
+ shadeRepository.setQsExpansion(0.5f)
+ assertThat(alpha).isEqualTo(0f)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
index 3455050d8542..3104842a9c2a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
@@ -18,8 +18,6 @@
package com.android.systemui.keyguard.ui.viewmodel
-import android.content.pm.UserInfo
-import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -27,21 +25,20 @@ import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
-import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
+import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.setCommunalAvailable
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
-import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
-import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
import com.android.systemui.testKosmos
-import com.android.systemui.user.data.repository.fakeUserRepository
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -85,35 +82,21 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
@EnableFlags(FLAG_COMMUNAL_HUB)
@Test
- fun leftTransitionSceneKey_communalIsEnabled_communal() =
+ fun leftTransitionSceneKey_communalIsAvailable_communal() =
testScope.runTest {
- with(kosmos.fakeUserRepository) {
- setUserInfos(listOf(PRIMARY_USER))
- setSelectedUserInfo(PRIMARY_USER)
- }
- kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
- val leftDestinationSceneKey by collectLastValue(underTest.leftDestinationSceneKey)
- assertThat(leftDestinationSceneKey).isEqualTo(SceneKey.Communal)
- }
-
- @DisableFlags(FLAG_COMMUNAL_HUB)
- @Test
- fun leftTransitionSceneKey_communalIsDisabled_null() =
- testScope.runTest {
- with(kosmos.fakeUserRepository) {
- setUserInfos(listOf(PRIMARY_USER))
- setSelectedUserInfo(PRIMARY_USER)
- }
- kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
val leftDestinationSceneKey by collectLastValue(underTest.leftDestinationSceneKey)
assertThat(leftDestinationSceneKey).isNull()
+
+ kosmos.setCommunalAvailable(true)
+ runCurrent()
+ assertThat(leftDestinationSceneKey).isEqualTo(SceneKey.Communal)
}
private fun createLockscreenSceneViewModel(): LockscreenSceneViewModel {
return LockscreenSceneViewModel(
applicationScope = testScope.backgroundScope,
deviceEntryInteractor = kosmos.deviceEntryInteractor,
- communalSettingsInteractor = kosmos.communalSettingsInteractor,
+ communalInteractor = kosmos.communalInteractor,
longPress =
KeyguardLongPressViewModel(
interactor = mock(),
@@ -121,9 +104,4 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
notifications = kosmos.notificationsPlaceholderViewModel,
)
}
-
- private companion object {
- val PRIMARY_USER =
- UserInfo(/* id= */ 0, /* name= */ "primary user", /* flags= */ UserInfo.FLAG_MAIN)
- }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModelTest.kt
new file mode 100644
index 000000000000..241d0b818193
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModelTest.kt
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2024 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.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+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.kosmos.testScope
+import com.android.systemui.res.R
+import com.android.systemui.testKosmos
+import com.google.common.collect.Range
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class LockscreenToGlanceableHubTransitionViewModelTest : SysuiTestCase() {
+ val kosmos = testKosmos()
+ val testScope = kosmos.testScope
+
+ val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+ val configurationRepository = kosmos.fakeConfigurationRepository
+ val underTest by lazy { kosmos.lockscreenToGlanceableHubTransitionViewModel }
+
+ @Test
+ fun lockscreenFadeOut() =
+ testScope.runTest {
+ val values by collectValues(underTest.keyguardAlpha)
+ assertThat(values).containsExactly(1f)
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ listOf(
+ // Should start running here
+ step(0f, TransitionState.STARTED),
+ step(0.1f),
+ step(0.2f),
+ // ...up to here
+ step(0.3f),
+ step(0.4f),
+ step(0.5f),
+ step(0.6f),
+ step(0.7f),
+ step(0.8f),
+ // ...up to here
+ step(1f),
+ ),
+ testScope,
+ )
+
+ assertThat(values).hasSize(4)
+ values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
+ }
+
+ @Test
+ fun lockscreenTranslationX() =
+ testScope.runTest {
+ configurationRepository.setDimensionPixelSize(
+ R.dimen.lockscreen_to_hub_transition_lockscreen_translation_x,
+ -100
+ )
+ val values by collectValues(underTest.keyguardTranslationX)
+ assertThat(values).isEmpty()
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ listOf(
+ step(0f, TransitionState.STARTED),
+ step(0f),
+ step(0.3f),
+ step(0.5f),
+ step(1f),
+ ),
+ testScope,
+ )
+
+ assertThat(values).hasSize(5)
+ values.forEach { assertThat(it.value).isIn(Range.closed(-100f, 0f)) }
+ }
+
+ private fun step(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GLANCEABLE_HUB,
+ value = value,
+ transitionState = state,
+ ownerName = this::class.java.simpleName
+ )
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
index 15cf83c50de3..47e1ee9c1b71 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
@@ -119,15 +119,19 @@ class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() {
fun lockscreenAlpha_runDimissFromKeyguard() =
testScope.runTest {
val values by collectValues(underTest.lockscreenAlpha)
- runCurrent()
-
sysuiStatusBarStateController.setLeaveOpenOnKeyguardHide(true)
+ runCurrent()
- keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED))
- keyguardTransitionRepository.sendTransitionStep(step(1f))
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = KeyguardState.GONE,
+ testScope,
+ )
- assertThat(values.size).isEqualTo(2)
- values.forEach { assertThat(it).isEqualTo(1f) }
+ assertThat(values[0]).isEqualTo(1f)
+ assertThat(values[1]).isEqualTo(1f)
+ // Ensure FINISHED sets alpha to 0
+ assertThat(values[2]).isEqualTo(0f)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/MinimumTilesResourceRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/MinimumTilesResourceRepositoryTest.kt
new file mode 100644
index 000000000000..62c91633ea81
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/MinimumTilesResourceRepositoryTest.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 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.qs.pipeline.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class MinimumTilesResourceRepositoryTest : SysuiTestCase() {
+
+ val testableResources = context.orCreateTestableResources
+
+ @Test
+ fun minimumQSTiles_followsConfig() {
+ val minTwo = 2
+ testableResources.addOverride(R.integer.quick_settings_min_num_tiles, minTwo)
+ val underTest = MinimumTilesResourceRepository(context.resources)
+ assertThat(underTest.minNumberOfTiles).isEqualTo(minTwo)
+
+ val minSix = 6
+ testableResources.addOverride(R.integer.quick_settings_min_num_tiles, minSix)
+ val otherUnderTest = MinimumTilesResourceRepository(context.resources)
+ assertThat(otherUnderTest.minNumberOfTiles).isEqualTo(minSix)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt
index ff8a9bd019fb..39851b6f21a7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt
@@ -8,6 +8,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.FakeBroadcastDispatcher
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import com.android.systemui.statusbar.policy.FakeDeviceProvisionedController
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
@@ -28,6 +29,7 @@ class QSSettingsRestoredBroadcastRepositoryTest : SysuiTestCase() {
private val testScope = TestScope(dispatcher)
@Mock private lateinit var pipelineLogger: QSPipelineLogger
+ private val deviceProvisionedController = FakeDeviceProvisionedController()
private lateinit var underTest: QSSettingsRestoredBroadcastRepository
@@ -38,6 +40,7 @@ class QSSettingsRestoredBroadcastRepositoryTest : SysuiTestCase() {
underTest =
QSSettingsRestoredBroadcastRepository(
fakeBroadcastDispatcher,
+ deviceProvisionedController,
pipelineLogger,
testScope.backgroundScope,
dispatcher,
@@ -176,6 +179,100 @@ class QSSettingsRestoredBroadcastRepositoryTest : SysuiTestCase() {
}
}
+ @Test
+ fun restoreAfterUserSetup_singleTilesRestoredBroadcast() =
+ testScope.runTest {
+ runCurrent()
+ val restoreData by collectLastValue(underTest.restoreData)
+ val user = 0
+
+ val tilesIntent =
+ createRestoreIntent(
+ RestoreType.TILES,
+ CURRENT_TILES,
+ RESTORED_TILES,
+ )
+
+ sendIntentForUser(tilesIntent, user)
+
+ deviceProvisionedController.setUserSetup(user)
+
+ with(restoreData!!) {
+ assertThat(restoredTiles).isEqualTo(RESTORED_TILES.toTilesList())
+ assertThat(restoredAutoAddedTiles).isEmpty()
+ assertThat(userId).isEqualTo(user)
+ }
+ }
+
+ @Test
+ fun restoreAfterUserSetup_singleAutoAddRestoredBroadcast_noRestore() =
+ testScope.runTest {
+ runCurrent()
+ val restoreData by collectLastValue(underTest.restoreData)
+ val user = 0
+
+ val autoAddIntent =
+ createRestoreIntent(
+ RestoreType.AUTOADD,
+ CURRENT_AUTO_ADDED_TILES,
+ RESTORED_AUTO_ADDED_TILES,
+ )
+
+ sendIntentForUser(autoAddIntent, user)
+
+ deviceProvisionedController.setUserSetup(user)
+
+ assertThat(restoreData).isNull()
+ }
+
+ @Test
+ fun restoreAfterUserSetup_otherUserFinishedSetup_noRestore() =
+ testScope.runTest {
+ runCurrent()
+ val restoreData by collectLastValue(underTest.restoreData)
+ val user = 0
+
+ val tilesIntent =
+ createRestoreIntent(
+ RestoreType.TILES,
+ CURRENT_TILES,
+ RESTORED_TILES,
+ )
+
+ sendIntentForUser(tilesIntent, user)
+
+ deviceProvisionedController.setUserSetup(user + 1)
+
+ assertThat(restoreData).isNull()
+ }
+
+ @Test
+ fun restoreAfterUserSetup_otherUserFinishedSetup_thenCorrectUser_restored() =
+ testScope.runTest {
+ runCurrent()
+ val restoreData by collectLastValue(underTest.restoreData)
+ val user = 0
+
+ val tilesIntent =
+ createRestoreIntent(
+ RestoreType.TILES,
+ CURRENT_TILES,
+ RESTORED_TILES,
+ )
+
+ sendIntentForUser(tilesIntent, user)
+
+ deviceProvisionedController.setUserSetup(user + 1)
+ runCurrent()
+ deviceProvisionedController.setUserSetup(user)
+
+ with(restoreData!!) {
+ assertThat(restoredTiles).isEqualTo(RESTORED_TILES.toTilesList())
+ assertThat(restoredAutoAddedTiles).isEmpty()
+ assertThat(userId).isEqualTo(user)
+ }
+ }
+
private fun sendIntentForUser(intent: Intent, userId: Int) {
fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
context,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
index 3418977c3211..37d472169ae5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
@@ -20,11 +20,11 @@ import android.platform.test.annotations.EnabledOnRavenwood
import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import com.android.systemui.res.R
import com.android.systemui.retail.data.repository.FakeRetailModeRepository
import com.android.systemui.util.settings.FakeSettings
import com.google.common.truth.Truth.assertThat
@@ -187,6 +187,22 @@ class TileSpecSettingsRepositoryTest : SysuiTestCase() {
assertThat(loadTilesForUser(0)).isEqualTo(DEFAULT_TILES)
}
+ @Test
+ fun prependDefault() =
+ testScope.runTest {
+ val tiles by collectLastValue(underTest.tilesSpecs(0))
+
+ val startingTiles = listOf(TileSpec.create("e"), TileSpec.create("f"))
+
+ underTest.setTiles(0, startingTiles)
+ runCurrent()
+
+ underTest.prependDefault(0)
+
+ assertThat(tiles!!)
+ .containsExactlyElementsIn(DEFAULT_TILES.toTileSpecs() + startingTiles)
+ }
+
private fun TestScope.storeTilesForUser(specs: String, forUser: Int) {
secureSettings.putStringForUser(SETTING, specs, forUser)
runCurrent()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
index c7e7845f206c..bf34d6ee4435 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddableTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs.pipeline.domain.autoaddable
import android.content.pm.UserInfo
+import android.content.pm.UserInfo.FLAG_DISABLED
import android.content.pm.UserInfo.FLAG_FULL
import android.content.pm.UserInfo.FLAG_MANAGED_PROFILE
import android.content.pm.UserInfo.FLAG_PRIMARY
@@ -73,14 +74,14 @@ class WorkTileAutoAddableTest : SysuiTestCase() {
fun changeInProfiles_hasManagedProfile_sendsAddSignal() = runTest {
val signal by collectLastValue(underTest.autoAddSignal(0))
- userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK), selectedUserIndex = 0)
+ userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK_ENABLED), selectedUserIndex = 0)
assertThat(signal).isEqualTo(AutoAddSignal.Add(SPEC))
}
@Test
fun changeInProfiles_noManagedProfile_sendsRemoveSignal() = runTest {
- userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK), selectedUserIndex = 0)
+ userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK_ENABLED), selectedUserIndex = 0)
val signal by collectLastValue(underTest.autoAddSignal(0))
@@ -90,8 +91,17 @@ class WorkTileAutoAddableTest : SysuiTestCase() {
}
@Test
+ fun changeInProfile_hasDisabledManagedProfile_noAddSignal() = runTest {
+ val signal by collectLastValue(underTest.autoAddSignal(0))
+
+ userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK_DISABLED), selectedUserIndex = 0)
+
+ assertThat(signal).isNotInstanceOf(AutoAddSignal.Add::class.java)
+ }
+
+ @Test
fun startingWithManagedProfile_sendsAddSignal() = runTest {
- userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK), selectedUserIndex = 0)
+ userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK_ENABLED), selectedUserIndex = 0)
val signal by collectLastValue(underTest.autoAddSignal(0))
@@ -102,14 +112,14 @@ class WorkTileAutoAddableTest : SysuiTestCase() {
fun userChangeToUserWithProfile_noSignalForOriginalUser() = runTest {
val signal by collectLastValue(underTest.autoAddSignal(0))
- userTracker.set(listOf(USER_INFO_1, USER_INFO_WORK), selectedUserIndex = 0)
+ userTracker.set(listOf(USER_INFO_1, USER_INFO_WORK_ENABLED), selectedUserIndex = 0)
assertThat(signal).isNotEqualTo(AutoAddSignal.Add(SPEC))
}
@Test
fun userChangeToUserWithoutProfile_noSignalForOriginalUser() = runTest {
- userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK), selectedUserIndex = 0)
+ userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK_ENABLED), selectedUserIndex = 0)
val signal by collectLastValue(underTest.autoAddSignal(0))
userTracker.set(listOf(USER_INFO_1), selectedUserIndex = 0)
@@ -137,7 +147,7 @@ class WorkTileAutoAddableTest : SysuiTestCase() {
@Test
fun restoreDataWithWorkTile_currentlyManagedProfile_doesntTriggerRemove() = runTest {
- userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK), selectedUserIndex = 0)
+ userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK_ENABLED), selectedUserIndex = 0)
val userId = 0
val signals by collectValues(underTest.autoAddSignal(userId))
runCurrent()
@@ -164,7 +174,7 @@ class WorkTileAutoAddableTest : SysuiTestCase() {
@Test
fun restoreDataWithoutWorkTile_managedProfile_doesntTriggerRemove() = runTest {
- userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK), selectedUserIndex = 0)
+ userTracker.set(listOf(USER_INFO_0, USER_INFO_WORK_ENABLED), selectedUserIndex = 0)
val userId = 0
val signals by collectValues(underTest.autoAddSignal(userId))
runCurrent()
@@ -180,7 +190,9 @@ class WorkTileAutoAddableTest : SysuiTestCase() {
private val SPEC = TileSpec.create(WorkModeTile.TILE_SPEC)
private val USER_INFO_0 = UserInfo(0, "", FLAG_PRIMARY or FLAG_FULL)
private val USER_INFO_1 = UserInfo(1, "", FLAG_FULL)
- private val USER_INFO_WORK = UserInfo(10, "", FLAG_PROFILE or FLAG_MANAGED_PROFILE)
+ private val USER_INFO_WORK_DISABLED =
+ UserInfo(10, "", FLAG_PROFILE or FLAG_MANAGED_PROFILE or FLAG_DISABLED)
+ private val USER_INFO_WORK_ENABLED = UserInfo(10, "", FLAG_PROFILE or FLAG_MANAGED_PROFILE)
private fun createRestoreWithWorkTile(userId: Int): RestoreData {
return RestoreData(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt
index 2ea12ef4c88f..8ae917264a37 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractorTest.kt
@@ -26,6 +26,7 @@ import com.android.systemui.qs.pipeline.data.repository.FakeAutoAddRepository
import com.android.systemui.qs.pipeline.domain.autoaddable.FakeAutoAddable
import com.android.systemui.qs.pipeline.domain.model.AutoAddTracking
import com.android.systemui.qs.pipeline.domain.model.AutoAddable
+import com.android.systemui.qs.pipeline.domain.model.TileModel
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
import com.android.systemui.util.mockito.any
@@ -65,6 +66,7 @@ class AutoAddInteractorTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this)
whenever(currentTilesInteractor.userId).thenReturn(MutableStateFlow(USER))
+ whenever(currentTilesInteractor.currentTiles).thenReturn(MutableStateFlow(emptyList()))
}
@Test
@@ -201,6 +203,45 @@ class AutoAddInteractorTest : SysuiTestCase() {
assertThat(autoAddedTiles).doesNotContain(SPEC)
}
+ @Test
+ fun autoAddable_trackIfNotAdded_currentTile_markedAsAdded() =
+ testScope.runTest {
+ val fakeTile = FakeQSTile(USER).apply { tileSpec = SPEC.spec }
+ val fakeCurrentTileModel = TileModel(SPEC, fakeTile)
+ whenever(currentTilesInteractor.currentTiles)
+ .thenReturn(MutableStateFlow(listOf(fakeCurrentTileModel)))
+
+ val autoAddedTiles by collectLastValue(autoAddRepository.autoAddedTiles(USER))
+ val fakeAutoAddable = FakeAutoAddable(SPEC, AutoAddTracking.IfNotAdded(SPEC))
+
+ underTest = createInteractor(setOf(fakeAutoAddable))
+ runCurrent()
+
+ assertThat(autoAddedTiles).contains(SPEC)
+ }
+
+ @Test
+ fun autoAddable_trackIfNotAdded_tileAddedToCurrentTiles_markedAsAdded() =
+ testScope.runTest {
+ val fakeTile = FakeQSTile(USER).apply { tileSpec = SPEC.spec }
+ val fakeCurrentTileModel = TileModel(SPEC, fakeTile)
+ val currentTilesFlow = MutableStateFlow(emptyList<TileModel>())
+
+ whenever(currentTilesInteractor.currentTiles).thenReturn(currentTilesFlow)
+
+ val autoAddedTiles by collectLastValue(autoAddRepository.autoAddedTiles(USER))
+ val fakeAutoAddable = FakeAutoAddable(SPEC, AutoAddTracking.IfNotAdded(SPEC))
+
+ underTest = createInteractor(setOf(fakeAutoAddable))
+ runCurrent()
+
+ assertThat(autoAddedTiles).doesNotContain(SPEC)
+
+ currentTilesFlow.value = listOf(fakeCurrentTileModel)
+
+ assertThat(autoAddedTiles).contains(SPEC)
+ }
+
private fun createInteractor(autoAddables: Set<AutoAddable>): AutoAddInteractor {
return AutoAddInteractor(
autoAddables,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
index 1e2784a622b1..634c5fa74295 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
@@ -40,6 +40,7 @@ import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepositor
import com.android.systemui.qs.pipeline.data.repository.FakeCustomTileAddedRepository
import com.android.systemui.qs.pipeline.data.repository.FakeInstalledTilesComponentRepository
import com.android.systemui.qs.pipeline.data.repository.FakeTileSpecRepository
+import com.android.systemui.qs.pipeline.data.repository.MinimumTilesFixedRepository
import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository
import com.android.systemui.qs.pipeline.domain.model.TileModel
import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository
@@ -82,6 +83,7 @@ class CurrentTilesInteractorImplTest : SysuiTestCase() {
FakeCustomTileAddedRepository()
private val pipelineFlags = QSPipelineFlagsRepository()
private val tileLifecycleManagerFactory = TLMFactory()
+ private val minimumTilesRepository = MinimumTilesFixedRepository()
@Mock private lateinit var customTileStatePersister: CustomTileStatePersister
@@ -114,6 +116,7 @@ class CurrentTilesInteractorImplTest : SysuiTestCase() {
tileSpecRepository = tileSpecRepository,
installedTilesComponentRepository = installedTilesPackageRepository,
userRepository = userRepository,
+ minimumTilesRepository = minimumTilesRepository,
customTileStatePersister = customTileStatePersister,
tileFactory = tileFactory,
newQSTileFactory = { newQSTileFactory },
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt
new file mode 100644
index 000000000000..90c83047e72f
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2024 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.qs.pipeline.domain.interactor
+
+import android.content.ComponentName
+import android.content.pm.UserInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.FakeQSFactory
+import com.android.systemui.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.data.repository.FakeDefaultTilesRepository
+import com.android.systemui.qs.pipeline.data.repository.MinimumTilesFixedRepository
+import com.android.systemui.qs.pipeline.data.repository.fakeDefaultTilesRepository
+import com.android.systemui.qs.pipeline.data.repository.fakeMinimumTilesRepository
+import com.android.systemui.qs.pipeline.data.repository.fakeRestoreRepository
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.qsTileFactory
+import com.android.systemui.settings.fakeUserTracker
+import com.android.systemui.settings.userTracker
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * This integration test is for testing the solution to b/324575996. In particular, when restoring
+ * from a device that uses different specs for tiles, we may end up with empty (or mostly empty) QS.
+ * In that case, we want to prepend the default tiles instead.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class NoLowNumberOfTilesTest : SysuiTestCase() {
+
+ private val USER_0_INFO =
+ UserInfo(
+ 0,
+ "zero",
+ "",
+ UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL,
+ )
+
+ private val defaultTiles =
+ listOf(
+ TileSpec.create("internet"),
+ TileSpec.create("bt"),
+ )
+
+ private val kosmos =
+ Kosmos().apply {
+ fakeMinimumTilesRepository = MinimumTilesFixedRepository(minNumberOfTiles = 2)
+ fakeUserTracker.set(listOf(USER_0_INFO), 0)
+ qsTileFactory = FakeQSFactory(::tileCreator)
+ fakeDefaultTilesRepository = FakeDefaultTilesRepository(defaultTiles)
+ }
+
+ private val currentUser: Int
+ get() = kosmos.userTracker.userId
+
+ private val goodTile = TileSpec.create("correct")
+
+ private val restoredTiles =
+ listOf(
+ TileSpec.create("OEM:internet"),
+ TileSpec.create("OEM:bt"),
+ TileSpec.create("OEM:dnd"),
+ // This is not an installed component so a tile won't be created
+ TileSpec.create(ComponentName.unflattenFromString("oem/.tile")!!),
+ TileSpec.create("OEM:flashlight"),
+ goodTile,
+ )
+
+ @Before
+ fun setUp() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_QS_NEW_PIPELINE)
+
+ with(kosmos) {
+ restoreReconciliationInteractor.start()
+ autoAddInteractor.init(kosmos.currentTilesInteractor)
+ }
+ }
+
+ @Test
+ fun noLessThanTwoTilesAfterOEMRestore_prependedDefault() =
+ with(kosmos) {
+ testScope.runTest {
+ val tiles by collectLastValue(currentTilesInteractor.currentTiles)
+ runCurrent()
+
+ assertThat(tiles!!).isNotEmpty()
+
+ val restoreData = RestoreData(restoredTiles, emptySet(), currentUser)
+ fakeRestoreRepository.onDataRestored(restoreData)
+ runCurrent()
+
+ assertThat(tiles!!.map { it.spec }).isEqualTo(defaultTiles + listOf(goodTile))
+ }
+ }
+
+ @Test
+ fun noEmptyTilesAfterSettingTilesToUnknownNames() =
+ with(kosmos) {
+ testScope.runTest {
+ val tiles by collectLastValue(currentTilesInteractor.currentTiles)
+ runCurrent()
+
+ assertThat(tiles!!).isNotEmpty()
+
+ val badTiles = listOf(TileSpec.create("OEM:unknown_tile"))
+ currentTilesInteractor.setTiles(badTiles)
+ runCurrent()
+
+ assertThat(tiles!!.map { it.spec }).isEqualTo(defaultTiles)
+ }
+ }
+
+ private fun tileCreator(spec: String): QSTile? {
+ return if (spec.contains("OEM")) {
+ null // We don't know how to create OEM spec tiles
+ } else {
+ FakeQSTile(currentUser)
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractorTest.kt
new file mode 100644
index 000000000000..a5c554406848
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractorTest.kt
@@ -0,0 +1,241 @@
+/*
+ * 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.qs.tiles.impl.custom.domain.interactor
+
+import android.content.ComponentName
+import android.content.pm.UserInfo
+import android.graphics.drawable.Icon
+import android.service.quicksettings.Tile
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.external.componentName
+import com.android.systemui.qs.external.iQSTileService
+import com.android.systemui.qs.external.tileServiceManagerFacade
+import com.android.systemui.qs.external.tileServicesFacade
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.impl.custom.TileSubject.Companion.assertThat
+import com.android.systemui.qs.tiles.impl.custom.customTileDefaultsRepository
+import com.android.systemui.qs.tiles.impl.custom.customTileInteractor
+import com.android.systemui.qs.tiles.impl.custom.customTilePackagesUpdatesRepository
+import com.android.systemui.qs.tiles.impl.custom.customTileRepository
+import com.android.systemui.qs.tiles.impl.custom.customTileServiceInteractor
+import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import com.android.systemui.qs.tiles.impl.custom.tileSpec
+import com.android.systemui.testKosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.android.systemui.user.data.repository.userRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class)
+class CustomTileDataInteractorTest : SysuiTestCase() {
+
+ private val kosmos =
+ testKosmos().apply {
+ componentName = TEST_COMPONENT
+ tileSpec = TileSpec.create(componentName)
+ }
+ private val underTest =
+ with(kosmos) {
+ CustomTileDataInteractor(
+ tileSpec = tileSpec,
+ defaultsRepository = customTileDefaultsRepository,
+ serviceInteractor = customTileServiceInteractor,
+ customTileInteractor = customTileInteractor,
+ packageUpdatesRepository = customTilePackagesUpdatesRepository,
+ userRepository = userRepository,
+ tileScope = testScope.backgroundScope,
+ )
+ }
+
+ private suspend fun setup() {
+ with(kosmos) {
+ fakeUserRepository.setUserInfos(listOf(TEST_USER_1))
+ fakeUserRepository.setSelectedUserInfo(TEST_USER_1)
+ }
+ }
+
+ @Test
+ fun activeTileIsNotBoundUntilDataCollected() =
+ with(kosmos) {
+ testScope.runTest {
+ setup()
+ customTileRepository.setTileActive(true)
+
+ runCurrent()
+
+ assertThat(iQSTileService.isTileListening).isFalse()
+ assertThat(tileServiceManagerFacade.isBound).isFalse()
+ }
+ }
+
+ @Test
+ fun notActiveTileIsNotBoundUntilDataCollected() =
+ with(kosmos) {
+ testScope.runTest {
+ setup()
+ customTileRepository.setTileActive(false)
+
+ runCurrent()
+
+ assertThat(iQSTileService.isTileListening).isFalse()
+ assertThat(tileServiceManagerFacade.isBound).isFalse()
+ }
+ }
+
+ @Test
+ fun tileIsUnboundWhenDataIsNotListened() =
+ with(kosmos) {
+ testScope.runTest {
+ setup()
+ customTileRepository.setTileActive(false)
+ customTileDefaultsRepository.putDefaults(
+ TEST_USER_1.userHandle,
+ componentName,
+ CustomTileDefaults.Result(TEST_TILE.icon, TEST_TILE.label),
+ )
+ val dataJob =
+ underTest
+ .tileData(TEST_USER_1.userHandle, flowOf(DataUpdateTrigger.InitialRequest))
+ .launchIn(backgroundScope)
+ runCurrent()
+ tileServiceManagerFacade.processPendingBind()
+ assertThat(iQSTileService.isTileListening).isTrue()
+ assertThat(tileServiceManagerFacade.isBound).isTrue()
+
+ dataJob.cancel()
+ runCurrent()
+
+ assertThat(iQSTileService.isTileListening).isFalse()
+ assertThat(tileServiceManagerFacade.isBound).isFalse()
+ }
+ }
+
+ @Test
+ fun tileDataCollection() =
+ with(kosmos) {
+ testScope.runTest {
+ setup()
+ customTileDefaultsRepository.putDefaults(
+ TEST_USER_1.userHandle,
+ componentName,
+ CustomTileDefaults.Result(TEST_TILE.icon, TEST_TILE.label),
+ )
+ val tileData by
+ collectLastValue(
+ underTest.tileData(
+ TEST_USER_1.userHandle,
+ flowOf(DataUpdateTrigger.InitialRequest)
+ )
+ )
+ runCurrent()
+ tileServicesFacade.customTileInterface!!.updateTileState(TEST_TILE, 1)
+
+ runCurrent()
+
+ with(tileData!!) {
+ assertThat(user.identifier).isEqualTo(TEST_USER_1.id)
+ assertThat(componentName).isEqualTo(componentName)
+ assertThat(tile).isEqualTo(TEST_TILE)
+ assertThat(callingAppUid).isEqualTo(1)
+ assertThat(hasPendingBind).isEqualTo(true)
+ assertThat(isToggleable).isEqualTo(false)
+ assertThat(defaultTileIcon).isEqualTo(TEST_TILE.icon)
+ assertThat(defaultTileLabel).isEqualTo(TEST_TILE.label)
+ }
+ }
+ }
+
+ @Test
+ fun tileAvailableWhenDefaultsAreLoaded() =
+ with(kosmos) {
+ testScope.runTest {
+ setup()
+ customTileDefaultsRepository.putDefaults(
+ TEST_USER_1.userHandle,
+ tileSpec.componentName,
+ CustomTileDefaults.Result(TEST_TILE.icon, TEST_TILE.label),
+ )
+
+ val isAvailable by collectValues(underTest.availability(TEST_USER_1.userHandle))
+ runCurrent()
+
+ assertThat(isAvailable).containsExactlyElementsIn(arrayOf(true)).inOrder()
+ }
+ }
+
+ @Test
+ fun tileUnavailableWhenDefaultsAreNotLoaded() =
+ with(kosmos) {
+ testScope.runTest {
+ setup()
+ customTileDefaultsRepository.putDefaults(
+ TEST_USER_1.userHandle,
+ tileSpec.componentName,
+ CustomTileDefaults.Error,
+ )
+
+ val isAvailable by collectValues(underTest.availability(TEST_USER_1.userHandle))
+ runCurrent()
+
+ assertThat(isAvailable).containsExactlyElementsIn(arrayOf(false)).inOrder()
+ }
+ }
+
+ @Test
+ fun tileAvailabilityUndefinedWhenDefaultsAreLoadedForAnotherUser() =
+ with(kosmos) {
+ testScope.runTest {
+ setup()
+ customTileDefaultsRepository.putDefaults(
+ TEST_USER_2.userHandle,
+ tileSpec.componentName,
+ CustomTileDefaults.Error,
+ )
+
+ val isAvailable by collectValues(underTest.availability(TEST_USER_1.userHandle))
+ runCurrent()
+
+ assertThat(isAvailable).containsExactlyElementsIn(arrayOf()).inOrder()
+ }
+ }
+
+ private companion object {
+
+ val TEST_COMPONENT = ComponentName("test.pkg", "test.cls")
+ val TEST_USER_1 = UserInfo(1, "first user", UserInfo.FLAG_MAIN)
+ val TEST_USER_2 = UserInfo(2, "second user", UserInfo.FLAG_MAIN)
+ val TEST_TILE =
+ Tile().apply {
+ label = "test_tile_1"
+ icon = Icon.createWithContentUri("file://test_1")
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt
index 995d6ac66137..9546a32e2a06 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt
@@ -25,7 +25,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.external.TileServiceKey
import com.android.systemui.qs.pipeline.shared.TileSpec
@@ -35,6 +34,7 @@ import com.android.systemui.qs.tiles.impl.custom.customTileRepository
import com.android.systemui.qs.tiles.impl.custom.customTileStatePersister
import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
import com.android.systemui.qs.tiles.impl.custom.tileSpec
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.first
@@ -50,16 +50,16 @@ import org.junit.runner.RunWith
@OptIn(ExperimentalCoroutinesApi::class)
class CustomTileInteractorTest : SysuiTestCase() {
- private val kosmos = Kosmos().apply { tileSpec = TileSpec.create(TEST_COMPONENT) }
+ private val kosmos = testKosmos().apply { tileSpec = TileSpec.create(TEST_COMPONENT) }
private val underTest: CustomTileInteractor =
with(kosmos) {
CustomTileInteractor(
- tileSpec,
- customTileDefaultsRepository,
- customTileRepository,
- testScope.backgroundScope,
- testScope.testScheduler,
+ tileSpec = tileSpec,
+ defaultsRepository = customTileDefaultsRepository,
+ customTileRepository = customTileRepository,
+ tileScope = testScope.backgroundScope,
+ backgroundContext = testScope.testScheduler,
)
}
@@ -69,14 +69,14 @@ class CustomTileInteractorTest : SysuiTestCase() {
testScope.runTest {
customTileRepository.setTileActive(true)
customTileStatePersister.persistState(
- TileServiceKey(TEST_COMPONENT, TEST_USER.identifier),
- TEST_TILE,
+ TileServiceKey(TEST_COMPONENT, TEST_USER_1.identifier),
+ TEST_TILE_1,
)
- underTest.initForUser(TEST_USER)
+ underTest.initForUser(TEST_USER_1)
- assertThat(underTest.getTile(TEST_USER)).isEqualTo(TEST_TILE)
- assertThat(underTest.getTiles(TEST_USER).first()).isEqualTo(TEST_TILE)
+ assertThat(underTest.getTile(TEST_USER_1)).isEqualTo(TEST_TILE_1)
+ assertThat(underTest.getTiles(TEST_USER_1).first()).isEqualTo(TEST_TILE_1)
}
}
@@ -86,18 +86,18 @@ class CustomTileInteractorTest : SysuiTestCase() {
testScope.runTest {
customTileRepository.setTileActive(false)
customTileStatePersister.persistState(
- TileServiceKey(TEST_COMPONENT, TEST_USER.identifier),
- TEST_TILE,
+ TileServiceKey(TEST_COMPONENT, TEST_USER_1.identifier),
+ TEST_TILE_1,
)
- val tiles = collectValues(underTest.getTiles(TEST_USER))
- val initJob = launch { underTest.initForUser(TEST_USER) }
+ val tiles = collectValues(underTest.getTiles(TEST_USER_1))
+ val initJob = launch { underTest.initForUser(TEST_USER_1) }
- underTest.updateTile(TEST_TILE)
+ underTest.updateTile(TEST_TILE_1)
runCurrent()
initJob.join()
assertThat(tiles()).hasSize(1)
- assertThat(tiles().last()).isEqualTo(TEST_TILE)
+ assertThat(tiles().last()).isEqualTo(TEST_TILE_1)
}
}
@@ -107,34 +107,34 @@ class CustomTileInteractorTest : SysuiTestCase() {
testScope.runTest {
customTileRepository.setTileActive(false)
customTileStatePersister.persistState(
- TileServiceKey(TEST_COMPONENT, TEST_USER.identifier),
- TEST_TILE,
+ TileServiceKey(TEST_COMPONENT, TEST_USER_1.identifier),
+ TEST_TILE_1,
)
- val tiles = collectValues(underTest.getTiles(TEST_USER))
- val initJob = launch { underTest.initForUser(TEST_USER) }
+ val tiles = collectValues(underTest.getTiles(TEST_USER_1))
+ val initJob = launch { underTest.initForUser(TEST_USER_1) }
- customTileDefaultsRepository.putDefaults(TEST_USER, TEST_COMPONENT, TEST_DEFAULTS)
- customTileDefaultsRepository.requestNewDefaults(TEST_USER, TEST_COMPONENT)
+ customTileDefaultsRepository.putDefaults(TEST_USER_1, TEST_COMPONENT, TEST_DEFAULTS)
+ customTileDefaultsRepository.requestNewDefaults(TEST_USER_1, TEST_COMPONENT)
runCurrent()
initJob.join()
assertThat(tiles()).hasSize(1)
- assertThat(tiles().last()).isEqualTo(TEST_TILE)
+ assertThat(tiles().last()).isEqualTo(TEST_TILE_1)
}
}
@Test(expected = IllegalStateException::class)
fun getTileBeforeInitThrows() =
- with(kosmos) { testScope.runTest { underTest.getTile(TEST_USER) } }
+ with(kosmos) { testScope.runTest { underTest.getTile(TEST_USER_1) } }
@Test
fun initSuspendsForActiveTileNotRestoredAndNotUpdated() =
with(kosmos) {
testScope.runTest {
customTileRepository.setTileActive(true)
- val tiles = collectValues(underTest.getTiles(TEST_USER))
+ val tiles = collectValues(underTest.getTiles(TEST_USER_1))
- val initJob = backgroundScope.launch { underTest.initForUser(TEST_USER) }
+ val initJob = backgroundScope.launch { underTest.initForUser(TEST_USER_1) }
advanceTimeBy(1 * DateUtils.DAY_IN_MILLIS)
// Is still suspended
@@ -149,12 +149,12 @@ class CustomTileInteractorTest : SysuiTestCase() {
testScope.runTest {
customTileRepository.setTileActive(false)
customTileStatePersister.persistState(
- TileServiceKey(TEST_COMPONENT, TEST_USER.identifier),
- TEST_TILE,
+ TileServiceKey(TEST_COMPONENT, TEST_USER_1.identifier),
+ TEST_TILE_1,
)
- val tiles = collectValues(underTest.getTiles(TEST_USER))
+ val tiles = collectValues(underTest.getTiles(TEST_USER_1))
- val initJob = backgroundScope.launch { underTest.initForUser(TEST_USER) }
+ val initJob = backgroundScope.launch { underTest.initForUser(TEST_USER_1) }
advanceTimeBy(1 * DateUtils.DAY_IN_MILLIS)
// Is still suspended
@@ -176,18 +176,89 @@ class CustomTileInteractorTest : SysuiTestCase() {
}
}
+ @Test
+ fun activeFollowsTheRepository() {
+ with(kosmos) {
+ testScope.runTest {
+ customTileRepository.setTileActive(false)
+ assertThat(underTest.isTileActive()).isFalse()
+
+ customTileRepository.setTileActive(true)
+ assertThat(underTest.isTileActive()).isTrue()
+ }
+ }
+ }
+
+ @Test
+ fun initForTheSameUserProcessedOnce() =
+ with(kosmos) {
+ testScope.runTest {
+ customTileRepository.setTileActive(false)
+ customTileStatePersister.persistState(
+ TileServiceKey(TEST_COMPONENT, TEST_USER_1.identifier),
+ TEST_TILE_1,
+ )
+ val tiles = collectValues(underTest.getTiles(TEST_USER_1))
+ val initJob = launch {
+ underTest.initForUser(TEST_USER_1)
+ underTest.initForUser(TEST_USER_1)
+ }
+
+ underTest.updateTile(TEST_TILE_1)
+ runCurrent()
+ initJob.join()
+
+ assertThat(tiles()).hasSize(1)
+ assertThat(tiles().last()).isEqualTo(TEST_TILE_1)
+ }
+ }
+
+ @Test
+ fun initForDifferentUsersProcessedOnce() =
+ with(kosmos) {
+ testScope.runTest {
+ customTileRepository.setTileActive(true)
+ customTileStatePersister.persistState(
+ TileServiceKey(TEST_COMPONENT, TEST_USER_1.identifier),
+ TEST_TILE_1,
+ )
+ customTileStatePersister.persistState(
+ TileServiceKey(TEST_COMPONENT, TEST_USER_2.identifier),
+ TEST_TILE_2,
+ )
+ val tiles1 by collectValues(underTest.getTiles(TEST_USER_1))
+ val tiles2 by collectValues(underTest.getTiles(TEST_USER_2))
+
+ val initJob = launch {
+ underTest.initForUser(TEST_USER_1)
+ underTest.initForUser(TEST_USER_2)
+ }
+ runCurrent()
+ initJob.join()
+
+ assertThat(tiles1).isEmpty()
+ assertThat(tiles2).hasSize(1)
+ assertThat(tiles2.last()).isEqualTo(TEST_TILE_2)
+ }
+ }
+
private companion object {
val TEST_COMPONENT = ComponentName("test.pkg", "test.cls")
- val TEST_USER = UserHandle.of(1)!!
- val TEST_TILE by lazy {
+ val TEST_USER_1 = UserHandle.of(1)!!
+ val TEST_USER_2 = UserHandle.of(2)!!
+ val TEST_TILE_1 by lazy {
Tile().apply {
label = "test_tile_1"
icon = Icon.createWithContentUri("file://test_1")
}
}
- val TEST_DEFAULTS by lazy {
- CustomTileDefaults.Result(TEST_TILE.icon, TEST_TILE.label)
+ val TEST_TILE_2 by lazy {
+ Tile().apply {
+ label = "test_tile_2"
+ icon = Icon.createWithContentUri("file://test_2")
+ }
}
+ val TEST_DEFAULTS by lazy { CustomTileDefaults.Result(TEST_TILE_1.icon, TEST_TILE_1.label) }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt
new file mode 100644
index 000000000000..a2127a4717ce
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt
@@ -0,0 +1,265 @@
+/*
+ * 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.qs.tiles.impl.custom.domain.interactor
+
+import android.app.IUriGrantsManager
+import android.content.ComponentName
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.Icon
+import android.graphics.drawable.TestStubDrawable
+import android.os.UserHandle
+import android.service.quicksettings.Tile
+import android.widget.Button
+import android.widget.Switch
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject.Companion.assertThat
+import com.android.systemui.qs.tiles.impl.custom.customTileQsTileConfig
+import com.android.systemui.qs.tiles.impl.custom.domain.CustomTileMapper
+import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
+import com.android.systemui.qs.tiles.impl.custom.tileSpec
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CustomTileMapperTest : SysuiTestCase() {
+
+ private val uriGrantsManager: IUriGrantsManager = mock {}
+ private val kosmos = testKosmos().apply { tileSpec = TileSpec.Companion.create(TEST_COMPONENT) }
+ private val underTest by lazy {
+ CustomTileMapper(
+ context = mock { whenever(createContextAsUser(any(), any())).thenReturn(context) },
+ uriGrantsManager = uriGrantsManager,
+ )
+ }
+
+ @Test
+ fun stateHasPendingBinding() =
+ with(kosmos) {
+ testScope.runTest {
+ val actual =
+ underTest.map(
+ customTileQsTileConfig,
+ createModel(hasPendingBind = true),
+ )
+ val expected =
+ createTileState(
+ activationState = QSTileState.ActivationState.UNAVAILABLE,
+ actions = setOf(QSTileState.UserAction.LONG_CLICK),
+ )
+
+ assertThat(actual).isEqualTo(expected)
+ }
+ }
+
+ @Test
+ fun stateActive() =
+ with(kosmos) {
+ testScope.runTest {
+ val actual =
+ underTest.map(
+ customTileQsTileConfig,
+ createModel(tileState = Tile.STATE_ACTIVE),
+ )
+ val expected =
+ createTileState(
+ activationState = QSTileState.ActivationState.ACTIVE,
+ )
+
+ assertThat(actual).isEqualTo(expected)
+ }
+ }
+
+ @Test
+ fun stateInactive() =
+ with(kosmos) {
+ testScope.runTest {
+ val actual =
+ underTest.map(
+ customTileQsTileConfig,
+ createModel(tileState = Tile.STATE_INACTIVE),
+ )
+ val expected =
+ createTileState(
+ activationState = QSTileState.ActivationState.INACTIVE,
+ )
+
+ assertThat(actual).isEqualTo(expected)
+ }
+ }
+
+ @Test
+ fun stateUnavailable() =
+ with(kosmos) {
+ testScope.runTest {
+ val actual =
+ underTest.map(
+ customTileQsTileConfig,
+ createModel(tileState = Tile.STATE_UNAVAILABLE),
+ )
+ val expected =
+ createTileState(
+ activationState = QSTileState.ActivationState.UNAVAILABLE,
+ actions = setOf(QSTileState.UserAction.LONG_CLICK),
+ )
+
+ assertThat(actual).isEqualTo(expected)
+ }
+ }
+
+ @Test
+ fun tileWithChevron() =
+ with(kosmos) {
+ testScope.runTest {
+ val actual =
+ underTest.map(
+ customTileQsTileConfig,
+ createModel(isToggleable = false),
+ )
+ val expected =
+ createTileState(
+ sideIcon = QSTileState.SideViewIcon.Chevron,
+ a11yClass = Button::class.qualifiedName,
+ )
+
+ assertThat(actual).isEqualTo(expected)
+ }
+ }
+
+ @Test
+ fun defaultIconFallback() =
+ with(kosmos) {
+ testScope.runTest {
+ val actual =
+ underTest.map(
+ customTileQsTileConfig,
+ createModel(tileIcon = createIcon(RuntimeException(), false)),
+ )
+ val expected =
+ createTileState(
+ activationState = QSTileState.ActivationState.INACTIVE,
+ icon = DEFAULT_DRAWABLE,
+ )
+
+ assertThat(actual).isEqualTo(expected)
+ }
+ }
+
+ @Test
+ fun failedToLoadIconTileIsInactive() =
+ with(kosmos) {
+ testScope.runTest {
+ val actual =
+ underTest.map(
+ customTileQsTileConfig,
+ createModel(
+ tileIcon = createIcon(RuntimeException(), false),
+ defaultTileIcon = createIcon(null, true)
+ ),
+ )
+ val expected =
+ createTileState(
+ icon = null,
+ activationState = QSTileState.ActivationState.INACTIVE,
+ )
+
+ assertThat(actual).isEqualTo(expected)
+ }
+ }
+
+ private fun Kosmos.createModel(
+ tileState: Int = Tile.STATE_ACTIVE,
+ tileIcon: Icon = createIcon(DRAWABLE, false),
+ hasPendingBind: Boolean = false,
+ isToggleable: Boolean = true,
+ defaultTileIcon: Icon = createIcon(DEFAULT_DRAWABLE, true),
+ ) =
+ CustomTileDataModel(
+ UserHandle.of(1),
+ tileSpec.componentName,
+ Tile().apply {
+ state = tileState
+ label = "test label"
+ subtitle = "test subtitle"
+ icon = tileIcon
+ contentDescription = "test content description"
+ },
+ callingAppUid = 0,
+ hasPendingBind = hasPendingBind,
+ isToggleable = isToggleable,
+ defaultTileLabel = "test default tile label",
+ defaultTileIcon = defaultTileIcon,
+ )
+
+ private fun createIcon(drawable: Drawable?, isDefault: Boolean): Icon = mock {
+ if (isDefault) {
+ whenever(loadDrawable(any())).thenReturn(drawable)
+ } else {
+ whenever(loadDrawableCheckingUriGrant(any(), any(), any(), any())).thenReturn(drawable)
+ }
+ }
+
+ private fun createIcon(exception: RuntimeException, isDefault: Boolean): Icon = mock {
+ if (isDefault) {
+ whenever(loadDrawable(any())).thenThrow(exception)
+ } else {
+ whenever(loadDrawableCheckingUriGrant(any(), eq(uriGrantsManager), any(), any()))
+ .thenThrow(exception)
+ }
+ }
+
+ private fun createTileState(
+ activationState: QSTileState.ActivationState = QSTileState.ActivationState.ACTIVE,
+ icon: Drawable? = DRAWABLE,
+ sideIcon: QSTileState.SideViewIcon = QSTileState.SideViewIcon.None,
+ actions: Set<QSTileState.UserAction> =
+ setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK),
+ a11yClass: String? = Switch::class.qualifiedName,
+ ): QSTileState {
+ return QSTileState(
+ { icon?.let { com.android.systemui.common.shared.model.Icon.Loaded(icon, null) } },
+ "test label",
+ activationState,
+ "test subtitle",
+ actions,
+ "test content description",
+ null,
+ sideIcon,
+ QSTileState.EnabledState.ENABLED,
+ a11yClass,
+ )
+ }
+
+ private companion object {
+ val TEST_COMPONENT = ComponentName("test.pkg", "test.cls")
+
+ val DEFAULT_DRAWABLE = TestStubDrawable("default_icon_drawable")
+ val DRAWABLE = TestStubDrawable("icon_drawable")
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractorTest.kt
new file mode 100644
index 000000000000..c709f16c3213
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractorTest.kt
@@ -0,0 +1,283 @@
+/*
+ * 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.qs.tiles.impl.custom.domain.interactor
+
+import android.app.PendingIntent
+import android.content.ComponentName
+import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
+import android.content.pm.UserInfo
+import android.graphics.drawable.Icon
+import android.provider.Settings
+import android.service.quicksettings.Tile
+import android.service.quicksettings.TileService
+import android.view.IWindowManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.external.componentName
+import com.android.systemui.qs.external.iQSTileService
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.actions.intentInputs
+import com.android.systemui.qs.tiles.base.actions.pendingIntentInputs
+import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx.click
+import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx.longClick
+import com.android.systemui.qs.tiles.impl.custom.customTileServiceInteractor
+import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
+import com.android.systemui.qs.tiles.impl.custom.qsTileLogger
+import com.android.systemui.qs.tiles.impl.custom.tileSpec
+import com.android.systemui.testKosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.nullable
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CustomTileUserActionInteractorTest : SysuiTestCase() {
+
+ private val inputHandler = FakeQSTileIntentUserInputHandler()
+ private val packageManagerFacade = FakePackageManagerFacade()
+ private val windowManagerFacade = FakeWindowManagerFacade()
+ private val kosmos =
+ testKosmos().apply {
+ componentName = TEST_COMPONENT
+ tileSpec = TileSpec.create(componentName)
+ testCase = this@CustomTileUserActionInteractorTest
+ }
+
+ private val underTest =
+ with(kosmos) {
+ CustomTileUserActionInteractor(
+ context =
+ mock {
+ whenever(packageManager).thenReturn(packageManagerFacade.packageManager)
+ },
+ tileSpec = tileSpec,
+ qsTileLogger = qsTileLogger,
+ windowManager = windowManagerFacade.windowManager,
+ displayTracker = mock {},
+ qsTileIntentUserInputHandler = inputHandler,
+ backgroundContext = testDispatcher,
+ serviceInteractor = customTileServiceInteractor,
+ )
+ }
+
+ private suspend fun setup() {
+ with(kosmos) {
+ fakeUserRepository.setUserInfos(listOf(TEST_USER_1))
+ fakeUserRepository.setSelectedUserInfo(TEST_USER_1)
+ }
+ }
+
+ @Test
+ fun clickStartsActivityWhenPossible() =
+ with(kosmos) {
+ testScope.runTest {
+ setup()
+ underTest.handleInput(
+ click(customTileModel(activityLaunchForClick = pendingIntent()))
+ )
+
+ assertThat(windowManagerFacade.isTokenGranted).isTrue()
+ assertThat(inputHandler.pendingIntentInputs).hasSize(1)
+ assertThat(iQSTileService.clicks).hasSize(0)
+ }
+ }
+
+ @Test
+ fun clickPassedToTheServiceWhenNoActivity() =
+ with(kosmos) {
+ testScope.runTest {
+ setup()
+ packageManagerFacade.resolutionResult = null
+ underTest.handleInput(click(customTileModel(activityLaunchForClick = null)))
+
+ assertThat(windowManagerFacade.isTokenGranted).isTrue()
+ assertThat(inputHandler.pendingIntentInputs).hasSize(0)
+ assertThat(iQSTileService.clicks).hasSize(1)
+ }
+ }
+
+ @Test
+ fun longClickOpensResolvedIntent() =
+ with(kosmos) {
+ testScope.runTest {
+ setup()
+ packageManagerFacade.resolutionResult =
+ ActivityInfo().apply {
+ packageName = "resolved.pkg"
+ name = "Test"
+ }
+ underTest.handleInput(longClick(customTileModel()))
+
+ assertThat(inputHandler.intentInputs).hasSize(1)
+ with(inputHandler.intentInputs.first()) {
+ assertThat(intent.action).isEqualTo(TileService.ACTION_QS_TILE_PREFERENCES)
+ assertThat(intent.component).isEqualTo(ComponentName("resolved.pkg", "Test"))
+ assertThat(
+ intent.getParcelableExtra(
+ Intent.EXTRA_COMPONENT_NAME,
+ ComponentName::class.java
+ )
+ )
+ .isEqualTo(componentName)
+ assertThat(intent.getIntExtra(TileService.EXTRA_STATE, Int.MAX_VALUE))
+ .isEqualTo(111)
+ }
+ }
+ }
+
+ @Test
+ fun longClickOpensDefaultIntentWhenNoResolved() =
+ with(kosmos) {
+ testScope.runTest {
+ setup()
+ underTest.handleInput(longClick(customTileModel()))
+
+ assertThat(inputHandler.intentInputs).hasSize(1)
+ with(inputHandler.intentInputs.first()) {
+ assertThat(intent.action)
+ .isEqualTo(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
+ assertThat(intent.data.toString()).isEqualTo("package:test.pkg")
+ }
+ }
+ }
+
+ @Test
+ fun revokeTokenDoesntRevokeWhenShowingDialog() =
+ with(kosmos) {
+ testScope.runTest {
+ setup()
+ underTest.handleInput(click(customTileModel()))
+ underTest.setShowingDialog(true)
+
+ underTest.revokeToken(false)
+
+ assertThat(windowManagerFacade.isTokenGranted).isTrue()
+ }
+ }
+
+ @Test
+ fun forceRevokeTokenRevokesWhenShowingDialog() =
+ with(kosmos) {
+ testScope.runTest {
+ setup()
+ underTest.handleInput(click(customTileModel()))
+ underTest.setShowingDialog(true)
+
+ underTest.revokeToken(true)
+
+ assertThat(windowManagerFacade.isTokenGranted).isFalse()
+ }
+ }
+
+ @Test
+ fun revokeTokenRevokesWhenNotShowingDialog() =
+ with(kosmos) {
+ testScope.runTest {
+ setup()
+ underTest.handleInput(click(customTileModel()))
+ underTest.setShowingDialog(false)
+
+ underTest.revokeToken(false)
+
+ assertThat(windowManagerFacade.isTokenGranted).isFalse()
+ }
+ }
+
+ @Test
+ fun startActivityDoesntStartWithNoToken() =
+ with(kosmos) {
+ testScope.runTest {
+ setup()
+ underTest.startActivityAndCollapse(mock())
+
+ // Checking all types of inputs
+ assertThat(inputHandler.handledInputs).isEmpty()
+ }
+ }
+
+ private fun pendingIntent(): PendingIntent = mock { whenever(isActivity).thenReturn(true) }
+
+ private fun Kosmos.customTileModel(
+ componentName: ComponentName = tileSpec.componentName,
+ activityLaunchForClick: PendingIntent? = null,
+ tileState: Int = 111,
+ ) =
+ CustomTileDataModel(
+ TEST_USER_1.userHandle,
+ componentName,
+ Tile().also {
+ it.activityLaunchForClick = activityLaunchForClick
+ it.state = tileState
+ },
+ callingAppUid = 0,
+ hasPendingBind = false,
+ isToggleable = false,
+ defaultTileLabel = "default_label",
+ defaultTileIcon = Icon.createWithContentUri("default_icon"),
+ )
+
+ private class FakePackageManagerFacade(val packageManager: PackageManager = mock()) {
+
+ var resolutionResult: ActivityInfo? = null
+
+ init {
+ whenever(packageManager.resolveActivityAsUser(any(), any<Int>(), any())).then {
+ ResolveInfo().apply { activityInfo = resolutionResult }
+ }
+ }
+ }
+
+ private class FakeWindowManagerFacade(val windowManager: IWindowManager = mock()) {
+
+ var isTokenGranted: Boolean = false
+ private set
+
+ init {
+ with(windowManager) {
+ whenever(removeWindowToken(any(), any())).then {
+ isTokenGranted = false
+ Unit
+ }
+ whenever(addWindowToken(any(), any(), any(), nullable())).then {
+ isTokenGranted = true
+ Unit
+ }
+ }
+ }
+ }
+
+ private companion object {
+
+ val TEST_COMPONENT = ComponentName("test.pkg", "test.cls")
+ val TEST_USER_1 = UserInfo(1, "first user", UserInfo.FLAG_MAIN)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
index f8573cc280c4..3c0ab240cbba 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
@@ -259,6 +259,37 @@ class QSSceneAdapterImplTest : SysuiTestCase() {
}
@Test
+ fun state_unsquishing() =
+ testScope.runTest {
+ val qsImpl by collectLastValue(underTest.qsImpl)
+ val squishiness = 0.342f
+
+ underTest.inflate(context)
+ runCurrent()
+ clearInvocations(qsImpl!!)
+
+ underTest.setState(QSSceneAdapter.State.Unsquishing(squishiness))
+ with(qsImpl!!) {
+ verify(this).setQsVisible(true)
+ verify(this)
+ .setQsExpansion(
+ /* expansion= */ 0f,
+ /* panelExpansionFraction= */ 1f,
+ /* proposedTranslation= */ 0f,
+ /* squishinessFraction= */ squishiness,
+ )
+ verify(this).setListening(true)
+ verify(this).setExpanded(true)
+ verify(this)
+ .setTransitionToFullShadeProgress(
+ /* isTransitioningToFullShade= */ false,
+ /* qsTransitionFraction= */ 1f,
+ /* qsSquishinessFraction = */ squishiness,
+ )
+ }
+ }
+
+ @Test
fun customizing_QS() =
testScope.runTest {
val customizing by collectLastValue(underTest.isCustomizing)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterTest.kt
index d1bc686385a1..e281383e6250 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterTest.kt
@@ -29,6 +29,12 @@ import org.junit.runner.RunWith
@EnabledOnRavenwood
@RunWith(AndroidJUnit4::class)
class QSSceneAdapterTest : SysuiTestCase() {
+
+ @Test
+ fun expanding_squishiness1() {
+ assertThat(QSSceneAdapter.State.Expanding(0.3f).squishiness).isEqualTo(1f)
+ }
+
@Test
fun expandingSpecialValues() {
assertThat(QSSceneAdapter.State.QQS).isEqualTo(QSSceneAdapter.State.Expanding(0f))
@@ -41,4 +47,11 @@ class QSSceneAdapterTest : SysuiTestCase() {
assertThat(Collapsing(collapsingProgress))
.isEqualTo(QSSceneAdapter.State.Expanding(1 - collapsingProgress))
}
+
+ @Test
+ fun unsquishing_expansionSameAsQQS() {
+ val squishiness = 0.6f
+ assertThat(QSSceneAdapter.State.Unsquishing(squishiness).expansion)
+ .isEqualTo(QSSceneAdapter.State.QQS.expansion)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
index d47da3e47d2f..1eb9adb8c004 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
@@ -26,11 +26,12 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.FooterActionsController
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
-import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Direction
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.UserAction
import com.android.systemui.scene.shared.model.UserActionResult
+import com.android.systemui.shade.domain.interactor.privacyChipInteractor
+import com.android.systemui.shade.domain.interactor.shadeHeaderClockInteractor
import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
@@ -58,7 +59,6 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val sceneInteractor by lazy { kosmos.sceneInteractor }
private val mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
private val flags = FakeFeatureFlagsClassic().also { it.set(Flags.NEW_NETWORK_SLICE_UI, false) }
private val qsFlexiglassAdapter = FakeQSSceneAdapter({ mock() })
@@ -95,9 +95,10 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() {
ShadeHeaderViewModel(
applicationScope = testScope.backgroundScope,
context = context,
- sceneInteractor = sceneInteractor,
mobileIconsInteractor = mobileIconsInteractor,
mobileIconsViewModel = mobileIconsViewModel,
+ privacyChipInteractor = kosmos.privacyChipInteractor,
+ clockInteractor = kosmos.shadeHeaderClockInteractor,
broadcastDispatcher = fakeBroadcastDispatcher,
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 7c30c7eb7f50..4e72843922e1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -40,7 +40,7 @@ import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.bouncerViewModel
import com.android.systemui.classifier.domain.interactor.falsingInteractor
import com.android.systemui.classifier.falsingCollector
-import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
+import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
@@ -50,7 +50,7 @@ import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
import com.android.systemui.kosmos.testScope
-import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
import com.android.systemui.model.SysUiState
import com.android.systemui.model.sceneContainerPlugin
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
@@ -65,8 +65,11 @@ import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
import com.android.systemui.settings.FakeDisplayTracker
+import com.android.systemui.shade.domain.interactor.privacyChipInteractor
+import com.android.systemui.shade.domain.interactor.shadeHeaderClockInteractor
import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
import com.android.systemui.shade.ui.viewmodel.ShadeSceneViewModel
+import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
@@ -130,7 +133,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
private val sceneInteractor by lazy { kosmos.sceneInteractor }
private val authenticationInteractor by lazy { kosmos.authenticationInteractor }
private val deviceEntryInteractor by lazy { kosmos.deviceEntryInteractor }
- private val communalSettingsInteractor by lazy { kosmos.communalSettingsInteractor }
+ private val communalInteractor by lazy { kosmos.communalInteractor }
private val transitionState by lazy {
MutableStateFlow<ObservableTransitionState>(
@@ -141,6 +144,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
SceneContainerViewModel(
sceneInteractor = sceneInteractor,
falsingInteractor = kosmos.falsingInteractor,
+ powerInteractor = kosmos.powerInteractor,
)
.apply { setTransitionState(transitionState) }
}
@@ -155,7 +159,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
LockscreenSceneViewModel(
applicationScope = testScope.backgroundScope,
deviceEntryInteractor = deviceEntryInteractor,
- communalSettingsInteractor = communalSettingsInteractor,
+ communalInteractor = communalInteractor,
longPress =
KeyguardLongPressViewModel(
interactor = mock(),
@@ -229,9 +233,10 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
ShadeHeaderViewModel(
applicationScope = testScope.backgroundScope,
context = context,
- sceneInteractor = sceneInteractor,
mobileIconsInteractor = mobileIconsInteractor,
mobileIconsViewModel = mobileIconsViewModel,
+ privacyChipInteractor = kosmos.privacyChipInteractor,
+ clockInteractor = kosmos.shadeHeaderClockInteractor,
broadcastDispatcher = fakeBroadcastDispatcher,
)
@@ -267,6 +272,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
windowController = mock(),
deviceProvisioningInteractor = kosmos.deviceProvisioningInteractor,
centralSurfaces = mock(),
+ headsUpInteractor = kosmos.headsUpNotificationInteractor,
)
startable.start()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
index 4b9ebdc295c6..dd3eb6845789 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
@@ -22,7 +22,6 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.kosmos.testScope
-import com.android.systemui.power.data.repository.fakePowerRepository
import com.android.systemui.scene.data.repository.sceneContainerRepository
import com.android.systemui.scene.sceneContainerConfig
import com.android.systemui.scene.sceneKeys
@@ -276,12 +275,4 @@ class SceneInteractorTest : SysuiTestCase() {
underTest.setVisible(true, "reason")
assertThat(isVisible).isTrue()
}
-
- @Test
- fun userInput() =
- testScope.runTest {
- assertThat(kosmos.fakePowerRepository.userTouchRegistered).isFalse()
- underTest.onUserInput()
- assertThat(kosmos.fakePowerRepository.userTouchRegistered).isTrue()
- }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index ffea84b70d07..f49b4777cf14 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -49,6 +49,8 @@ import com.android.systemui.scene.shared.model.ObservableTransitionState
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.statusbar.NotificationShadeWindowController
+import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository
+import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
import com.android.systemui.statusbar.policy.data.repository.fakeDeviceProvisioningRepository
@@ -120,6 +122,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
windowController = windowController,
deviceProvisioningInteractor = kosmos.deviceProvisioningInteractor,
centralSurfaces = centralSurfaces,
+ headsUpInteractor = kosmos.headsUpNotificationInteractor,
)
}
@@ -168,6 +171,12 @@ class SceneContainerStartableTest : SysuiTestCase() {
fakeSceneDataSource.unpause(expectedScene = SceneKey.Gone)
transitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Gone)
assertThat(isVisible).isFalse()
+
+ kosmos.headsUpNotificationRepository.hasPinnedHeadsUp.value = true
+ assertThat(isVisible).isTrue()
+
+ kosmos.headsUpNotificationRepository.hasPinnedHeadsUp.value = false
+ assertThat(isVisible).isFalse()
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
index 6c78317f61e5..ffbdafe338e7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
@@ -22,16 +22,23 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.domain.interactor.falsingInteractor
+import com.android.systemui.classifier.fakeFalsingManager
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
+import com.android.systemui.power.data.repository.fakePowerRepository
+import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.sceneContainerConfig
import com.android.systemui.scene.sceneKeys
import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -45,6 +52,8 @@ class SceneContainerViewModelTest : SysuiTestCase() {
private val testScope by lazy { kosmos.testScope }
private val interactor by lazy { kosmos.sceneInteractor }
private val fakeSceneDataSource = kosmos.fakeSceneDataSource
+ private val sceneContainerConfig = kosmos.sceneContainerConfig
+ private val falsingManager = kosmos.fakeFalsingManager
private lateinit var underTest: SceneContainerViewModel
@@ -55,6 +64,7 @@ class SceneContainerViewModelTest : SysuiTestCase() {
SceneContainerViewModel(
sceneInteractor = interactor,
falsingInteractor = kosmos.falsingInteractor,
+ powerInteractor = kosmos.powerInteractor,
)
}
@@ -86,4 +96,107 @@ class SceneContainerViewModelTest : SysuiTestCase() {
assertThat(currentScene).isEqualTo(SceneKey.Shade)
}
+
+ @Test
+ fun canChangeScene_whenAllowed_switchingFromGone_returnsTrue() =
+ testScope.runTest {
+ val currentScene by collectLastValue(underTest.currentScene)
+ fakeSceneDataSource.changeScene(toScene = SceneKey.Gone)
+ runCurrent()
+ assertThat(currentScene).isEqualTo(SceneKey.Gone)
+
+ sceneContainerConfig.sceneKeys
+ .filter { it != currentScene }
+ .forEach { toScene ->
+ assertWithMessage("Scene $toScene incorrectly protected when allowed")
+ .that(underTest.canChangeScene(toScene = toScene))
+ .isTrue()
+ }
+ }
+
+ @Test
+ fun canChangeScene_whenAllowed_switchingFromLockscreen_returnsTrue() =
+ testScope.runTest {
+ val currentScene by collectLastValue(underTest.currentScene)
+ fakeSceneDataSource.changeScene(toScene = SceneKey.Lockscreen)
+ runCurrent()
+ assertThat(currentScene).isEqualTo(SceneKey.Lockscreen)
+
+ sceneContainerConfig.sceneKeys
+ .filter { it != currentScene }
+ .forEach { toScene ->
+ assertWithMessage("Scene $toScene incorrectly protected when allowed")
+ .that(underTest.canChangeScene(toScene = toScene))
+ .isTrue()
+ }
+ }
+
+ @Test
+ fun canChangeScene_whenNotAllowed_fromLockscreen_toFalsingProtectedScenes_returnsFalse() =
+ testScope.runTest {
+ falsingManager.setIsFalseTouch(true)
+ val currentScene by collectLastValue(underTest.currentScene)
+ fakeSceneDataSource.changeScene(toScene = SceneKey.Lockscreen)
+ runCurrent()
+ assertThat(currentScene).isEqualTo(SceneKey.Lockscreen)
+
+ sceneContainerConfig.sceneKeys
+ .filter { it != currentScene }
+ .filter {
+ // Moving to the Communal scene is not currently falsing protected.
+ it != SceneKey.Communal
+ }
+ .forEach { toScene ->
+ assertWithMessage("Protected scene $toScene not properly protected")
+ .that(underTest.canChangeScene(toScene = toScene))
+ .isFalse()
+ }
+ }
+
+ @Test
+ fun canChangeScene_whenNotAllowed_fromLockscreen_toFalsingUnprotectedScenes_returnsTrue() =
+ testScope.runTest {
+ falsingManager.setIsFalseTouch(true)
+ val currentScene by collectLastValue(underTest.currentScene)
+ fakeSceneDataSource.changeScene(toScene = SceneKey.Lockscreen)
+ runCurrent()
+ assertThat(currentScene).isEqualTo(SceneKey.Lockscreen)
+
+ sceneContainerConfig.sceneKeys
+ .filter {
+ // Moving to the Communal scene is not currently falsing protected.
+ it == SceneKey.Communal
+ }
+ .forEach { toScene ->
+ assertWithMessage("Unprotected scene $toScene is incorrectly protected")
+ .that(underTest.canChangeScene(toScene = toScene))
+ .isTrue()
+ }
+ }
+
+ @Test
+ fun canChangeScene_whenNotAllowed_fromGone_toAnyOtherScene_returnsTrue() =
+ testScope.runTest {
+ falsingManager.setIsFalseTouch(true)
+ val currentScene by collectLastValue(underTest.currentScene)
+ fakeSceneDataSource.changeScene(toScene = SceneKey.Gone)
+ runCurrent()
+ assertThat(currentScene).isEqualTo(SceneKey.Gone)
+
+ sceneContainerConfig.sceneKeys
+ .filter { it != currentScene }
+ .forEach { toScene ->
+ assertWithMessage("Protected scene $toScene not properly protected")
+ .that(underTest.canChangeScene(toScene = toScene))
+ .isTrue()
+ }
+ }
+
+ @Test
+ fun userInput() =
+ testScope.runTest {
+ assertThat(kosmos.fakePowerRepository.userTouchRegistered).isFalse()
+ underTest.onMotionEvent(mock())
+ assertThat(kosmos.fakePowerRepository.userTouchRegistered).isTrue()
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/PrivacyChipRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/PrivacyChipRepositoryTest.kt
new file mode 100644
index 000000000000..613f256113f3
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/PrivacyChipRepositoryTest.kt
@@ -0,0 +1,183 @@
+/*
+ * 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.shade.data.repository
+
+import android.content.Intent
+import android.safetycenter.SafetyCenterManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.privacy.PrivacyApplication
+import com.android.systemui.privacy.PrivacyConfig
+import com.android.systemui.privacy.PrivacyItem
+import com.android.systemui.privacy.PrivacyItemController
+import com.android.systemui.privacy.PrivacyType
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.kotlinArgumentCaptor
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.mockito.withArgCaptor
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations.initMocks
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PrivacyChipRepositoryTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val broadcastDispatcher = kosmos.broadcastDispatcher
+
+ @Mock private lateinit var privacyConfig: PrivacyConfig
+ @Mock private lateinit var privacyItemController: PrivacyItemController
+ @Mock private lateinit var safetyCenterManager: SafetyCenterManager
+
+ lateinit var underTest: PrivacyChipRepositoryImpl
+
+ @Before
+ fun setUp() {
+ initMocks(this)
+ setUpUnderTest()
+ }
+
+ @Test
+ fun isSafetyCenterEnabled_startEnabled() =
+ testScope.runTest {
+ setUpUnderTest(true)
+
+ val actual by collectLastValue(underTest.isSafetyCenterEnabled)
+ runCurrent()
+
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun isSafetyCenterEnabled_startDisabled() =
+ testScope.runTest {
+ setUpUnderTest(false)
+
+ val actual by collectLastValue(underTest.isSafetyCenterEnabled)
+
+ assertThat(actual).isFalse()
+ }
+
+ @Test
+ fun isSafetyCenterEnabled_updates() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isSafetyCenterEnabled)
+ runCurrent()
+
+ assertThat(actual).isFalse()
+
+ whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(true)
+
+ broadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(SafetyCenterManager.ACTION_SAFETY_CENTER_ENABLED_CHANGED),
+ )
+
+ runCurrent()
+
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun privacyItems_updates() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.privacyItems)
+ runCurrent()
+
+ val callback =
+ withArgCaptor<PrivacyItemController.Callback> {
+ verify(privacyItemController).addCallback(capture())
+ }
+
+ callback.onPrivacyItemsChanged(emptyList())
+ assertThat(actual).isEmpty()
+
+ val privacyItems =
+ listOf(
+ PrivacyItem(
+ privacyType = PrivacyType.TYPE_CAMERA,
+ application = PrivacyApplication("", 0)
+ ),
+ )
+ callback.onPrivacyItemsChanged(privacyItems)
+ assertThat(actual).isEqualTo(privacyItems)
+ }
+
+ @Test
+ fun isMicCameraIndicationEnabled_updates() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isMicCameraIndicationEnabled)
+ runCurrent()
+
+ val captor = kotlinArgumentCaptor<PrivacyConfig.Callback>()
+ verify(privacyConfig, times(2)).addCallback(captor.capture())
+ val callback = captor.allValues[0]
+
+ callback.onFlagMicCameraChanged(false)
+ assertThat(actual).isFalse()
+
+ callback.onFlagMicCameraChanged(true)
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun isLocationIndicationEnabled_updates() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isLocationIndicationEnabled)
+ runCurrent()
+
+ val captor = kotlinArgumentCaptor<PrivacyConfig.Callback>()
+ verify(privacyConfig, times(2)).addCallback(captor.capture())
+ val callback = captor.allValues[1]
+
+ callback.onFlagLocationChanged(false)
+ assertThat(actual).isFalse()
+
+ callback.onFlagLocationChanged(true)
+ assertThat(actual).isTrue()
+ }
+
+ private fun setUpUnderTest(isSafetyCenterEnabled: Boolean = false) {
+ whenever(safetyCenterManager.isSafetyCenterEnabled).thenReturn(isSafetyCenterEnabled)
+
+ underTest =
+ PrivacyChipRepositoryImpl(
+ applicationScope = kosmos.applicationCoroutineScope,
+ privacyConfig = privacyConfig,
+ privacyItemController = privacyItemController,
+ backgroundDispatcher = kosmos.testDispatcher,
+ broadcastDispatcher = broadcastDispatcher,
+ safetyCenterManager = safetyCenterManager,
+ )
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeHeaderClockRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeHeaderClockRepositoryTest.kt
new file mode 100644
index 000000000000..d7b77e613c2d
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeHeaderClockRepositoryTest.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.shade.data.repository
+
+import android.app.AlarmManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback
+import com.android.systemui.statusbar.policy.nextAlarmController
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.withArgCaptor
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ShadeHeaderClockRepositoryTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val nextAlarmController = kosmos.nextAlarmController
+
+ val underTest = kosmos.shadeHeaderClockRepository
+
+ @Test
+ fun nextAlarmIntent_updates() =
+ testScope.runTest {
+ assertThat(underTest.nextAlarmIntent).isNull()
+
+ val callback =
+ withArgCaptor<NextAlarmChangeCallback> {
+ verify(nextAlarmController).addCallback(capture())
+ }
+
+ callback.onNextAlarmChanged(AlarmManager.AlarmClockInfo(1L, mock()))
+ assertThat(underTest.nextAlarmIntent).isNotNull()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/PrivacyChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/PrivacyChipInteractorTest.kt
new file mode 100644
index 000000000000..f0293a8efc8a
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/PrivacyChipInteractorTest.kt
@@ -0,0 +1,154 @@
+/*
+ * 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.shade.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.privacy.OngoingPrivacyChip
+import com.android.systemui.privacy.PrivacyApplication
+import com.android.systemui.privacy.PrivacyItem
+import com.android.systemui.privacy.PrivacyType
+import com.android.systemui.privacy.privacyDialogController
+import com.android.systemui.privacy.privacyDialogControllerV2
+import com.android.systemui.shade.data.repository.fakePrivacyChipRepository
+import com.android.systemui.shade.data.repository.privacyChipRepository
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+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.initMocks
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PrivacyChipInteractorTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val privacyChipRepository = kosmos.fakePrivacyChipRepository
+ private val privacyDialogController = kosmos.privacyDialogController
+ private val privacyDialogControllerV2 = kosmos.privacyDialogControllerV2
+ @Mock private lateinit var privacyChip: OngoingPrivacyChip
+
+ val underTest = kosmos.privacyChipInteractor
+
+ @Before
+ fun setUp() {
+ initMocks(this)
+ whenever(privacyChip.context).thenReturn(this.context)
+ }
+
+ @Test
+ fun isChipVisible_updates() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isChipVisible)
+
+ privacyChipRepository.setPrivacyItems(emptyList())
+ runCurrent()
+
+ assertThat(actual).isFalse()
+
+ val privacyItems =
+ listOf(
+ PrivacyItem(
+ privacyType = PrivacyType.TYPE_CAMERA,
+ application = PrivacyApplication("", 0)
+ ),
+ )
+ privacyChipRepository.setPrivacyItems(privacyItems)
+ runCurrent()
+
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun isChipEnabled_noIndicationEnabled() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isChipEnabled)
+
+ privacyChipRepository.setIsMicCameraIndicationEnabled(false)
+ privacyChipRepository.setIsLocationIndicationEnabled(false)
+
+ assertThat(actual).isFalse()
+ }
+
+ @Test
+ fun isChipEnabled_micCameraIndicationEnabled() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isChipEnabled)
+
+ privacyChipRepository.setIsMicCameraIndicationEnabled(true)
+ privacyChipRepository.setIsLocationIndicationEnabled(false)
+
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun isChipEnabled_locationIndicationEnabled() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isChipEnabled)
+
+ privacyChipRepository.setIsMicCameraIndicationEnabled(false)
+ privacyChipRepository.setIsLocationIndicationEnabled(true)
+
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun isChipEnabled_allIndicationEnabled() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isChipEnabled)
+
+ privacyChipRepository.setIsMicCameraIndicationEnabled(true)
+ privacyChipRepository.setIsLocationIndicationEnabled(true)
+
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun onPrivacyChipClicked_safetyCenterEnabled() =
+ testScope.runTest {
+ privacyChipRepository.setIsSafetyCenterEnabled(true)
+
+ underTest.onPrivacyChipClicked(privacyChip)
+
+ verify(privacyDialogControllerV2).showDialog(any(), any())
+ verify(privacyDialogController, never()).showDialog(any())
+ }
+
+ @Test
+ fun onPrivacyChipClicked_safetyCenterDisabled() =
+ testScope.runTest {
+ privacyChipRepository.setIsSafetyCenterEnabled(false)
+
+ underTest.onPrivacyChipClicked(privacyChip)
+
+ verify(privacyDialogController).showDialog(any())
+ verify(privacyDialogControllerV2, never()).showDialog(any(), any())
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeHeaderClockInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeHeaderClockInteractorTest.kt
new file mode 100644
index 000000000000..84fc93008f49
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeHeaderClockInteractorTest.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade.domain.interactor
+
+import android.app.AlarmManager
+import android.content.Intent
+import android.provider.AlarmClock
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.activityStarter
+import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback
+import com.android.systemui.statusbar.policy.nextAlarmController
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argThat
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.withArgCaptor
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatcher
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ShadeHeaderClockInteractorTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val activityStarter = kosmos.activityStarter
+ private val nextAlarmController = kosmos.nextAlarmController
+
+ val underTest = kosmos.shadeHeaderClockInteractor
+
+ @Test
+ fun launchClockActivity_default() =
+ testScope.runTest {
+ underTest.launchClockActivity()
+ verify(activityStarter)
+ .postStartActivityDismissingKeyguard(
+ argThat(IntentMatcherAction(AlarmClock.ACTION_SHOW_ALARMS)),
+ any()
+ )
+ }
+
+ @Test
+ fun launchClockActivity_nextAlarmIntent() =
+ testScope.runTest {
+ val callback =
+ withArgCaptor<NextAlarmChangeCallback> {
+ verify(nextAlarmController).addCallback(capture())
+ }
+ callback.onNextAlarmChanged(AlarmManager.AlarmClockInfo(1L, mock()))
+
+ underTest.launchClockActivity()
+ verify(activityStarter).postStartActivityDismissingKeyguard(any())
+ }
+}
+
+private class IntentMatcherAction(private val action: String) : ArgumentMatcher<Intent> {
+ override fun matches(argument: Intent?): Boolean {
+ return argument?.action == action
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
index c0aaab3ad6e1..062741df01cf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
@@ -1,5 +1,7 @@
package com.android.systemui.shade.ui.viewmodel
+import android.content.Intent
+import android.provider.AlarmClock
import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -8,7 +10,9 @@ import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.kosmos.testScope
-import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.plugins.activityStarter
+import com.android.systemui.shade.domain.interactor.privacyChipInteractor
+import com.android.systemui.shade.domain.interactor.shadeHeaderClockInteractor
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
@@ -18,12 +22,16 @@ import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsVi
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.argThat
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatcher
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@SmallTest
@@ -31,7 +39,6 @@ import org.mockito.MockitoAnnotations
class ShadeHeaderViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val sceneInteractor by lazy { kosmos.sceneInteractor }
private val mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
private val flags = FakeFeatureFlagsClassic().also { it.set(Flags.NEW_NETWORK_SLICE_UI, false) }
@@ -62,9 +69,10 @@ class ShadeHeaderViewModelTest : SysuiTestCase() {
ShadeHeaderViewModel(
applicationScope = testScope.backgroundScope,
context = context,
- sceneInteractor = sceneInteractor,
mobileIconsInteractor = mobileIconsInteractor,
mobileIconsViewModel = mobileIconsViewModel,
+ privacyChipInteractor = kosmos.privacyChipInteractor,
+ clockInteractor = kosmos.shadeHeaderClockInteractor,
broadcastDispatcher = fakeBroadcastDispatcher,
)
}
@@ -82,6 +90,19 @@ class ShadeHeaderViewModelTest : SysuiTestCase() {
assertThat(mobileSubIds).isEqualTo(listOf(1, 2))
}
+ @Test
+ fun onClockClicked_launchesClock() =
+ testScope.runTest {
+ val activityStarter = kosmos.activityStarter
+ underTest.onClockClicked()
+
+ verify(activityStarter)
+ .postStartActivityDismissingKeyguard(
+ argThat(IntentMatcherAction(AlarmClock.ACTION_SHOW_ALARMS)),
+ anyInt(),
+ )
+ }
+
companion object {
private val SUB_1 =
SubscriptionModel(
@@ -99,3 +120,9 @@ class ShadeHeaderViewModelTest : SysuiTestCase() {
)
}
}
+
+private class IntentMatcherAction(private val action: String) : ArgumentMatcher<Intent> {
+ override fun matches(argument: Intent?): Boolean {
+ return argument?.action == action
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
index 799e8f054d51..d655ade5cf2c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
@@ -27,10 +27,12 @@ import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.kosmos.testScope
-import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.shade.domain.interactor.privacyChipInteractor
+import com.android.systemui.shade.domain.interactor.shadeHeaderClockInteractor
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
@@ -96,9 +98,10 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
ShadeHeaderViewModel(
applicationScope = testScope.backgroundScope,
context = context,
- sceneInteractor = sceneInteractor,
mobileIconsInteractor = mobileIconsInteractor,
mobileIconsViewModel = mobileIconsViewModel,
+ privacyChipInteractor = kosmos.privacyChipInteractor,
+ clockInteractor = kosmos.shadeHeaderClockInteractor,
broadcastDispatcher = fakeBroadcastDispatcher,
)
@@ -166,6 +169,32 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
}
@Test
+ fun isClickable_deviceUnlocked_false() =
+ testScope.runTest {
+ val isClickable by collectLastValue(underTest.isClickable)
+ kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+ AuthenticationMethodModel.Pin
+ )
+ kosmos.fakeDeviceEntryRepository.setUnlocked(true)
+ runCurrent()
+
+ assertThat(isClickable).isFalse()
+ }
+
+ @Test
+ fun isClickable_deviceLockedSecurely_true() =
+ testScope.runTest {
+ val isClickable by collectLastValue(underTest.isClickable)
+ kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+ AuthenticationMethodModel.Pin
+ )
+ kosmos.fakeDeviceEntryRepository.setUnlocked(false)
+ runCurrent()
+
+ assertThat(isClickable).isTrue()
+ }
+
+ @Test
fun onContentClicked_deviceUnlocked_switchesToGone() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java
deleted file mode 100644
index d0e05fa60114..000000000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java
+++ /dev/null
@@ -1,614 +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.statusbar;
-
-import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
-import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS;
-import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS;
-import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS;
-import static android.provider.Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS;
-
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.ActivityManager;
-import android.app.KeyguardManager;
-import android.app.Notification;
-import android.app.admin.DevicePolicyManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.UserInfo;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings;
-import android.util.SparseArray;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.widget.LockPatternUtils;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FakeFeatureFlagsClassic;
-import com.android.systemui.flags.Flags;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.recents.OverviewProxyService;
-import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager.NotificationStateChangedListener;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
-import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.settings.FakeSettings;
-
-import com.google.android.collect.Lists;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.concurrent.Executor;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class NotificationLockscreenUserManagerMainThreadTest extends SysuiTestCase {
- @Mock
- private NotificationPresenter mPresenter;
- @Mock
- private UserManager mUserManager;
- @Mock
- private UserTracker mUserTracker;
-
- // Dependency mocks:
- @Mock
- private NotificationVisibilityProvider mVisibilityProvider;
- @Mock
- private CommonNotifCollection mNotifCollection;
- @Mock
- private DevicePolicyManager mDevicePolicyManager;
- @Mock
- private NotificationClickNotifier mClickNotifier;
- @Mock
- private OverviewProxyService mOverviewProxyService;
- @Mock
- private KeyguardManager mKeyguardManager;
- @Mock
- private DeviceProvisionedController mDeviceProvisionedController;
- @Mock
- private StatusBarStateController mStatusBarStateController;
- @Mock
- private BroadcastDispatcher mBroadcastDispatcher;
- @Mock
- private KeyguardStateController mKeyguardStateController;
-
- private UserInfo mCurrentUser;
- private UserInfo mSecondaryUser;
- private UserInfo mWorkUser;
- private UserInfo mCommunalUser;
- private FakeSettings mSettings;
- private TestNotificationLockscreenUserManager mLockscreenUserManager;
- private NotificationEntry mCurrentUserNotif;
- private NotificationEntry mSecondaryUserNotif;
- private NotificationEntry mWorkProfileNotif;
- private final FakeFeatureFlagsClassic mFakeFeatureFlags = new FakeFeatureFlagsClassic();
- private Executor mMainExecutor = Runnable::run; // Direct executor
- private Executor mBackgroundExecutor = Runnable::run; // Direct executor
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- mFakeFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, false);
-
- int currentUserId = ActivityManager.getCurrentUser();
- when(mUserTracker.getUserId()).thenReturn(currentUserId);
- mSettings = new FakeSettings();
- mSettings.setUserId(ActivityManager.getCurrentUser());
- mCurrentUser = new UserInfo(currentUserId, "", 0);
- mSecondaryUser = new UserInfo(currentUserId + 1, "", 0);
- mWorkUser = new UserInfo(currentUserId + 2, "" /* name */, null /* iconPath */, 0,
- UserManager.USER_TYPE_PROFILE_MANAGED);
- mCommunalUser = new UserInfo(currentUserId + 3, "" /* name */, null /* iconPath */, 0,
- UserManager.USER_TYPE_PROFILE_COMMUNAL);
-
- when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(true);
- when(mUserManager.getProfiles(currentUserId)).thenReturn(Lists.newArrayList(
- mCurrentUser, mWorkUser));
- when(mUserManager.getProfilesIncludingCommunal(currentUserId)).thenReturn(
- Lists.newArrayList(mCurrentUser, mWorkUser, mCommunalUser));
- when(mUserManager.getProfiles(mSecondaryUser.id)).thenReturn(Lists.newArrayList(
- mSecondaryUser));
- when(mUserManager.getProfilesIncludingCommunal(mSecondaryUser.id)).thenReturn(
- Lists.newArrayList(mSecondaryUser, mCommunalUser));
-
- Notification notifWithPrivateVisibility = new Notification();
- notifWithPrivateVisibility.visibility = Notification.VISIBILITY_PRIVATE;
- mCurrentUserNotif = new NotificationEntryBuilder()
- .setNotification(notifWithPrivateVisibility)
- .setUser(new UserHandle(mCurrentUser.id))
- .build();
- mSecondaryUserNotif = new NotificationEntryBuilder()
- .setNotification(notifWithPrivateVisibility)
- .setUser(new UserHandle(mSecondaryUser.id))
- .build();
- mWorkProfileNotif = new NotificationEntryBuilder()
- .setNotification(notifWithPrivateVisibility)
- .setUser(new UserHandle(mWorkUser.id))
- .build();
-
- mLockscreenUserManager = new TestNotificationLockscreenUserManager(mContext);
- mLockscreenUserManager.setUpWithPresenter(mPresenter);
- }
-
- private void changeSetting(String setting) {
- final Collection<Uri> lockScreenUris = new ArrayList<>();
- lockScreenUris.add(Settings.Secure.getUriFor(setting));
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false,
- lockScreenUris, 0);
- }
-
- @Test
- public void testGetCurrentProfiles() {
- final SparseArray<UserInfo> expectedCurProfiles = new SparseArray<>();
- expectedCurProfiles.put(mCurrentUser.id, mCurrentUser);
- expectedCurProfiles.put(mWorkUser.id, mWorkUser);
- if (android.multiuser.Flags.supportCommunalProfile()) {
- expectedCurProfiles.put(mCommunalUser.id, mCommunalUser);
- }
- assertTrue(mLockscreenUserManager.getCurrentProfiles().contentEquals(expectedCurProfiles));
-
- mLockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext);
-
- final SparseArray<UserInfo> expectedSecProfiles = new SparseArray<>();
- expectedSecProfiles.put(mSecondaryUser.id, mSecondaryUser);
- if (android.multiuser.Flags.supportCommunalProfile()) {
- expectedSecProfiles.put(mCommunalUser.id, mCommunalUser);
- }
- assertTrue(mLockscreenUserManager.getCurrentProfiles().contentEquals(expectedSecProfiles));
- }
-
- @Test
- public void testLockScreenShowNotificationsFalse() {
- mSettings.putInt(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
- changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
- assertFalse(mLockscreenUserManager.shouldShowLockscreenNotifications());
- }
-
- @Test
- public void testLockScreenShowNotificationsTrue() {
- mSettings.putInt(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
- changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
- assertTrue(mLockscreenUserManager.shouldShowLockscreenNotifications());
- }
-
- @Test
- public void testLockScreenAllowPrivateNotificationsTrue() {
- mSettings.putInt(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1);
- changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
- assertTrue(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUser.id));
- }
-
- @Test
- public void testLockScreenAllowPrivateNotificationsFalse() {
- mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
- mCurrentUser.id);
- changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
- assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUser.id));
- }
-
- @Test
- public void testLockScreenAllowsWorkPrivateNotificationsFalse() {
- mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
- mWorkUser.id);
- changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
- assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mWorkUser.id));
- }
-
- @Test
- public void testLockScreenAllowsWorkPrivateNotificationsTrue() {
- mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
- mWorkUser.id);
- changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
- assertTrue(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mWorkUser.id));
- }
-
- @Test
- public void testCurrentUserPrivateNotificationsNotRedacted() {
- // GIVEN current user doesn't allow private notifications to show
- mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
- mCurrentUser.id);
- changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
-
- // THEN current user's notification is redacted
- assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
- }
-
- @Test
- public void testCurrentUserPrivateNotificationsRedacted() {
- // GIVEN current user allows private notifications to show
- mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
- mCurrentUser.id);
- changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
-
- // THEN current user's notification isn't redacted
- assertFalse(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
- }
-
- @Test
- public void testWorkPrivateNotificationsRedacted() {
- // GIVEN work profile doesn't private notifications to show
- mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
- mWorkUser.id);
- changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
-
- // THEN work profile notification is redacted
- assertTrue(mLockscreenUserManager.needsRedaction(mWorkProfileNotif));
- assertFalse(mLockscreenUserManager.allowsManagedPrivateNotificationsInPublic());
- }
-
- @Test
- public void testWorkPrivateNotificationsNotRedacted() {
- // GIVEN work profile allows private notifications to show
- mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
- mWorkUser.id);
- changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
-
- // THEN work profile notification isn't redacted
- assertFalse(mLockscreenUserManager.needsRedaction(mWorkProfileNotif));
- assertTrue(mLockscreenUserManager.allowsManagedPrivateNotificationsInPublic());
- }
-
- @Test
- public void testWorkPrivateNotificationsNotRedacted_otherUsersRedacted() {
- // GIVEN work profile allows private notifications to show but the other users don't
- mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
- mWorkUser.id);
- mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
- mCurrentUser.id);
- mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
- mSecondaryUser.id);
- changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
- changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
- changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
-
- // THEN the work profile notification doesn't need to be redacted
- assertFalse(mLockscreenUserManager.needsRedaction(mWorkProfileNotif));
-
- // THEN the current user and secondary user notifications do need to be redacted
- assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
- assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
- }
-
- @Test
- public void testWorkProfileRedacted_otherUsersNotRedacted() {
- // GIVEN work profile doesn't allow private notifications to show but the other users do
- mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
- mWorkUser.id);
- mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
- mCurrentUser.id);
- mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
- mSecondaryUser.id);
- changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
- changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
- changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
-
- // THEN the work profile notification needs to be redacted
- assertTrue(mLockscreenUserManager.needsRedaction(mWorkProfileNotif));
-
- // THEN the current user and secondary user notifications don't need to be redacted
- assertFalse(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
- assertFalse(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
- }
-
- @Test
- public void testSecondaryUserNotRedacted_currentUserRedacted() {
- // GIVEN secondary profile allows private notifications to show but the current user
- // doesn't allow private notifications to show
- mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
- mCurrentUser.id);
- mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
- mSecondaryUser.id);
- changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
- changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
-
- // THEN the secondary profile notification still needs to be redacted because the current
- // user's setting takes precedence
- assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
- }
-
- @Test
- public void testUserSwitchedCallsOnUserSwitching() {
- mLockscreenUserManager.getUserTrackerCallbackForTest().onUserChanging(mSecondaryUser.id,
- mContext);
- verify(mPresenter, times(1)).onUserSwitched(mSecondaryUser.id);
- }
-
- @Test
- public void testIsLockscreenPublicMode() {
- assertFalse(mLockscreenUserManager.isLockscreenPublicMode(mCurrentUser.id));
- mLockscreenUserManager.setLockscreenPublicMode(true, mCurrentUser.id);
- assertTrue(mLockscreenUserManager.isLockscreenPublicMode(mCurrentUser.id));
- }
-
- @Test
- public void testUpdateIsPublicMode() {
- when(mKeyguardStateController.isMethodSecure()).thenReturn(true);
-
- NotificationStateChangedListener listener = mock(NotificationStateChangedListener.class);
- mLockscreenUserManager.addNotificationStateChangedListener(listener);
- mLockscreenUserManager.mCurrentProfiles.append(0, mock(UserInfo.class));
-
- // first call explicitly sets user 0 to not public; notifies
- mLockscreenUserManager.updatePublicMode();
- assertFalse(mLockscreenUserManager.isLockscreenPublicMode(0));
- verify(listener).onNotificationStateChanged();
- clearInvocations(listener);
-
- // calling again has no changes; does not notify
- mLockscreenUserManager.updatePublicMode();
- assertFalse(mLockscreenUserManager.isLockscreenPublicMode(0));
- verify(listener, never()).onNotificationStateChanged();
-
- // Calling again with keyguard now showing makes user 0 public; notifies
- when(mKeyguardStateController.isShowing()).thenReturn(true);
- mLockscreenUserManager.updatePublicMode();
- assertTrue(mLockscreenUserManager.isLockscreenPublicMode(0));
- verify(listener).onNotificationStateChanged();
- clearInvocations(listener);
-
- // calling again has no changes; does not notify
- mLockscreenUserManager.updatePublicMode();
- assertTrue(mLockscreenUserManager.isLockscreenPublicMode(0));
- verify(listener, never()).onNotificationStateChanged();
- }
-
- @Test
- public void testDevicePolicyDoesNotAllowNotifications() {
- // User allows them
- mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
- changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
- // DevicePolicy hides notifs on lockscreen
- when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
- .thenReturn(KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
-
- BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
- 0, null, null, 0, true, false, null, mCurrentUser.id, 0);
- mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
- mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
- new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
-
- assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
- }
-
- @Test
- public void testDevicePolicyDoesNotAllowNotifications_secondary() {
- Mockito.clearInvocations(mDevicePolicyManager);
- // User allows notifications
- mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mSecondaryUser.id);
- changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
- // DevicePolicy hides notifications
- when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mSecondaryUser.id))
- .thenReturn(KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
-
- BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
- 0, null, null, 0, true, false, null, mSecondaryUser.id, 0);
- mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
- mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
- new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
-
- assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id));
- }
-
- @Test
- public void testDevicePolicy_noPrivateNotifications() {
- Mockito.clearInvocations(mDevicePolicyManager);
- // User allows notifications
- mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
- changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
- // DevicePolicy hides sensitive content
- when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
- .thenReturn(KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
-
- BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
- 0, null, null, 0, true, false, null, mCurrentUser.id, 0);
- mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
- mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
- new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
-
- assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
- }
-
- @Test
- public void testDevicePolicy_noPrivateNotifications_userAll() {
- // User allows notifications
- mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
- changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
- // DevicePolicy hides sensitive content
- when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
- .thenReturn(KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
-
- BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
- 0, null, null, 0, true, false, null, mCurrentUser.id, 0);
- mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
- mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
- new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
-
- assertTrue(mLockscreenUserManager.needsRedaction(new NotificationEntryBuilder()
- .setNotification(new Notification())
- .setUser(UserHandle.ALL)
- .build()));
- }
-
- @Test
- public void testDevicePolicyPrivateNotifications_secondary() {
- Mockito.clearInvocations(mDevicePolicyManager);
- // User allows notifications
- mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mSecondaryUser.id);
- changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
- // DevicePolicy hides sensitive content
- when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mSecondaryUser.id))
- .thenReturn(KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
-
- BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
- 0, null, null, 0, true, false, null, mSecondaryUser.id, 0);
- mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
- mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
- new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
-
- mLockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext);
- assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
- }
-
- @Test
- public void testHideNotifications_primary() {
- mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mCurrentUser.id);
- changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
- assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
- }
-
- @Test
- public void testHideNotifications_secondary() {
- mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mSecondaryUser.id);
- changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
- assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id));
- }
-
- @Test
- public void testHideNotifications_secondary_userSwitch() {
- mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mSecondaryUser.id);
- changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
- mLockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext);
-
- assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id));
- }
-
- @Test
- public void testShowNotifications_secondary_userSwitch() {
- mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mSecondaryUser.id);
- changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
- mLockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext);
-
- assertTrue(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id));
- }
-
- @Test
- public void testUserAllowsNotificationsInPublic_keyguardManagerNoPrivateNotifications() {
- // DevicePolicy allows notifications
- when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
- .thenReturn(0);
- BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
- 0, null, null, 0, true, false, null, mCurrentUser.id, 0);
- mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
- mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
- new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
-
- // KeyguardManager does not allow notifications
- when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(false);
-
- // User allows notifications
- mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
- // We shouldn't need to call this method, but getPrivateNotificationsAllowed has no
- // callback, so it's only updated when the setting is
- changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
- assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
- }
-
- @Test
- public void testUserAllowsNotificationsInPublic_settingsChange() {
- // User allows notifications
- mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
- changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
- assertTrue(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
-
- // User disables
- mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mCurrentUser.id);
- changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
- assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
- }
-
- private class TestNotificationLockscreenUserManager
- extends NotificationLockscreenUserManagerImpl {
- public TestNotificationLockscreenUserManager(Context context) {
- super(
- context,
- mBroadcastDispatcher,
- mDevicePolicyManager,
- mUserManager,
- mUserTracker,
- (() -> mVisibilityProvider),
- (() -> mNotifCollection),
- mClickNotifier,
- (() -> mOverviewProxyService),
- NotificationLockscreenUserManagerMainThreadTest.this.mKeyguardManager,
- mStatusBarStateController,
- mMainExecutor,
- mBackgroundExecutor,
- mDeviceProvisionedController,
- mKeyguardStateController,
- mSettings,
- mock(DumpManager.class),
- mock(LockPatternUtils.class),
- mFakeFeatureFlags);
- }
-
- public BroadcastReceiver getBaseBroadcastReceiverForTest() {
- return mBaseBroadcastReceiver;
- }
-
- public UserTracker.Callback getUserTrackerCallbackForTest() {
- return mUserChangedCallback;
- }
-
- public ContentObserver getLockscreenSettingsObserverForTest() {
- return mLockscreenSettingsObserver;
- }
-
- public ContentObserver getSettingsObserverForTest() {
- return mSettingsObserver;
- }
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index bcc0710359cc..d505b27a9969 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -172,8 +172,6 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
public void setUp() {
MockitoAnnotations.initMocks(this);
- mFakeFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true);
-
int currentUserId = ActivityManager.getCurrentUser();
when(mUserTracker.getUserId()).thenReturn(currentUserId);
mSettings = new FakeSettings();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationContentDescriptionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationContentDescriptionTest.kt
index f67c70ce783f..12473cb46793 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationContentDescriptionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationContentDescriptionTest.kt
@@ -62,36 +62,23 @@ class NotificationContentDescriptionTest : SysuiTestCase() {
assertThat(description).isEqualTo(createDescriptionText(n, ""))
}
- @Test
- fun nullNotification_descriptionIsAppName() {
- val description = contentDescForNotification(context, null)
- assertThat(description).isEqualTo(createDescriptionText(null, ""))
- }
-
private fun createNotification(
title: String? = null,
text: String? = null,
ticker: String? = null
): Notification =
- Notification.Builder(context)
+ Notification.Builder(context, "channel")
.setContentTitle(title)
.setContentText(text)
.setTicker(ticker)
.build()
private fun getTestAppName(): String {
- return getAppName(createNotification("", "", ""))
+ return createNotification("", "", "").loadHeaderAppName(mContext)
}
- private fun getAppName(n: Notification?) =
- n?.let {
- val builder = Notification.Builder.recoverBuilder(context, it)
- builder.loadHeaderAppName()
- }
- ?: ""
-
private fun createDescriptionText(n: Notification?, desc: String?): String {
- val appName = getAppName(n)
+ val appName = n?.loadHeaderAppName(mContext)
return context.getString(R.string.accessibility_desc_notification_icon, appName, desc)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractorTest.kt
new file mode 100644
index 000000000000..e188f5bfc1c8
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractorTest.kt
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.domain.interactor
+
+import android.app.NotificationManager
+import android.media.AudioManager
+import android.provider.Settings.Global
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.statusbar.notification.data.model.ZenMode
+import com.android.settingslib.statusbar.notification.data.repository.updateNotificationPolicy
+import com.android.settingslib.statusbar.notification.domain.interactor.NotificationsSoundPolicyInteractor
+import com.android.settingslib.volume.shared.model.AudioStream
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Expect
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+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
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NotificationsSoundPolicyInteractorTest : SysuiTestCase() {
+
+ @JvmField @Rule val expect = Expect.create()
+
+ private val kosmos = testKosmos()
+
+ private lateinit var underTest: NotificationsSoundPolicyInteractor
+
+ @Before
+ fun setup() {
+ with(kosmos) {
+ underTest = NotificationsSoundPolicyInteractor(notificationsSoundPolicyRepository)
+ }
+ }
+
+ @Test
+ fun onlyAlarmsCategory_areAlarmsAllowed_isTrue() {
+ with(kosmos) {
+ testScope.runTest {
+ notificationsSoundPolicyRepository.updateZenMode(ZenMode(Global.ZEN_MODE_OFF))
+ val expectedByCategory =
+ NotificationManager.Policy.ALL_PRIORITY_CATEGORIES.associateWith {
+ it == NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS
+ }
+ expectedByCategory.forEach { entry ->
+ notificationsSoundPolicyRepository.updateNotificationPolicy(
+ priorityCategories = entry.key
+ )
+
+ val areAlarmsAllowed by collectLastValue(underTest.areAlarmsAllowed)
+ runCurrent()
+
+ expect.that(areAlarmsAllowed).isEqualTo(entry.value)
+ }
+ }
+ }
+ }
+
+ @Test
+ fun onlyMediaCategory_areAlarmsAllowed_isTrue() {
+ with(kosmos) {
+ testScope.runTest {
+ notificationsSoundPolicyRepository.updateZenMode(ZenMode(Global.ZEN_MODE_OFF))
+ val expectedByCategory =
+ NotificationManager.Policy.ALL_PRIORITY_CATEGORIES.associateWith {
+ it == NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA
+ }
+ expectedByCategory.forEach { entry ->
+ notificationsSoundPolicyRepository.updateNotificationPolicy(
+ priorityCategories = entry.key
+ )
+
+ val isMediaAllowed by collectLastValue(underTest.isMediaAllowed)
+ runCurrent()
+
+ expect.that(isMediaAllowed).isEqualTo(entry.value)
+ }
+ }
+ }
+ }
+
+ @Test
+ fun atLeastOneCategoryAllowed_isRingerAllowed_isTrue() {
+ with(kosmos) {
+ testScope.runTest {
+ for (category in NotificationManager.Policy.ALL_PRIORITY_CATEGORIES) {
+ notificationsSoundPolicyRepository.updateNotificationPolicy(
+ priorityCategories = category,
+ state = NotificationManager.Policy.STATE_UNSET,
+ )
+
+ val isRingerAllowed by collectLastValue(underTest.isRingerAllowed)
+ runCurrent()
+
+ expect.that(isRingerAllowed).isTrue()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun allCategoriesAllowed_isRingerAllowed_isTrue() {
+ with(kosmos) {
+ testScope.runTest {
+ notificationsSoundPolicyRepository.updateNotificationPolicy(
+ priorityCategories =
+ NotificationManager.Policy.ALL_PRIORITY_CATEGORIES.reduce { acc, value ->
+ acc or value
+ },
+ state = NotificationManager.Policy.STATE_PRIORITY_CHANNELS_BLOCKED,
+ )
+
+ val isRingerAllowed by collectLastValue(underTest.isRingerAllowed)
+ runCurrent()
+
+ assertThat(isRingerAllowed).isTrue()
+ }
+ }
+ }
+
+ @Test
+ fun noCategoriesAndBlocked_isRingerAllowed_isFalse() {
+ with(kosmos) {
+ testScope.runTest {
+ notificationsSoundPolicyRepository.updateNotificationPolicy(
+ priorityCategories = 0,
+ state = NotificationManager.Policy.STATE_PRIORITY_CHANNELS_BLOCKED,
+ )
+
+ val isRingerAllowed by collectLastValue(underTest.isRingerAllowed)
+ runCurrent()
+
+ assertThat(isRingerAllowed).isFalse()
+ }
+ }
+ }
+
+ @Test
+ fun zenModeNoInterruptions_allStreams_muted() {
+ with(kosmos) {
+ testScope.runTest {
+ notificationsSoundPolicyRepository.updateNotificationPolicy()
+ notificationsSoundPolicyRepository.updateZenMode(
+ ZenMode(Global.ZEN_MODE_NO_INTERRUPTIONS)
+ )
+
+ for (stream in AudioStream.supportedStreamTypes) {
+ val isZenMuted by collectLastValue(underTest.isZenMuted(AudioStream(stream)))
+ runCurrent()
+
+ expect.that(isZenMuted).isTrue()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun zenModeOff_allStreams_notMuted() {
+ with(kosmos) {
+ testScope.runTest {
+ notificationsSoundPolicyRepository.updateNotificationPolicy()
+ notificationsSoundPolicyRepository.updateZenMode(ZenMode(Global.ZEN_MODE_OFF))
+
+ for (stream in AudioStream.supportedStreamTypes) {
+ val isZenMuted by collectLastValue(underTest.isZenMuted(AudioStream(stream)))
+ runCurrent()
+
+ expect.that(isZenMuted).isFalse()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun zenModeAlarms_ringAndNotifications_muted() {
+ with(kosmos) {
+ val expectedToBeMuted =
+ setOf(AudioManager.STREAM_RING, AudioManager.STREAM_NOTIFICATION)
+ testScope.runTest {
+ notificationsSoundPolicyRepository.updateNotificationPolicy()
+ notificationsSoundPolicyRepository.updateZenMode(ZenMode(Global.ZEN_MODE_ALARMS))
+
+ for (stream in AudioStream.supportedStreamTypes) {
+ val isZenMuted by collectLastValue(underTest.isZenMuted(AudioStream(stream)))
+ runCurrent()
+
+ expect.that(isZenMuted).isEqualTo(stream in expectedToBeMuted)
+ }
+ }
+ }
+ }
+
+ @Test
+ fun alarms_allowed_notMuted() {
+ with(kosmos) {
+ testScope.runTest {
+ notificationsSoundPolicyRepository.updateZenMode(
+ ZenMode(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
+ )
+ notificationsSoundPolicyRepository.updateNotificationPolicy(
+ priorityCategories = NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS
+ )
+
+ val isZenMuted by
+ collectLastValue(underTest.isZenMuted(AudioStream(AudioManager.STREAM_ALARM)))
+ runCurrent()
+
+ expect.that(isZenMuted).isFalse()
+ }
+ }
+ }
+
+ @Test
+ fun media_allowed_notMuted() {
+ with(kosmos) {
+ testScope.runTest {
+ notificationsSoundPolicyRepository.updateZenMode(
+ ZenMode(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
+ )
+ notificationsSoundPolicyRepository.updateNotificationPolicy(
+ priorityCategories = NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA
+ )
+
+ val isZenMuted by
+ collectLastValue(underTest.isZenMuted(AudioStream(AudioManager.STREAM_MUSIC)))
+ runCurrent()
+
+ expect.that(isZenMuted).isFalse()
+ }
+ }
+ }
+
+ @Test
+ fun ringer_allowed_notificationsNotMuted() {
+ with(kosmos) {
+ testScope.runTest {
+ notificationsSoundPolicyRepository.updateZenMode(
+ ZenMode(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
+ )
+ notificationsSoundPolicyRepository.updateNotificationPolicy(
+ priorityCategories =
+ NotificationManager.Policy.ALL_PRIORITY_CATEGORIES.reduce { acc, value ->
+ acc or value
+ },
+ state = NotificationManager.Policy.STATE_PRIORITY_CHANNELS_BLOCKED,
+ )
+
+ val isZenMuted by
+ collectLastValue(
+ underTest.isZenMuted(AudioStream(AudioManager.STREAM_NOTIFICATION))
+ )
+ runCurrent()
+
+ expect.that(isZenMuted).isFalse()
+ }
+ }
+ }
+
+ @Test
+ fun ringer_allowed_ringNotMuted() {
+ with(kosmos) {
+ testScope.runTest {
+ notificationsSoundPolicyRepository.updateZenMode(
+ ZenMode(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
+ )
+ notificationsSoundPolicyRepository.updateNotificationPolicy(
+ priorityCategories =
+ NotificationManager.Policy.ALL_PRIORITY_CATEGORIES.reduce { acc, value ->
+ acc or value
+ },
+ state = NotificationManager.Policy.STATE_PRIORITY_CHANNELS_BLOCKED,
+ )
+
+ val isZenMuted by
+ collectLastValue(underTest.isZenMuted(AudioStream(AudioManager.STREAM_RING)))
+ runCurrent()
+
+ expect.that(isZenMuted).isFalse()
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index 2da88e9f0ef9..0641c610aaff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -21,7 +21,7 @@ package com.android.systemui.statusbar.notification.stack.ui.viewmodel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR
+import com.android.systemui.Flags.FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
@@ -52,6 +52,7 @@ import com.android.systemui.statusbar.policy.splitShadeStateController
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
+import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
@@ -134,7 +135,7 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
@Test
fun validatePaddingTopInSplitShade_refactorFlagOff_usesLargeHeaderResource() =
testScope.runTest {
- mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(5)
whenever(splitShadeStateController.shouldUseSplitNotificationShade(any()))
.thenReturn(true)
@@ -153,7 +154,7 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
@Test
fun validatePaddingTopInSplitShade_refactorFlagOn_usesLargeHeaderHelper() =
testScope.runTest {
- mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(5)
whenever(splitShadeStateController.shouldUseSplitNotificationShade(any()))
.thenReturn(true)
@@ -210,7 +211,7 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
@Test
fun validateMarginTopWithLargeScreenHeader_refactorFlagOff_usesResource() =
testScope.runTest {
- mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
val headerResourceHeight = 50
val headerHelperHeight = 100
whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight())
@@ -229,7 +230,7 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
@Test
fun validateMarginTopWithLargeScreenHeader_refactorFlagOn_usesHelper() =
testScope.runTest {
- mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
val headerResourceHeight = 50
val headerHelperHeight = 100
whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight())
@@ -274,8 +275,7 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
)
)
runCurrent()
- // Expected alpha is inverse of progress as notifications are fading away
- assertThat(alpha).isEqualTo(1 - progress)
+ assertThat(alpha).isIn(Range.closed(0f, 1f))
// Finish transition to glanceable hub
keyguardTransitionRepository.sendTransitionStep(
@@ -449,7 +449,7 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
@Test
fun boundsOnLockscreenInSplitShade_refactorFlagOff_usesLargeHeaderResource() =
testScope.runTest {
- mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
val bounds by collectLastValue(underTest.bounds)
// When in split shade
@@ -478,7 +478,7 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
@Test
fun boundsOnLockscreenInSplitShade_refactorFlagOn_usesLargeHeaderHelper() =
testScope.runTest {
- mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
val bounds by collectLastValue(underTest.bounds)
// When in split shade
@@ -681,7 +681,7 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
@Test
fun shadeCollapseFadeIn() =
testScope.runTest {
- val fadeIn by collectLastValue(underTest.shadeCollpaseFadeIn)
+ val fadeIn by collectLastValue(underTest.shadeCollapseFadeIn)
// Start on lockscreen without the shade
underTest.setShadeCollapseFadeInComplete(false)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
index 7a78b366dd7f..91699381ae7a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
@@ -36,6 +36,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.LockIconViewController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.biometrics.AuthController;
@@ -45,7 +46,7 @@ import com.android.systemui.flags.FakeFeatureFlagsClassic;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.keyguard.domain.interactor.DozeInteractor;
import com.android.systemui.shade.NotificationShadeWindowViewController;
-import com.android.systemui.shade.ShadeViewController;
+import com.android.systemui.shade.ShadeLockscreenInteractor;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.StatusBarState;
@@ -91,7 +92,8 @@ public class DozeServiceHostTest extends SysuiTestCase {
@Mock private NotificationIconAreaController mNotificationIconAreaController;
@Mock private NotificationShadeWindowViewController mNotificationShadeWindowViewController;
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
- @Mock private ShadeViewController mShadeViewController;
+ @Mock private ShadeLockscreenInteractor mShadeLockscreenInteractor;
+ @Mock private LockIconViewController mLockIconViewController;
@Mock private View mAmbientIndicationContainer;
@Mock private BiometricUnlockController mBiometricUnlockController;
@Mock private AuthController mAuthController;
@@ -109,13 +111,12 @@ public class DozeServiceHostTest extends SysuiTestCase {
() -> mBiometricUnlockController, () -> mAssistManager, mDozeScrimController,
mKeyguardUpdateMonitor, mPulseExpansionHandler, mNotificationShadeWindowController,
mNotificationWakeUpCoordinator, mAuthController, mNotificationIconAreaController,
- mDozeInteractor);
+ mShadeLockscreenInteractor, mDozeInteractor);
mDozeServiceHost.initialize(
mCentralSurfaces,
mStatusBarKeyguardViewManager,
mNotificationShadeWindowViewController,
- mShadeViewController,
mAmbientIndicationContainer);
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
index 8b6880c3cb67..c804fc6990ae 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
@@ -86,7 +86,8 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
var isRtl = false
var targetRotation = ROTATION_NONE
- var bounds = calculateInsetsForRotationWithRotatedResources(
+ var bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
null,
@@ -97,29 +98,33 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
var chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl)
- /* 1080 - 20 (rounded corner) - 30 (chip),
- * 0 (sb top)
- * 1080 - 20 (rounded corner) + 10 ( dot),
- * 100 (sb height portrait)
- */
+ /*
+ * 1080 - 20 (rounded corner) - 30 (chip),
+ * 0 (sb top)
+ * 1080 - 20 (rounded corner) + 10 ( dot),
+ * 100 (sb height portrait)
+ */
var expected = Rect(1030, 0, 1070, 100)
assertRects(expected, chipBounds, currentRotation, targetRotation)
isRtl = true
chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl)
- /* 0 + 20 (rounded corner) - 10 (dot),
- * 0 (sb top)
- * 0 + 20 (rounded corner) + 30 (chip),
- * 100 (sb height portrait)
- */
+ /*
+ * 0 + 20 (rounded corner) - 10 (dot),
+ * 0 (sb top)
+ * 0 + 20 (rounded corner) + 30 (chip),
+ * 100 (sb height portrait)
+ */
expected = Rect(10, 0, 50, 100)
assertRects(expected, chipBounds, currentRotation, targetRotation)
isRtl = false
targetRotation = ROTATION_LANDSCAPE
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -130,23 +135,26 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl)
- /* 2160 - 20 (rounded corner) - 30 (chip),
- * 0 (sb top)
- * 2160 - 20 (rounded corner) + 10 ( dot),
- * 60 (sb height landscape)
- */
+ /*
+ * 2160 - 20 (rounded corner) - 30 (chip),
+ * 0 (sb top)
+ * 2160 - 20 (rounded corner) + 10 ( dot),
+ * 60 (sb height landscape)
+ */
expected = Rect(2110, 0, 2150, 60)
assertRects(expected, chipBounds, currentRotation, targetRotation)
isRtl = true
chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl)
- /* 0 + 20 (rounded corner) - 10 (dot),
- * 0 (sb top)
- * 0 + 20 (rounded corner) + 30 (chip),
- * 60 (sb height landscape)
- */
+ /*
+ * 0 + 20 (rounded corner) - 10 (dot),
+ * 0 (sb top)
+ * 0 + 20 (rounded corner) + 30 (chip),
+ * 60 (sb height landscape)
+ */
expected = Rect(10, 0, 50, 60)
assertRects(expected, chipBounds, currentRotation, targetRotation)
}
@@ -157,10 +165,10 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
val dotWidth = 10
val isRtl = false
val contentRect =
- Rect(/* left = */ 0, /* top = */ 10, /* right = */ 1000, /* bottom = */ 100)
+ Rect(/* left = */ 0, /* top = */ 10, /* right = */ 1000, /* bottom = */ 100)
val chipBounds =
- getPrivacyChipBoundingRectForInsets(contentRect, dotWidth, chipWidth, isRtl)
+ getPrivacyChipBoundingRectForInsets(contentRect, dotWidth, chipWidth, isRtl)
assertThat(chipBounds.top).isEqualTo(contentRect.top)
}
@@ -184,12 +192,11 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
// THEN rotations which share a short side should use the greater value between rounded
// corner padding and the display cutout's size
var targetRotation = ROTATION_NONE
- var expectedBounds = Rect(dcBounds.right,
- 0,
- screenBounds.right - minRightPadding,
- sbHeightPortrait)
+ var expectedBounds =
+ Rect(dcBounds.right, 0, screenBounds.right - minRightPadding, sbHeightPortrait)
- var bounds = calculateInsetsForRotationWithRotatedResources(
+ var bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -200,17 +207,17 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
targetRotation = ROTATION_LANDSCAPE
- expectedBounds = Rect(dcBounds.height(),
- 0,
- screenBounds.height() - minRightPadding,
- sbHeightLandscape)
+ expectedBounds =
+ Rect(dcBounds.height(), 0, screenBounds.height() - minRightPadding, sbHeightLandscape)
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -221,19 +228,19 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
// THEN the side that does NOT share a short side with the display cutout ignores the
// display cutout bounds
targetRotation = ROTATION_UPSIDE_DOWN
- expectedBounds = Rect(minLeftPadding,
- 0,
- screenBounds.width() - minRightPadding,
- sbHeightPortrait)
+ expectedBounds =
+ Rect(minLeftPadding, 0, screenBounds.width() - minRightPadding, sbHeightPortrait)
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -244,18 +251,23 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
// Phone in portrait, seascape (rot_270) bounds
targetRotation = ROTATION_SEASCAPE
- expectedBounds = Rect(minLeftPadding,
+ expectedBounds =
+ Rect(
+ minLeftPadding,
0,
screenBounds.height() - dcBounds.height() - dotWidth,
- sbHeightLandscape)
+ sbHeightLandscape
+ )
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -266,7 +278,8 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
}
@@ -292,12 +305,11 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
// THEN rotations which share a short side should use the greater value between rounded
// corner padding, the display cutout's size, and the camera protections' size.
var targetRotation = ROTATION_NONE
- var expectedBounds = Rect(protectionBounds.right,
- 0,
- screenBounds.right - minRightPadding,
- sbHeightPortrait)
+ var expectedBounds =
+ Rect(protectionBounds.right, 0, screenBounds.right - minRightPadding, sbHeightPortrait)
- var bounds = calculateInsetsForRotationWithRotatedResources(
+ var bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -308,17 +320,22 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
targetRotation = ROTATION_LANDSCAPE
- expectedBounds = Rect(protectionBounds.bottom,
+ expectedBounds =
+ Rect(
+ protectionBounds.bottom,
0,
screenBounds.height() - minRightPadding,
- sbHeightLandscape)
+ sbHeightLandscape
+ )
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -329,19 +346,19 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
// THEN the side that does NOT share a short side with the display cutout ignores the
// display cutout bounds
targetRotation = ROTATION_UPSIDE_DOWN
- expectedBounds = Rect(minLeftPadding,
- 0,
- screenBounds.width() - minRightPadding,
- sbHeightPortrait)
+ expectedBounds =
+ Rect(minLeftPadding, 0, screenBounds.width() - minRightPadding, sbHeightPortrait)
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -352,18 +369,23 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
// Phone in portrait, seascape (rot_270) bounds
targetRotation = ROTATION_SEASCAPE
- expectedBounds = Rect(minLeftPadding,
+ expectedBounds =
+ Rect(
+ minLeftPadding,
0,
screenBounds.height() - protectionBounds.bottom - dotWidth,
- sbHeightLandscape)
+ sbHeightLandscape
+ )
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -374,7 +396,8 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
}
@@ -428,7 +451,8 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
bottom = sbHeightPortrait
)
- var bounds = calculateInsetsForRotationWithRotatedResources(
+ var bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -439,7 +463,8 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -453,7 +478,8 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
bottom = sbHeightLandscape
)
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -464,7 +490,8 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -478,7 +505,8 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
bottom = sbHeightPortrait
)
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -489,7 +517,8 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -503,7 +532,8 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
bottom = sbHeightLandscape
)
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -514,7 +544,8 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
}
@@ -541,7 +572,8 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
var expectedBounds =
Rect(minLeftPadding, 0, protectionBounds.left - dotWidth, sbHeightPortrait)
- var bounds = calculateInsetsForRotationWithRotatedResources(
+ var bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -552,17 +584,22 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
targetRotation = ROTATION_LANDSCAPE
- expectedBounds = Rect(protectionBounds.bottom,
+ expectedBounds =
+ Rect(
+ protectionBounds.bottom,
0,
screenBounds.height() - minRightPadding,
- sbHeightLandscape)
+ sbHeightLandscape
+ )
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -573,19 +610,19 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
// THEN the side that does NOT share a short side with the display cutout ignores the
// display cutout bounds
targetRotation = ROTATION_UPSIDE_DOWN
- expectedBounds = Rect(minLeftPadding,
- 0,
- screenBounds.width() - minRightPadding,
- sbHeightPortrait)
+ expectedBounds =
+ Rect(minLeftPadding, 0, screenBounds.width() - minRightPadding, sbHeightPortrait)
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -596,18 +633,23 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
// Phone in portrait, seascape (rot_270) bounds
targetRotation = ROTATION_SEASCAPE
- expectedBounds = Rect(minLeftPadding,
+ expectedBounds =
+ Rect(
+ minLeftPadding,
0,
screenBounds.height() - protectionBounds.bottom - dotWidth,
- sbHeightLandscape)
+ sbHeightLandscape
+ )
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -618,7 +660,8 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
}
@@ -627,7 +670,8 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
fun calculateInsetsForRotationWithRotatedResources_bottomAlignedMarginDisabled_noTopInset() {
setNoCutout()
- val bounds = calculateInsetsForRotationWithRotatedResources(
+ val bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation = ROTATION_NONE,
targetRotation = ROTATION_NONE,
sysUICutout = sysUICutout,
@@ -638,7 +682,8 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl = false,
dotWidth = 10,
bottomAlignedMargin = BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight = 15)
+ statusBarContentHeight = 15
+ )
assertThat(bounds.top).isEqualTo(0)
}
@@ -647,7 +692,8 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
fun calculateInsetsForRotationWithRotatedResources_bottomAlignedMargin_topBasedOnMargin() {
whenever(dc.boundingRects).thenReturn(emptyList())
- val bounds = calculateInsetsForRotationWithRotatedResources(
+ val bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation = ROTATION_NONE,
targetRotation = ROTATION_NONE,
sysUICutout = sysUICutout,
@@ -658,7 +704,8 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl = false,
dotWidth = 10,
bottomAlignedMargin = 5,
- statusBarContentHeight = 15)
+ statusBarContentHeight = 15
+ )
// Content in the status bar is centered vertically. To achieve the bottom margin we want,
// we need to "shrink" the height of the status bar until the centered content has the
@@ -694,12 +741,11 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
// THEN only the landscape/seascape rotations should avoid the cutout area because of the
// potential letterboxing
var targetRotation = ROTATION_NONE
- var expectedBounds = Rect(minLeftPadding,
- 0,
- screenBounds.right - minRightPadding,
- sbHeightPortrait)
+ var expectedBounds =
+ Rect(minLeftPadding, 0, screenBounds.right - minRightPadding, sbHeightPortrait)
- var bounds = calculateInsetsForRotationWithRotatedResources(
+ var bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout = sysUICutout,
@@ -710,17 +756,17 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
targetRotation = ROTATION_LANDSCAPE
- expectedBounds = Rect(dcBounds.height(),
- 0,
- screenBounds.height() - minRightPadding,
- sbHeightLandscape)
+ expectedBounds =
+ Rect(dcBounds.height(), 0, screenBounds.height() - minRightPadding, sbHeightLandscape)
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout = sysUICutout,
@@ -731,17 +777,17 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
targetRotation = ROTATION_UPSIDE_DOWN
- expectedBounds = Rect(minLeftPadding,
- 0,
- screenBounds.right - minRightPadding,
- sbHeightPortrait)
+ expectedBounds =
+ Rect(minLeftPadding, 0, screenBounds.right - minRightPadding, sbHeightPortrait)
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout = sysUICutout,
@@ -752,17 +798,22 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
targetRotation = ROTATION_SEASCAPE
- expectedBounds = Rect(minLeftPadding,
+ expectedBounds =
+ Rect(
+ minLeftPadding,
0,
screenBounds.height() - dcBounds.height() - dotWidth,
- sbHeightLandscape)
+ sbHeightLandscape
+ )
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout = sysUICutout,
@@ -773,7 +824,8 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
}
@@ -793,12 +845,11 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
// THEN content insets should only use rounded corner padding
var targetRotation = ROTATION_NONE
- var expectedBounds = Rect(minLeftPadding,
- 0,
- screenBounds.right - minRightPadding,
- sbHeightPortrait)
+ var expectedBounds =
+ Rect(minLeftPadding, 0, screenBounds.right - minRightPadding, sbHeightPortrait)
- var bounds = calculateInsetsForRotationWithRotatedResources(
+ var bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
null, /* no cutout */
@@ -809,16 +860,16 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
targetRotation = ROTATION_LANDSCAPE
- expectedBounds = Rect(minLeftPadding,
- 0,
- screenBounds.height() - minRightPadding,
- sbHeightLandscape)
+ expectedBounds =
+ Rect(minLeftPadding, 0, screenBounds.height() - minRightPadding, sbHeightLandscape)
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
null, /* no cutout */
@@ -829,16 +880,16 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
targetRotation = ROTATION_UPSIDE_DOWN
- expectedBounds = Rect(minLeftPadding,
- 0,
- screenBounds.width() - minRightPadding,
- sbHeightPortrait)
+ expectedBounds =
+ Rect(minLeftPadding, 0, screenBounds.width() - minRightPadding, sbHeightPortrait)
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
null, /* no cutout */
@@ -849,16 +900,16 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
targetRotation = ROTATION_LANDSCAPE
- expectedBounds = Rect(minLeftPadding,
- 0,
- screenBounds.height() - minRightPadding,
- sbHeightLandscape)
+ expectedBounds =
+ Rect(minLeftPadding, 0, screenBounds.height() - minRightPadding, sbHeightLandscape)
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
null, /* no cutout */
@@ -869,7 +920,8 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
}
@@ -891,12 +943,11 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
// THEN left should be set to the display cutout width, and right should use the minRight
val targetRotation = ROTATION_NONE
- val expectedBounds = Rect(dcBounds.right,
- 0,
- screenBounds.right - minRightPadding,
- sbHeightPortrait)
+ val expectedBounds =
+ Rect(dcBounds.right, 0, screenBounds.right - minRightPadding, sbHeightPortrait)
- val bounds = calculateInsetsForRotationWithRotatedResources(
+ val bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -907,7 +958,8 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
}
@@ -915,8 +967,14 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
@Test
fun testDisplayChanged_returnsUpdatedInsets() {
// GIVEN: get insets on the first display and switch to the second display
- val provider = StatusBarContentInsetsProvider(contextMock, configurationController,
- mock<DumpManager>(), mock<CommandRegistry>(), mock<SysUICutoutProvider>())
+ val provider =
+ StatusBarContentInsetsProvider(
+ contextMock,
+ configurationController,
+ mock<DumpManager>(),
+ mock<CommandRegistry>(),
+ mock<SysUICutoutProvider>()
+ )
configuration.windowConfiguration.setMaxBounds(Rect(0, 0, 1080, 2160))
val firstDisplayInsets = provider.getStatusBarContentAreaForRotation(ROTATION_NONE)
@@ -934,12 +992,17 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
fun testDisplayChangedAndReturnedBack_returnsTheSameInsets() {
// GIVEN: get insets on the first display, switch to the second display,
// get insets and switch back
- val provider = StatusBarContentInsetsProvider(contextMock, configurationController,
- mock<DumpManager>(), mock<CommandRegistry>(), mock<SysUICutoutProvider>())
+ val provider =
+ StatusBarContentInsetsProvider(
+ contextMock,
+ configurationController,
+ mock<DumpManager>(),
+ mock<CommandRegistry>(),
+ mock<SysUICutoutProvider>()
+ )
configuration.windowConfiguration.setMaxBounds(Rect(0, 0, 1080, 2160))
- val firstDisplayInsetsFirstCall = provider
- .getStatusBarContentAreaForRotation(ROTATION_NONE)
+ val firstDisplayInsetsFirstCall = provider.getStatusBarContentAreaForRotation(ROTATION_NONE)
configuration.windowConfiguration.setMaxBounds(Rect(0, 0, 800, 600))
provider.getStatusBarContentAreaForRotation(ROTATION_NONE)
@@ -947,8 +1010,8 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
configuration.windowConfiguration.setMaxBounds(Rect(0, 0, 1080, 2160))
// WHEN: get insets on the first display again
- val firstDisplayInsetsSecondCall = provider
- .getStatusBarContentAreaForRotation(ROTATION_NONE)
+ val firstDisplayInsetsSecondCall =
+ provider.getStatusBarContentAreaForRotation(ROTATION_NONE)
// THEN: insets for the first and second calls for the first display are the same
assertThat(firstDisplayInsetsFirstCall).isEqualTo(firstDisplayInsetsSecondCall)
@@ -960,15 +1023,22 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
// Start out with an existing configuration with bounds
configuration.windowConfiguration.setMaxBounds(0, 0, 100, 100)
configurationController.onConfigurationChanged(configuration)
- val provider = StatusBarContentInsetsProvider(contextMock, configurationController,
- mock<DumpManager>(), mock<CommandRegistry>(), mock<SysUICutoutProvider>())
- val listener = object : StatusBarContentInsetsChangedListener {
- var triggered = false
+ val provider =
+ StatusBarContentInsetsProvider(
+ contextMock,
+ configurationController,
+ mock<DumpManager>(),
+ mock<CommandRegistry>(),
+ mock<SysUICutoutProvider>()
+ )
+ val listener =
+ object : StatusBarContentInsetsChangedListener {
+ var triggered = false
- override fun onStatusBarContentInsetsChanged() {
- triggered = true
+ override fun onStatusBarContentInsetsChanged() {
+ triggered = true
+ }
}
- }
provider.addCallback(listener)
// WHEN the config is updated with new bounds
@@ -982,15 +1052,22 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
@Test
fun onDensityOrFontScaleChanged_listenerNotified() {
configuration.densityDpi = 12
- val provider = StatusBarContentInsetsProvider(contextMock, configurationController,
- mock<DumpManager>(), mock<CommandRegistry>(), mock<SysUICutoutProvider>())
- val listener = object : StatusBarContentInsetsChangedListener {
- var triggered = false
+ val provider =
+ StatusBarContentInsetsProvider(
+ contextMock,
+ configurationController,
+ mock<DumpManager>(),
+ mock<CommandRegistry>(),
+ mock<SysUICutoutProvider>()
+ )
+ val listener =
+ object : StatusBarContentInsetsChangedListener {
+ var triggered = false
- override fun onStatusBarContentInsetsChanged() {
- triggered = true
+ override fun onStatusBarContentInsetsChanged() {
+ triggered = true
+ }
}
- }
provider.addCallback(listener)
// WHEN the config is updated
@@ -1003,15 +1080,22 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
@Test
fun onThemeChanged_listenerNotified() {
- val provider = StatusBarContentInsetsProvider(contextMock, configurationController,
- mock<DumpManager>(), mock<CommandRegistry>(), mock<SysUICutoutProvider>())
- val listener = object : StatusBarContentInsetsChangedListener {
- var triggered = false
+ val provider =
+ StatusBarContentInsetsProvider(
+ contextMock,
+ configurationController,
+ mock<DumpManager>(),
+ mock<CommandRegistry>(),
+ mock<SysUICutoutProvider>()
+ )
+ val listener =
+ object : StatusBarContentInsetsChangedListener {
+ var triggered = false
- override fun onStatusBarContentInsetsChanged() {
- triggered = true
+ override fun onStatusBarContentInsetsChanged() {
+ triggered = true
+ }
}
- }
provider.addCallback(listener)
configurationController.notifyThemeChanged()
@@ -1027,7 +1111,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
@Rotation targetRotation: Int
) {
assertTrue(
- "Rects must match. currentRotation=${RotationUtils.toString(currentRotation)}" +
+ "Rects must match. currentRotation=${RotationUtils.toString(currentRotation)}" +
" targetRotation=${RotationUtils.toString(targetRotation)}" +
" expected=$expected actual=$actual",
expected.equals(actual)
@@ -1054,7 +1138,7 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() {
private fun setCameraProtectionBounds(protectionBounds: Rect) {
val protectionInfo =
- mock<CameraProtectionInfo> { whenever(this.cutoutBounds).thenReturn(protectionBounds) }
+ mock<CameraProtectionInfo> { whenever(this.bounds).thenReturn(protectionBounds) }
whenever(sysUICutout.cameraProtection).thenReturn(protectionInfo)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
index 4c824c0d130a..87d25ddcc75c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
@@ -46,12 +46,12 @@ import android.content.Intent;
import android.graphics.Region;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.testing.UiEventLoggerFake;
@@ -75,8 +75,8 @@ import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
+@RunWith(AndroidJUnit4.class)
public class BaseHeadsUpManagerTest extends SysuiTestCase {
@Rule
public MockitoRule rule = MockitoJUnit.rule();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/OWNERS b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/OWNERS
new file mode 100644
index 000000000000..1f07df9f1e8e
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/OWNERS
@@ -0,0 +1 @@
+include /packages/SystemUI/src/com/android/systemui/volume/OWNERS \ No newline at end of file
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/domain/interactor/AudioModeInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioModeInteractorTest.kt
index 4dbf865475a4..fe34361540e1 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/domain/interactor/AudioModeInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioModeInteractorTest.kt
@@ -14,13 +14,14 @@
* limitations under the License.
*/
-package com.android.settingslib.volume.domain.interactor
+package com.android.systemui.volume.domain.interactor
import android.media.AudioManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.settingslib.BaseTest
-import com.android.settingslib.volume.data.repository.FakeAudioRepository
+import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.volume.data.repository.FakeAudioRepository
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
@@ -34,7 +35,7 @@ import org.junit.runner.RunWith
@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
@SmallTest
-class AudioModeInteractorTest : BaseTest() {
+class AudioModeInteractorTest : SysuiTestCase() {
private val testScope = TestScope()
private val fakeAudioRepository = FakeAudioRepository()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioVolumeInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioVolumeInteractorTest.kt
new file mode 100644
index 000000000000..a2f3ccb8c416
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioVolumeInteractorTest.kt
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2024 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.volume.domain.interactor
+
+import android.media.AudioManager
+import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.statusbar.notification.data.model.ZenMode
+import com.android.settingslib.statusbar.notification.data.repository.updateNotificationPolicy
+import com.android.settingslib.volume.domain.interactor.AudioVolumeInteractor
+import com.android.settingslib.volume.shared.model.AudioStream
+import com.android.settingslib.volume.shared.model.RingerMode
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.notification.domain.interactor.notificationsSoundPolicyInteractor
+import com.android.systemui.statusbar.notification.domain.interactor.notificationsSoundPolicyRepository
+import com.android.systemui.testKosmos
+import com.android.systemui.volume.audioRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class AudioVolumeInteractorTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+
+ private lateinit var underTest: AudioVolumeInteractor
+
+ @Before
+ fun setup() {
+ with(kosmos) {
+ underTest = AudioVolumeInteractor(audioRepository, notificationsSoundPolicyInteractor)
+
+ audioRepository.setRingerMode(RingerMode(AudioManager.RINGER_MODE_NORMAL))
+
+ notificationsSoundPolicyRepository.updateNotificationPolicy()
+ notificationsSoundPolicyRepository.updateZenMode(ZenMode(Settings.Global.ZEN_MODE_OFF))
+ }
+ }
+
+ @Test
+ fun setMuted_mutesStream() {
+ with(kosmos) {
+ testScope.runTest {
+ val model by collectLastValue(underTest.getAudioStream(audioStream))
+
+ underTest.setMuted(audioStream, false)
+ runCurrent()
+ assertThat(model!!.isMuted).isFalse()
+
+ underTest.setMuted(audioStream, true)
+ runCurrent()
+ assertThat(model!!.isMuted).isTrue()
+ }
+ }
+ }
+
+ @Test
+ fun setVolume_changesVolume() {
+ with(kosmos) {
+ testScope.runTest {
+ val model by collectLastValue(underTest.getAudioStream(audioStream))
+
+ underTest.setVolume(audioStream, 10)
+ runCurrent()
+ assertThat(model!!.volume).isEqualTo(10)
+
+ underTest.setVolume(audioStream, 20)
+ runCurrent()
+ assertThat(model!!.volume).isEqualTo(20)
+ }
+ }
+ }
+
+ @Test
+ fun ringMuted_notificationVolume_cantChange() {
+ with(kosmos) {
+ testScope.runTest {
+ val canChangeVolume by
+ collectLastValue(
+ underTest.canChangeVolume(AudioStream(AudioManager.STREAM_NOTIFICATION))
+ )
+
+ underTest.setMuted(AudioStream(AudioManager.STREAM_RING), true)
+ runCurrent()
+
+ assertThat(canChangeVolume).isFalse()
+ }
+ }
+ }
+
+ @Test
+ fun streamIsMuted_getStream_volumeZero() {
+ with(kosmos) {
+ testScope.runTest {
+ val model by collectLastValue(underTest.getAudioStream(audioStream))
+
+ underTest.setMuted(audioStream, true)
+ runCurrent()
+
+ assertThat(model!!.volume).isEqualTo(0)
+ }
+ }
+ }
+
+ @Test
+ fun streamIsZenMuted_getStream_lastAudibleVolume() {
+ with(kosmos) {
+ testScope.runTest {
+ audioRepository.setLastAudibleVolume(audioStream, 30)
+ notificationsSoundPolicyRepository.updateZenMode(
+ ZenMode(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS)
+ )
+
+ val model by collectLastValue(underTest.getAudioStream(audioStream))
+ runCurrent()
+
+ assertThat(model!!.volume).isEqualTo(30)
+ }
+ }
+ }
+
+ @Test
+ fun ringerModeVibrateAndMuted_getNotificationStream_volumeIsZero() {
+ with(kosmos) {
+ testScope.runTest {
+ audioRepository.setRingerMode(RingerMode(AudioManager.RINGER_MODE_VIBRATE))
+ underTest.setMuted(AudioStream(AudioManager.STREAM_NOTIFICATION), true)
+
+ val model by
+ collectLastValue(
+ underTest.getAudioStream(AudioStream(AudioManager.STREAM_NOTIFICATION))
+ )
+ runCurrent()
+
+ assertThat(model!!.volume).isEqualTo(0)
+ }
+ }
+ }
+
+ @Test
+ fun ringerModeVibrate_getRingerStream_volumeIsZero() {
+ with(kosmos) {
+ testScope.runTest {
+ audioRepository.setRingerMode(RingerMode(AudioManager.RINGER_MODE_VIBRATE))
+
+ val model by
+ collectLastValue(
+ underTest.getAudioStream(AudioStream(AudioManager.STREAM_RING))
+ )
+ runCurrent()
+
+ assertThat(model!!.volume).isEqualTo(0)
+ }
+ }
+ }
+
+ private companion object {
+ val audioStream = AudioStream(AudioManager.STREAM_SYSTEM)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepositoryTest.kt
new file mode 100644
index 000000000000..e31cdcd82e7e
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepositoryTest.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.anc.data.repository
+
+import android.bluetooth.BluetoothDevice
+import android.net.Uri
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.media.BluetoothMediaDevice
+import com.android.settingslib.media.MediaDevice
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.volume.localMediaRepository
+import com.android.systemui.volume.localMediaRepositoryFactory
+import com.android.systemui.volume.panel.component.anc.FakeSliceFactory
+import com.android.systemui.volume.panel.component.anc.sliceViewManager
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AncSliceRepositoryTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+
+ private lateinit var underTest: AncSliceRepository
+
+ @Before
+ fun setup() {
+ with(kosmos) {
+ val slice = FakeSliceFactory.createSlice(hasError = false, hasSliceItem = true)
+ whenever(sliceViewManager.bindSlice(any<Uri>())).thenReturn(slice)
+
+ underTest =
+ AncSliceRepositoryImpl(
+ localMediaRepositoryFactory,
+ testScope.testScheduler,
+ sliceViewManager,
+ )
+ }
+ }
+
+ @Test
+ fun noConnectedDevice_noSlice() {
+ with(kosmos) {
+ testScope.runTest {
+ localMediaRepository.updateCurrentConnectedDevice(null)
+
+ val slice by collectLastValue(underTest.ancSlice(1))
+ runCurrent()
+
+ assertThat(slice).isNull()
+ }
+ }
+ }
+
+ @Test
+ fun connectedDevice_sliceReturned() {
+ with(kosmos) {
+ testScope.runTest {
+ localMediaRepository.updateCurrentConnectedDevice(createMediaDevice())
+
+ val slice by collectLastValue(underTest.ancSlice(1))
+ runCurrent()
+
+ assertThat(slice).isNotNull()
+ }
+ }
+ }
+
+ private fun createMediaDevice(sliceUri: String = "content://test.slice"): MediaDevice {
+ val bluetoothDevice: BluetoothDevice = mock {
+ whenever(getMetadata(any()))
+ .thenReturn(
+ ("<HEARABLE_CONTROL_SLICE_WITH_WIDTH>" +
+ sliceUri +
+ "</HEARABLE_CONTROL_SLICE_WITH_WIDTH>")
+ .toByteArray()
+ )
+ }
+ val cachedBluetoothDevice: CachedBluetoothDevice = mock {
+ whenever(device).thenReturn(bluetoothDevice)
+ }
+ return mock<BluetoothMediaDevice> {
+ whenever(cachedDevice).thenReturn(cachedBluetoothDevice)
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/AncAvailabilityCriteriaTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/AncAvailabilityCriteriaTest.kt
new file mode 100644
index 000000000000..553aed8cfb05
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/AncAvailabilityCriteriaTest.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.anc.domain
+
+import android.net.Uri
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.volume.panel.component.anc.FakeSliceFactory
+import com.android.systemui.volume.panel.component.anc.ancSliceInteractor
+import com.android.systemui.volume.panel.component.anc.ancSliceRepository
+import com.android.systemui.volume.panel.component.anc.sliceViewManager
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AncAvailabilityCriteriaTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+
+ private lateinit var underTest: AncAvailabilityCriteria
+
+ @Before
+ fun setup() {
+ with(kosmos) {
+ whenever(sliceViewManager.bindSlice(any<Uri>())).thenReturn(mock {})
+
+ underTest = AncAvailabilityCriteria(ancSliceInteractor)
+ }
+ }
+
+ @Test
+ fun noSlice_unavailable() {
+ with(kosmos) {
+ testScope.runTest {
+ ancSliceRepository.putSlice(1, null)
+
+ val isAvailable by collectLastValue(underTest.isAvailable())
+ runCurrent()
+
+ assertThat(isAvailable).isFalse()
+ }
+ }
+ }
+
+ @Test
+ fun hasSlice_available() {
+ with(kosmos) {
+ testScope.runTest {
+ ancSliceRepository.putSlice(
+ 1,
+ FakeSliceFactory.createSlice(hasError = false, hasSliceItem = true)
+ )
+
+ val isAvailable by collectLastValue(underTest.isAvailable())
+ runCurrent()
+
+ assertThat(isAvailable).isTrue()
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractorTest.kt
new file mode 100644
index 000000000000..53f0bc9ddb51
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractorTest.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.anc.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.volume.panel.component.anc.FakeSliceFactory
+import com.android.systemui.volume.panel.component.anc.ancSliceRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AncSliceInteractorTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+
+ private lateinit var underTest: AncSliceInteractor
+
+ @Before
+ fun setup() {
+ with(kosmos) {
+ underTest = AncSliceInteractor(ancSliceRepository, testScope.backgroundScope)
+ }
+ }
+
+ @Test
+ fun errorSlice_returnsNull() {
+ with(kosmos) {
+ testScope.runTest {
+ ancSliceRepository.putSlice(
+ 1,
+ FakeSliceFactory.createSlice(hasError = true, hasSliceItem = true)
+ )
+
+ val slice by collectLastValue(underTest.ancSlice)
+ runCurrent()
+
+ assertThat(slice).isNull()
+ }
+ }
+ }
+
+ @Test
+ fun noSliceItem_returnsNull() {
+ with(kosmos) {
+ testScope.runTest {
+ ancSliceRepository.putSlice(
+ 1,
+ FakeSliceFactory.createSlice(hasError = false, hasSliceItem = false)
+ )
+
+ val slice by collectLastValue(underTest.ancSlice)
+ runCurrent()
+
+ assertThat(slice).isNull()
+ }
+ }
+ }
+
+ @Test
+ fun sliceItem_noError_returnsSlice() {
+ with(kosmos) {
+ testScope.runTest {
+ ancSliceRepository.putSlice(
+ 1,
+ FakeSliceFactory.createSlice(hasError = false, hasSliceItem = true)
+ )
+
+ val slice by collectLastValue(underTest.ancSlice)
+ runCurrent()
+
+ assertThat(slice).isNotNull()
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteriaTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteriaTest.kt
new file mode 100644
index 000000000000..ec55c75d4ae5
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteriaTest.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.mediaoutput.domain
+
+import android.media.AudioManager
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.volume.audioModeInteractor
+import com.android.systemui.volume.audioRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@RunWithLooper(setAsMainLooper = true)
+class MediaOutputAvailabilityCriteriaTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+
+ private lateinit var underTest: MediaOutputAvailabilityCriteria
+
+ @Before
+ fun setup() {
+ underTest = MediaOutputAvailabilityCriteria(kosmos.audioModeInteractor)
+ }
+
+ @Test
+ fun notInCall_isAvailable_true() {
+ with(kosmos) {
+ testScope.runTest {
+ audioRepository.setMode(AudioManager.MODE_NORMAL)
+
+ val isAvailable by collectLastValue(underTest.isAvailable())
+ runCurrent()
+
+ assertThat(isAvailable).isTrue()
+ }
+ }
+ }
+
+ @Test
+ fun inCall_isAvailable_false() {
+ with(kosmos) {
+ testScope.runTest {
+ audioRepository.setMode(AudioManager.MODE_IN_CALL)
+
+ val isAvailable by collectLastValue(underTest.isAvailable())
+ runCurrent()
+
+ assertThat(isAvailable).isFalse()
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt
new file mode 100644
index 000000000000..243aab24b07d
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.mediaoutput.ui.viewmodel
+
+import android.content.applicationContext
+import android.media.session.MediaSession
+import android.media.session.PlaybackState
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.res.R
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.volume.localMediaRepository
+import com.android.systemui.volume.mediaController
+import com.android.systemui.volume.mediaControllerRepository
+import com.android.systemui.volume.mediaOutputInteractor
+import com.android.systemui.volume.panel.mediaOutputActionsInteractor
+import com.android.systemui.volume.panel.volumePanelViewModel
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class MediaOutputViewModelTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val playbackStateBuilder = PlaybackState.Builder()
+
+ private lateinit var underTest: MediaOutputViewModel
+
+ @Before
+ fun setup() {
+ with(kosmos) {
+ underTest =
+ MediaOutputViewModel(
+ applicationContext,
+ testScope.backgroundScope,
+ volumePanelViewModel,
+ mediaOutputActionsInteractor,
+ mediaOutputInteractor,
+ )
+
+ with(context.orCreateTestableResources) {
+ addOverride(R.string.media_output_label_title, "media_output_label_title")
+ addOverride(
+ R.string.media_output_title_without_playing,
+ "media_output_title_without_playing"
+ )
+ }
+
+ whenever(mediaController.packageName).thenReturn("test.pkg")
+ whenever(mediaController.sessionToken).thenReturn(MediaSession.Token(0, mock {}))
+ whenever(mediaController.playbackState).then { playbackStateBuilder.build() }
+
+ mediaControllerRepository.setActiveLocalMediaController(mediaController)
+ }
+ }
+
+ @Test
+ fun playingSession_connectedDeviceViewMode_hasTheDevice() {
+ with(kosmos) {
+ testScope.runTest {
+ playbackStateBuilder.setState(PlaybackState.STATE_PLAYING, 0, 0f)
+ localMediaRepository.updateCurrentConnectedDevice(
+ mock { whenever(name).thenReturn("test_device") }
+ )
+
+ val connectedDeviceViewModel by collectLastValue(underTest.connectedDeviceViewModel)
+ runCurrent()
+
+ assertThat(connectedDeviceViewModel!!.label).isEqualTo("media_output_label_title")
+ assertThat(connectedDeviceViewModel!!.deviceName).isEqualTo("test_device")
+ }
+ }
+ }
+
+ @Test
+ fun notPlaying_connectedDeviceViewMode_hasTheDevice() {
+ with(kosmos) {
+ testScope.runTest {
+ playbackStateBuilder.setState(PlaybackState.STATE_STOPPED, 0, 0f)
+ localMediaRepository.updateCurrentConnectedDevice(
+ mock { whenever(name).thenReturn("test_device") }
+ )
+
+ val connectedDeviceViewModel by collectLastValue(underTest.connectedDeviceViewModel)
+ runCurrent()
+
+ assertThat(connectedDeviceViewModel!!.label)
+ .isEqualTo("media_output_title_without_playing")
+ assertThat(connectedDeviceViewModel!!.deviceName).isEqualTo("test_device")
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/VolumeSliderInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/VolumeSliderInteractorTest.kt
new file mode 100644
index 000000000000..a1e4fcafd3a4
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/VolumeSliderInteractorTest.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.volume.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class VolumeSliderInteractorTest : SysuiTestCase() {
+
+ private val underTest = VolumeSliderInteractor()
+
+ @Test
+ fun translateValueToVolume() {
+ assertThat(underTest.translateValueToVolume(30f, volumeRange)).isEqualTo(3)
+ }
+
+ @Test
+ fun processVolumeToValue_muted_zero() {
+ assertThat(underTest.processVolumeToValue(3, volumeRange, null, true)).isEqualTo(0)
+ }
+
+ @Test
+ fun processVolumeToValue_currentValue_currentValue() {
+ assertThat(underTest.processVolumeToValue(3, volumeRange, 30f, false)).isEqualTo(30f)
+ }
+
+ @Test
+ fun processVolumeToValue_currentValueDiffersVolume_returnsTranslatedVolume() {
+ assertThat(underTest.processVolumeToValue(1, volumeRange, 60f, false)).isEqualTo(10f)
+ }
+
+ @Test
+ fun processVolumeToValue_currentValueDiffersNotEnoughVolume_returnsTranslatedVolume() {
+ assertThat(underTest.processVolumeToValue(1, volumeRange, 12f, false)).isEqualTo(12f)
+ }
+
+ private companion object {
+ val volumeRange = 0..10
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/DefaultComponentsLayoutManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/DefaultComponentsLayoutManagerTest.kt
index 7c993603b810..71866b3957b6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/DefaultComponentsLayoutManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/DefaultComponentsLayoutManagerTest.kt
@@ -50,7 +50,7 @@ class DefaultComponentsLayoutManagerTest : SysuiTestCase() {
val component4 = ComponentState(COMPONENT_4, kosmos.mockVolumePanelUiComponent, false)
val layout =
underTest.layout(
- VolumePanelState(0, false),
+ VolumePanelState(0, false, false),
setOf(bottomBarComponentState, component1, component2, component3, component4)
)
@@ -71,7 +71,7 @@ class DefaultComponentsLayoutManagerTest : SysuiTestCase() {
val component1State = ComponentState(COMPONENT_1, kosmos.mockVolumePanelUiComponent, false)
val component2State = ComponentState(COMPONENT_2, kosmos.mockVolumePanelUiComponent, false)
underTest.layout(
- VolumePanelState(0, false),
+ VolumePanelState(0, false, false),
setOf(
component1State,
component2State,
diff --git a/packages/SystemUI/plugin/proguard_plugins.flags b/packages/SystemUI/plugin/proguard_plugins.flags
index abac27f0cbe6..23ba8d066bb2 100644
--- a/packages/SystemUI/plugin/proguard_plugins.flags
+++ b/packages/SystemUI/plugin/proguard_plugins.flags
@@ -7,3 +7,13 @@
-keep class com.android.systemui.log.core.** {
*;
}
+
+# This type is used in the plugin API boundary, so ensure the used public methods are kept.
+-keepclassmembers class androidx.constraintlayout.widget.ConstraintSet {
+ public void connect(int, int, int, int, int);
+ public void constrainWidth(int, int);
+ public void constrainHeight(int, int);
+ public int getHeight(int);
+ public int getWidth(int);
+ public void setGoneMargin(int, int, int);
+}
diff --git a/packages/SystemUI/proguard_common.flags b/packages/SystemUI/proguard_common.flags
index f9546c46e915..162d8aebfc62 100644
--- a/packages/SystemUI/proguard_common.flags
+++ b/packages/SystemUI/proguard_common.flags
@@ -17,10 +17,6 @@
<1> *;
}
--keepclasseswithmembers class * {
- public <init>(android.content.Context, android.util.AttributeSet);
-}
-
-keep class androidx.core.app.CoreComponentFactory
# Keep the wm shell lib
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml b/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
index f4d34f4ca141..8a0dd125d88b 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
@@ -27,7 +27,7 @@
<com.android.keyguard.KeyguardStatusView
android:id="@+id/clock"
android:orientation="vertical"
- android:layout_width="410dp"
+ android:layout_width="@dimen/keyguard_presentation_width"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 4e540de245dd..186bd7cc48c5 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -173,4 +173,6 @@
<dimen name="sfps_progress_bar_thickness">6dp</dimen>
<!-- Padding from the edge of the screen for the progress bar -->
<dimen name="sfps_progress_bar_padding_from_edge">7dp</dimen>
+
+ <dimen name="keyguard_presentation_width">410dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/drawable/ic_call.xml b/packages/SystemUI/res/drawable/ic_call.xml
new file mode 100644
index 000000000000..859506ad5e7c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_call.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2024 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="24dp"
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M798,840Q673,840 551,785.5Q429,731 329,631Q229,531 174.5,409Q120,287 120,162Q120,144 132,132Q144,120 162,120L324,120Q338,120 349,129.5Q360,139 362,152L388,292Q390,308 387,319Q384,330 376,338L279,436Q299,473 326.5,507.5Q354,542 387,574Q418,605 452,631.5Q486,658 524,680L618,586Q627,577 641.5,572.5Q656,568 670,570L808,598Q822,602 831,612.5Q840,623 840,636L840,798Q840,816 828,828Q816,840 798,840ZM241,360L307,294Q307,294 307,294Q307,294 307,294L290,200Q290,200 290,200Q290,200 290,200L201,200Q201,200 201,200Q201,200 201,200Q206,241 215,281Q224,321 241,360ZM599,718Q638,735 678.5,745Q719,755 760,758Q760,758 760,758Q760,758 760,758L760,670Q760,670 760,670Q760,670 760,670L666,651Q666,651 666,651Q666,651 666,651L599,718ZM241,360Q241,360 241,360Q241,360 241,360Q241,360 241,360Q241,360 241,360L241,360Q241,360 241,360Q241,360 241,360L241,360Q241,360 241,360Q241,360 241,360L241,360ZM599,718L599,718Q599,718 599,718Q599,718 599,718L599,718Q599,718 599,718Q599,718 599,718L599,718Q599,718 599,718Q599,718 599,718Q599,718 599,718Q599,718 599,718Z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_filled_arrow_down.xml b/packages/SystemUI/res/drawable/ic_filled_arrow_down.xml
new file mode 100644
index 000000000000..c85965fd0e58
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_filled_arrow_down.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 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:viewportWidth="24"
+ android:viewportHeight="24"
+ android:width="24dp"
+ android:height="24dp">
+ <path
+ android:pathData="M7 10l5 5 5 -5z"
+ android:fillColor="#FF000000"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_filled_arrow_up.xml b/packages/SystemUI/res/drawable/ic_filled_arrow_up.xml
new file mode 100644
index 000000000000..8ee7e1330531
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_filled_arrow_up.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 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:viewportWidth="24"
+ android:viewportHeight="24"
+ android:width="24dp"
+ android:height="24dp">
+ <path
+ android:pathData="M7 14l5-5 5 5z"
+ android:fillColor="#FF000000"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_music_note_off.xml b/packages/SystemUI/res/drawable/ic_music_note_off.xml
new file mode 100644
index 000000000000..d583576911dd
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_music_note_off.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ Copyright (C) 2024 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="24dp"
+ android:height="24dp"
+ android:viewportHeight="960"
+ android:viewportWidth="960">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M792,904L56,168L112,112L848,848L792,904ZM560,446L480,366L480,120L720,120L720,280L560,280L560,446ZM400,840Q334,840 287,793Q240,746 240,680Q240,614 287,567Q334,520 400,520Q423,520 442.5,525.5Q462,531 480,542L480,480L560,560L560,680Q560,746 513,793Q466,840 400,840Z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_noise_aware.xml b/packages/SystemUI/res/drawable/ic_noise_aware.xml
new file mode 100644
index 000000000000..54826414f3f7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_noise_aware.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2024 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="24dp"
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M440,82Q450,81 460,80.5Q470,80 480,80Q491,80 500.5,80.5Q510,81 520,82L520,162Q510,160 500.5,160Q491,160 480,160Q469,160 459.5,160Q450,160 440,162L440,82ZM272,138Q289,127 306.5,119Q324,111 343,104L378,176Q358,182 340.5,190.5Q323,199 306,210L272,138ZM654,210Q637,199 619.5,190.5Q602,182 582,176L617,104Q636,111 653.5,119Q671,127 688,138L654,210ZM753,311Q742,294 729,278.5Q716,263 702,249L765,199Q779,213 792,228.5Q805,244 816,261L753,311ZM143,263Q154,246 166.5,230.5Q179,215 193,201L256,251Q242,265 229.5,280.5Q217,296 206,313L143,263ZM83,428Q85,408 90,388.5Q95,369 101,350L180,368Q173,387 168.5,406.5Q164,426 162,446L83,428ZM799,449Q797,429 792.5,409Q788,389 781,370L859,352Q865,371 870,390.5Q875,410 877,430L799,449ZM781,590Q788,571 792,552Q796,533 798,513L877,531Q875,551 870,570.5Q865,590 859,609L781,590ZM162,514Q164,534 168.5,553.5Q173,573 180,592L101,610Q95,591 90,571.5Q85,552 83,532L162,514ZM705,708Q719,694 731,678.5Q743,663 754,646L818,696Q807,713 794.5,728.5Q782,744 768,758L705,708ZM194,760Q180,746 167.5,730Q155,714 144,697L206,647Q217,664 229.5,680Q242,696 256,710L194,760ZM583,783Q603,776 620,768Q637,760 654,749L689,821Q672,832 654.5,840.5Q637,849 618,856L583,783ZM344,857Q325,850 307,841.5Q289,833 272,822L307,750Q324,761 341.5,769.5Q359,778 379,784L344,857ZM480,880Q470,880 460,879.5Q450,879 440,878L440,798Q453,800 480,800Q491,800 500.5,800Q510,800 520,798L520,878Q510,879 500.5,879.5Q491,880 480,880ZM520,720Q482,720 450.5,697Q419,674 406,638Q403,629 399.5,620.5Q396,612 389,605L334,550Q308,524 294,490.5Q280,457 280,420Q280,345 332.5,292.5Q385,240 460,240Q529,240 580,285.5Q631,331 639,400L558,400Q551,365 523.5,342.5Q496,320 460,320Q418,320 389,349Q360,378 360,420Q360,440 368,459.5Q376,479 391,494L445,548Q459,562 467.5,578.5Q476,595 482,612Q487,625 497,632.5Q507,640 520,640Q537,640 548.5,628.5Q560,617 560,600L640,600Q640,650 605.5,685Q571,720 520,720ZM540,560Q515,560 497.5,542.5Q480,525 480,500Q480,474 497.5,457Q515,440 540,440Q566,440 583,457Q600,474 600,500Q600,525 583,542.5Q566,560 540,560Z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_volume_off.xml b/packages/SystemUI/res/drawable/ic_volume_off.xml
new file mode 100644
index 000000000000..209f684436ee
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_off.xml
@@ -0,0 +1,27 @@
+<!--
+ ~ Copyright (C) 2024 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="24dp"
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="?attr/colorControlNormal"
+ android:autoMirrored="true">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M792,904L671,783Q646,799 618,810.5Q590,822 560,829L560,747Q574,742 587.5,737Q601,732 613,725L480,592L480,800L280,600L120,600L120,360L248,360L56,168L112,112L848,848L792,904ZM784,672L726,614Q743,583 751.5,549Q760,515 760,479Q760,385 705,311Q650,237 560,211L560,129Q684,157 762,254.5Q840,352 840,479Q840,532 825.5,581Q811,630 784,672ZM650,538L560,448L560,318Q607,340 633.5,384Q660,428 660,480Q660,495 657.5,509.5Q655,524 650,538ZM480,368L376,264L480,160L480,368ZM400,606L400,512L328,440L328,440L200,440L200,520L314,520L400,606ZM364,476L364,476L364,476L364,476L364,476L364,476L364,476L364,476Z"/>
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/media_squiggly_progress.xml b/packages/SystemUI/res/drawable/media_squiggly_progress.xml
index 9cd3f6288b1d..ae797f763b77 100644
--- a/packages/SystemUI/res/drawable/media_squiggly_progress.xml
+++ b/packages/SystemUI/res/drawable/media_squiggly_progress.xml
@@ -14,4 +14,4 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.systemui.media.controls.ui.SquigglyProgress /> \ No newline at end of file
+<com.android.systemui.media.controls.ui.drawable.SquigglyProgress /> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_media_background.xml b/packages/SystemUI/res/drawable/qs_media_background.xml
index 217656dab022..830c882e1010 100644
--- a/packages/SystemUI/res/drawable/qs_media_background.xml
+++ b/packages/SystemUI/res/drawable/qs_media_background.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
-<com.android.systemui.media.controls.ui.IlluminationDrawable
+<com.android.systemui.media.controls.ui.drawable.IlluminationDrawable
xmlns:systemui="http://schemas.android.com/apk/res-auto"
systemui:highlight="15"
systemui:cornerRadius="@dimen/notification_corner_radius" /> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_media_light_source.xml b/packages/SystemUI/res/drawable/qs_media_light_source.xml
index 849349a5f100..0b42dbab6ced 100644
--- a/packages/SystemUI/res/drawable/qs_media_light_source.xml
+++ b/packages/SystemUI/res/drawable/qs_media_light_source.xml
@@ -14,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.systemui.media.controls.ui.LightSourceDrawable
+<com.android.systemui.media.controls.ui.drawable.LightSourceDrawable
xmlns:systemui="http://schemas.android.com/apk/res-auto"
systemui:rippleMinSize="25dp"
systemui:rippleMaxSize="135dp" /> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/anc_slice.xml b/packages/SystemUI/res/layout/anc_slice.xml
new file mode 100644
index 000000000000..71752f217a4f
--- /dev/null
+++ b/packages/SystemUI/res/layout/anc_slice.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 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.
+ -->
+<androidx.slice.widget.SliceView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/slice_view"
+ style="@style/Widget.SliceView.Panel.Slider"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" /> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/media_carousel.xml b/packages/SystemUI/res/layout/media_carousel.xml
index 715c86957e02..825ece856ed2 100644
--- a/packages/SystemUI/res/layout/media_carousel.xml
+++ b/packages/SystemUI/res/layout/media_carousel.xml
@@ -24,7 +24,7 @@
android:clipToPadding="false"
android:forceHasOverlappingRendering="false"
android:theme="@style/MediaPlayer">
- <com.android.systemui.media.controls.ui.MediaScrollView
+ <com.android.systemui.media.controls.ui.view.MediaScrollView
android:id="@+id/media_carousel_scroller"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -42,7 +42,7 @@
>
<!-- QSMediaPlayers will be added here dynamically -->
</LinearLayout>
- </com.android.systemui.media.controls.ui.MediaScrollView>
+ </com.android.systemui.media.controls.ui.view.MediaScrollView>
<com.android.systemui.qs.PageIndicator
android:id="@+id/media_page_indicator"
android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/layout/media_session_view.xml b/packages/SystemUI/res/layout/media_session_view.xml
index 5db9eee6a908..109e63c6167a 100644
--- a/packages/SystemUI/res/layout/media_session_view.xml
+++ b/packages/SystemUI/res/layout/media_session_view.xml
@@ -67,6 +67,18 @@
android:background="@drawable/qs_media_outline_layout_bg"
/>
+ <com.android.systemui.surfaceeffects.loadingeffect.LoadingEffectView
+ android:id="@+id/loading_effect_view"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/qs_media_session_height_expanded"
+ app:layout_constraintStart_toStartOf="@id/album_art"
+ app:layout_constraintEnd_toEndOf="@id/album_art"
+ app:layout_constraintTop_toTopOf="@id/album_art"
+ app:layout_constraintBottom_toBottomOf="@id/album_art"
+ android:clipToOutline="true"
+ android:background="@drawable/qs_media_outline_layout_bg"
+ />
+
<!-- Guideline for output switcher -->
<androidx.constraintlayout.widget.Guideline
android:id="@+id/center_vertical_guideline"
diff --git a/packages/SystemUI/res/layout/scene_window_root.xml b/packages/SystemUI/res/layout/scene_window_root.xml
index 0dcd15b429c1..bb8de4c32e76 100644
--- a/packages/SystemUI/res/layout/scene_window_root.xml
+++ b/packages/SystemUI/res/layout/scene_window_root.xml
@@ -24,7 +24,7 @@
android:id="@+id/scene_window_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:fitsSystemWindows="true">
+ android:fitsSystemWindows="false">
<include layout="@layout/super_notification_shade"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/screen_share_dialog.xml b/packages/SystemUI/res/layout/screen_share_dialog.xml
index 37964158a4aa..2616e8ae25e8 100644
--- a/packages/SystemUI/res/layout/screen_share_dialog.xml
+++ b/packages/SystemUI/res/layout/screen_share_dialog.xml
@@ -67,12 +67,12 @@
android:gravity="start"/>
<!-- Buttons -->
- <LinearLayout
+ <com.android.internal.widget.ButtonBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="@dimen/screenrecord_buttons_margin_top">
- <TextView
+ <Button
android:id="@android:id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -83,13 +83,13 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
- <TextView
+ <Button
android:id="@android:id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:text="@string/screenrecord_continue"
style="@style/Widget.Dialog.Button" />
- </LinearLayout>
+ </com.android.internal.widget.ButtonBarLayout>
</LinearLayout>
</ScrollView> \ No newline at end of file
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index a3174a95ee6b..4002517b2cd4 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Tik om te bekyk"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Kon nie skermopname stoor nie"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Kon nie skermopname begin nie"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Kwessieopnemer"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Verwerk tans kwessieopname"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Deurlopende kennisgewing vir ’n kwessieversamelingsessie"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Opnamekwessie"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Deel"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Kwessieopname is gestoor"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Tik om te bekyk"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Kon nie kwessieopname stoor nie"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Kon nie kwessieopname begin nie"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Bekyk tans volskerm"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Swiep van bo af as jy wil uitgaan."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Het dit"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gestoor"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ontkoppel"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiveer"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Skakel dit môre outomaties weer aan"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Kenmerke soos Kitsdeel, Kry My Toestel en toestelligging gebruik Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batterykrag"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Oudio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Kopstuk"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Hierdie toestel word deur jou ouer bestuur. Jou ouer kan inligting sien en bestuur soos die programme wat jy gebruik, jou ligging en jou skermtyd."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Ontsluit gehou deur TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Diefstalbeskerming\nToestel gesluit; te veel pogings om te ontsluit"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Klankinstellings"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Gee outomaties mediaopskrifte"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"deaktiveer"</string>
<string name="sound_settings" msgid="8874581353127418308">"Klank en vibrasie"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Instellings"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Intydse Onderskrifte"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Volume is verlaag na ’n veiliger vlak"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Oorfoonvolume was langer as wat aanbeveel word hoog"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Oorfoonvolume het die veilige limiet vir hierdie week oorskry"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibreer"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s volumekontroles"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Oproepe en kennisgewings sal lui (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> speel tans op"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Oudio sal speel op"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Stelsel-UI-ontvanger"</string>
<string name="status_bar" msgid="4357390266055077437">"Statusbalk"</string>
<string name="demo_mode" msgid="263484519766901593">"Stelsel-UI-demonstrasiemodus"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"om <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"op <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Warmkol"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Satelliet, geen verbinding nie"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Satelliet, swak verbinding"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelliet, goeie toestand"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelliet, verbinding is beskikbaar"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Werkprofiel"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Pret vir party mense, maar nie vir almal nie"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Stelsel-UI-ontvanger gee jou ekstra maniere om die Android-gebruikerkoppelvlak in te stel en te pasmaak. Hierdie eksperimentele kenmerke kan in toekomstige uitreikings verander, breek of verdwyn. Gaan versigtig voort."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Maak instellings oop"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Maak Assistent oop"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Sluit skerm"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Maak notas oop"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Maak ’n nota"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Verrig veelvuldige stelseltake"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Gaan by verdeelde skerm in met huidige app aan die regterkant"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Gaan by verdeelde skerm in met huidige app aan die linkerkant"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index b4c68e22ef34..ebc20027209f 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"ለመመልከት መታ ያድርጉ"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"የማያ ገጽ ቀረጻን ማስቀመጥ ላይ ስህተት"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"የማያ ገፅ ቀረጻን መጀመር ላይ ስህተት"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"ችግር መመዝገቢያ"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"የአሰራር ችግር አመዘጋገብ"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"ለችግር መሰብሰብ ክፍለ ጊዜ ቀጣይነት ያለው ማሳወቂያ"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"የቀረጻ ችግር"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"አጋራ"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"የችግር ምዝገባ ቀምጧል"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"ለመመልከት መታ ያድርጉ"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"የችግር ምዝገባ ማስቀመጥ ላይ ስህተት"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"ችግር ምዝገባ ማስጀመር ላይ ስህተት"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"በሙሉ ገጽ ዕይታ በማየት ላይ"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"ለመውጣት፣ ከላይ ወደታች ያንሸራትቱ።"</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"ገባኝ"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ተቀምጧል"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ግንኙነትን አቋርጥ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ያግብሩ"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ነገ እንደገና በራስ-ሰር አስጀምር"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"እንደ ፈጣን ማጋራት፣ የእኔን መሣሪያ አግኝ እና የመሣሪያ አካባቢ ያሉ ባህሪያት ብሉቱዝን ይጠቀማሉ"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ባትሪ"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ኦዲዮ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ማዳመጫ"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"ይህ መሣሪያ በእርስዎ ወላጅ የሚተዳደር ነው። ወላጅዎ የሚጠቀሙባቸውን መተግበሪያዎች፣ አካባቢዎን እና የማያ ገፅ ጊዜዎን የመሳሰሉ መረጃዎችን ማየት እና ማስተዳደር ይችላል።"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"በ TrustAgent እንደተከፈተ ቀርቷል"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"የስርቆት መከላከያ\nመሳሪያ ተቆልፏል፣ በጣም ብዙ የመክፈት ሙከራዎች"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>። <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"የድምፅ ቅንብሮች"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"ራስሰር የሥዕል መግለጫ ጽሑፍን ሚዲያ"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"አሰናክል"</string>
<string name="sound_settings" msgid="8874581353127418308">"ድምፅ እና ንዝረት"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"ቅንብሮች"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"የቀጥታ መግለጫ ጽሑፍ"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"የድምፅ መጠን ይበልጥ ደህንነቱ ወደተጠበቀ ደረጃ ዝቅ ተደርጓል"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"የራስ ላይ ማዳመጫ የድምፅ መጠን ከሚመከረው በላይ ረዘም ላለ ጊዜ ከፍተኛ ነበር"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"የራስ ላይ ማዳመጫ የድምፅ መጠን ለዚህ ሳምንት ደህንነቱ ከተጠበቀው ገደብ አልፏል"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ንዘር"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s የድምፅ መቆጣጠሪያዎች"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"ጥሪዎች እና ማሳወቂያዎች (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>) ላይ ይደውላሉ"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> እየተጫወተ ያለው በ"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"ኦዲዮ ይጫወታል በ"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"የስርዓት በይነገጽ መቃኛ"</string>
<string name="status_bar" msgid="4357390266055077437">"የሁኔታ አሞሌ"</string>
<string name="demo_mode" msgid="263484519766901593">"የስርዓት ተጠቃሚ በይነገጽ ማሳያ ሁነታ"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"በ<xliff:g id="WHEN">%1$s</xliff:g> ላይ"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"በ<xliff:g id="WHEN">%1$s</xliff:g> ላይ"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"መገናኛ ነጥብ"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"ሳተላይት፣ ግንኙነት የለም"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"ሳተላይት፣ ደካማ ግንኙነት"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ሳተላይት፣ ጥሩ ግንኙነት"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ሳተላይት፣ ግንኙነት አለ"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"የስራ መገለጫ"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"ለአንዳንዶች አስደሳች ቢሆንም ለሁሉም አይደለም"</string>
<string name="tuner_warning" msgid="1861736288458481650">"የስርዓት በይነገጽ መቃኛ የAndroid ተጠቃሚ በይነገጹን የሚነካኩበት እና የሚያበጁበት ተጨማሪ መንገዶች ይሰጠዎታል። እነዚህ የሙከራ ባህሪዎች ወደፊት በሚኖሩ ልቀቶች ላይ ሊለወጡ፣ ሊሰበሩ ወይም ሊጠፉ ይችላሉ። ከጥንቃቄ ጋር ወደፊት ይቀጥሉ።"</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"ቅንብሮችን ክፈት"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"ረዳትን ክፈት"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"ማያ ገፅ ቁልፍ"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"ማስታወሻዎችን ክፈት"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"ማስታወሻ ይውሰዱ"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"የሥርዓት ብዙ ተግባራትን በተመሳሳይ ጊዜ ማከናወን"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"ለአርኤችኤስ በአሁኑ መተግበሪያ ወደ የተከፈለ ማያ ገጽ ግባ"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"ለኤልኤችኤስ በአሁኑ መተግበሪያ ወደ የተከፈለ ማያ ገጽ ይግቡ"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 7cd07fd0f47a..ec72578f9fc3 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"انقر لعرض التسجيل."</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"حدث خطأ أثناء حفظ تسجيل محتوى الشاشة."</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"حدث خطأ في بدء تسجيل الشاشة"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"مسجّلة المشاكل"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"يجري معالجة تسجيل المشكلة."</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"إشعار بنشاط مستمر في الخلفية لجلسة جمع البيانات حول المشكلة"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"يجري تسجيل المشكلة."</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"مشاركة"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"تم حفظ تسجيل المشكلة."</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"انقر لعرض التسجيل."</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"حدث خطأ أثناء حفظ تسجيل المشكلة."</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"حدث خطأ أثناء بدء تسجيل المشكلة."</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"جارٍ العرض بملء الشاشة"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"للخروج، مرِّر سريعًا من أعلى الشاشة لأسفلها."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"حسنًا"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"محفوظ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"إلغاء الربط"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"تفعيل"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"تفعيل البلوتوث تلقائيًا مرة أخرى غدًا"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"‏يُستخدَم البلوتوث في ميزات مثل Quick Share و\"العثور على جهازي\" والموقع الجغرافي للجهاز"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"مستوى طاقة البطارية <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"صوت"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"سماعة الرأس"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"يتولّى أحد الوالدين إدارة هذا الجهاز. يمكن للوالدين عرض وإدارة معلوماتك، مثلاً التطبيقات التي تستخدمها وموقعك الجغرافي ووقت النظر إلى الشاشة."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"شبكة افتراضية خاصة"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"‏فتح القفل باستمرار بواسطة TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"تم قفل الجهاز\nلحمايته من السرقة بسبب إجراء العديد من محاولات إلغاء القفل."</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"إعدادات الصوت"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"شرح تلقائي للوسائط"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"إيقاف"</string>
<string name="sound_settings" msgid="8874581353127418308">"الصوت والاهتزاز"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"الإعدادات"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"النسخ النصي التلقائي"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"تم خفض مستوى الصوت إلى مستوى أكثر أمانًا"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"كان مستوى صوت سمّاعة الرأس مرتفعًا لمدة أطول مما يُنصَح به."</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"تجاوز مستوى صوت سمّاعة الرأس الحد الآمن هذا الأسبوع."</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"اهتزاز"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"‏%s عنصر للتحكم في مستوى الصوت"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"سيصدر الهاتف رنينًا عند تلقي المكالمات والإشعارات (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)."</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"تشغيل <xliff:g id="LABEL">%s</xliff:g> على"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"سيتم تشغيل الصوت على"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"أداة ضبط واجهة مستخدم النظام"</string>
<string name="status_bar" msgid="4357390266055077437">"شريط الحالة"</string>
<string name="demo_mode" msgid="263484519766901593">"وضع تجريبي لواجهة مستخدم النظام"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"في <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"يوم <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"نقطة اتصال"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"قمر صناعي، لا يتوفّر اتصال بالإنترنت"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"قمر صناعي، الاتصال ضعيف"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"قمر صناعي، الاتصال جيد"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"قمر صناعي، الاتصال متوفّر"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"الملف الشخصي للعمل"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"متعة للبعض وليس للجميع"</string>
<string name="tuner_warning" msgid="1861736288458481650">"‏توفر لك أداة ضبط واجهة مستخدم النظام طرقًا إضافية لتعديل واجهة مستخدم Android وتخصيصها. ويمكن أن تطرأ تغييرات على هذه الميزات التجريبية أو يمكن أن تتعطل هذه الميزات أو تختفي في الإصدارات المستقبلية. عليك متابعة الاستخدام مع توخي الحذر."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"فتح الإعدادات"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"‏فتح \"مساعد Google\""</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"شاشة القفل"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"فتح تطبيق تدوين الملاحظات"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"تدوين ملاحظة"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"تعدُّد المهام في النظام"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"تفعيل وضع \"تقسيم الشاشة\" مع عرض التطبيق الحالي على يسار الشاشة"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"تفعيل وضع \"تقسيم الشاشة\" مع عرض التطبيق الحالي على يمين الشاشة"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 1752f97fd52f..f66650b41451 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"চাবলৈ টিপক"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"ৰেকৰ্ড কৰা স্ক্ৰীন ছেভ কৰোঁতে আসোঁৱাহ হৈছে"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"স্ক্রীন ৰেকৰ্ড কৰা আৰম্ভ কৰোঁতে আসোঁৱাহ হৈছে"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"সমস্যা ৰেকৰ্ডাৰ"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"সমস্যা ৰেকৰ্ড কৰি থকা হৈছে"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"সমস্যা সংগ্ৰহ কৰা এটা ছেশ্বনৰ বাবে চলিত জাননী"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"সমস্যাটো ৰেকৰ্ড কৰি থকা হৈছে"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"শ্বেয়াৰ কৰক"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"সমস্যাৰ ৰেকৰ্ডিং ছেভ কৰা হৈছে"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"চাবলৈ টিপক"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"সমস্যাৰ ৰেকৰ্ডিং ছেভ কৰাত আসোঁৱাহ"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"সমস্যাৰ ৰেকৰ্ডিং আৰম্ভ কৰোঁতে আসোঁৱাহ হৈছে"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"পূৰ্ণ স্ক্ৰীনত চাই আছে"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"বাহিৰ হ’বলৈ ওপৰৰ পৰা তললৈ ছোৱাইপ কৰক।"</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"বুজি পালোঁ"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ছেভ কৰা হৈছে"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"সংযোগ বিচ্ছিন্ন কৰক"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"সক্ৰিয় কৰক"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"কাইলৈ পুনৰ স্বয়ংক্ৰিয়ভাৱে অন কৰক"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Quick Share, Find My Device আৰু ডিভাইচৰ অৱস্থানৰ দৰে সুবিধাই ব্লুটুথ ব্যৱহাৰ কৰে"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"বেটাৰী <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"অডিঅ’"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"হেডছেট"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"এই ডিভাইচটো আপোনাৰ অভিভাৱকে পৰিচালনা কৰে। আপোনাৰ অভিভাৱকে আপুনি ব্যৱহাৰ কৰা এপ্‌, আপোনাৰ অৱস্থান আৰু আপুনি ডিভাইচত অতিবাহিত কৰা সময়ৰ দৰে তথ্য চাব আৰু পৰিচালনা কৰিব পাৰে।"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"ভিপিএন"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgentএ আনলক কৰি ৰাখিছে"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"চুৰিৰ পৰা সুৰক্ষা\nডিভাইচ লক আছে, বহুবাৰ আনলকৰ চেষ্টা কৰা হৈছে"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"ধ্বনিৰ ছেটিং"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"স্বয়ংক্ৰিয় কেপশ্বন মিডিয়া"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"অক্ষম কৰক"</string>
<string name="sound_settings" msgid="8874581353127418308">"ধ্বনি আৰু কম্পন"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"ছেটিং"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"লাইভ কেপশ্বন"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"ভলিউম সুৰক্ষিত স্তৰলৈ কম কৰা হৈছে"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"চুপাৰিছ কৰাতকৈ দীঘলীয়া সময়ৰ বাবে হেডফ’নৰ ভলিউম উচ্চ হৈ আছে"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"হেডফ’নৰ ভলিউমে এই সপ্তাহৰ বাবে সুৰক্ষিত সীমা অতিক্ৰম কৰিছে"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"কম্পন কৰক"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s ধ্বনি নিয়ন্ত্ৰণসমূহ"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"কল আৰু জাননীবোৰ ইমান ভলিউমত বাজিব (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"ইয়াত <xliff:g id="LABEL">%s</xliff:g> প্লে’ হৈ আছে"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"অডিঅ’ ইয়াত প্লে’ হ’ব"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"স্থিতি দণ্ড"</string>
<string name="demo_mode" msgid="263484519766901593">"ছিষ্টেমৰ UI প্ৰদৰ্শন ম\'ড"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> বজাত"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> বজাত"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"হটস্পট"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"উপগ্ৰহ, সংযোগ নাই"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"উপগ্ৰহ, বেয়া সংযোগ"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"উপগ্ৰহ, ভাল সংযোগ"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"উপগ্ৰহ, সংযোগ উপলব্ধ"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"কৰ্মস্থানৰ প্ৰ\'ফাইল"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"কিছুমানৰ বাবে আমোদজনক হয় কিন্তু সকলোৰে বাবে নহয়"</string>
<string name="tuner_warning" msgid="1861736288458481650">"System UI Tunerএ আপোনাক Android ব্যৱহাৰকাৰী ইণ্টাৰফেইচ সলনি কৰিবলৈ আৰু নিজৰ উপযোগিতা অনুসৰি ব্যৱহাৰ কৰিবলৈ অতিৰিক্ত সুবিধা প্ৰদান কৰে। এই পৰীক্ষামূলক সুবিধাসমূহ সলনি হ\'ব পাৰে, সেইবোৰে কাম নকৰিব পাৰে বা আগন্তুক সংস্কৰণসমূহত সেইবোৰ অন্তৰ্ভুক্ত কৰা নহ’ব পাৰে। সাৱধানেৰে আগবাঢ়ক।"</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"ছেটিং খোলক"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Assistant খোলক"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"লক স্ক্ৰীন"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Notes এপ্ খোলক"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"টোকা লিখক"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"ছিষ্টেম মাল্টিটাস্কিং"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"বৰ্তমানৰ এপৰ জৰিয়তে বিভাজিত স্ক্ৰীনৰ সোঁফালৰ স্ক্ৰীনখনত সোমাওক"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"বৰ্তমানৰ এপৰ জৰিয়তে বিভাজিত স্ক্ৰীনৰ বাওঁফালৰ স্ক্ৰীনখনত সোমাওক"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 4a79b70d31ae..70484414a4cb 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Baxmaq üçün toxunun"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Ekran çəkimini yadda saxlayarkən xəta oldu"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Ekranın yazılması ilə bağlı xəta"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Problem qeydə alan"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Problem qeydi emal edilir"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Problemin əldə edilməsi sessiyası üçün davam edən bildiriş"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Problem qeydə alınır"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Paylaşın"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Problem qeydi yadda saxlandı"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Baxmaq üçün toxunun"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Problem qeydini yadda saxlayarkən xəta"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Problemi qeydə almağa başlayarkən xəta"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Tam ekran rejimi"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Çıxmaq üçün yuxarıdan aşağı sürüşdürün."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Anladım"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Yadda saxlandı"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"əlaqəni kəsin"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivləşdirin"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Sabah avtomatik aktiv edin"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Cəld Paylaşım, Cihazın Tapılması və cihaz məkanı kimi funksiyalar Bluetooth istifadə edir"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batareya"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Qulaqlıq"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Bu cihaz valideyniniz tərəfindən idarə olunur. Valideyniniz işlətdiyiniz tətbiqlər, məkanınız və ekran vaxtınız kimi bilgiləri görə və idarə edə bilər."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN (Virtual Şəxsi Şəbəkələr)"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent ilə açıq saxlayın"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Oğurluqdan qoruma\nCihaz kilidləndi. Çoxlu kilidaçma cəhdi"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Səs ayarları"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Avtomatik başlıq mediası"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"deaktiv edin"</string>
<string name="sound_settings" msgid="8874581353127418308">"Səs və vibrasiya"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Ayarlar"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Canlı Altyazı"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Səs təhlükəsiz səviyyəyə azaldıldı"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Qulaqlığın səsi tövsiyə ediləndən uzun müddət yüksək olub"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Bu həftə qulaqlığın səsi təhlükəsiz limiti keçib"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrasiya"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s səs nəzarətləri"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Çağrı və bildirişlər zəng çalacaq (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> tətbiqində oxudulur"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio oxudulacaq"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Status paneli"</string>
<string name="demo_mode" msgid="263484519766901593">"Sistem interfeysi: demorejim"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Hotspot"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Peyk, bağlantı yoxdur"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Peyk, bağlantı zəifdir"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Peyk, bağlantı yaxşıdır"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Peyk, bağlantı var"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"İş profili"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Hamı üçün deyil, bəziləri üçün əyləncəli"</string>
<string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner Android istifadəçi interfeysini dəyişdirmək və fərdiləşdirmək üçün Sizə ekstra yollar təklif edir."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Ayarları açın"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Assistenti açın"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Kilid ekranı"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Qeydləri açın"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Qeyd götürün"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Sistemdə çoxsaylı tapşırıq icrası"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Cari tətbiq sağda olmaqla bölünmüş ekrana daxil olun"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Cari tətbiq solda olmaqla bölünmüş ekrana daxil olun"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index eceedc144706..a598d8f3733b 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Dodirnite da biste pregledali"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Greška pri čuvanju snimka ekrana"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Greška pri pokretanju snimanja ekrana"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Snimač problema"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Obrađuje se snimak problema"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"obaveštenje o aktivnosti u toku za sesiju prikupljanja podataka o problemu"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Snimamo problem"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Deli"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Snimak problema je sačuvan"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Dodirnite da biste pregledali"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Greška pri čuvanju snimka problema"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Greška pri pokretanju snimanja problema"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Prikazuje se ceo ekran"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Da biste izašli, prevucite nadole odozgo."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Važi"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Sačuvano"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"prekinite vezu"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivirajte"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatski ponovo uključi sutra"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Funkcije kao što su Quick Share, Pronađi moj uređaj i lokacija uređaja koriste Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Nivo baterije je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Ovim uređajem upravlja roditelj. Roditelj može da vidi informacije, kao što su aplikacije koje koristiš, tvoju lokaciju i vreme ispred ekrana, i da upravlja njima."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Pouzdani agent sprečava zaključavanje"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Zaštita od krađe\nZaklj. uređaj, previše pokušaja otključavanja"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Podešavanja zvuka"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatski titl za medije"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"onemogućite"</string>
<string name="sound_settings" msgid="8874581353127418308">"Zvuk i vibriranje"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Podešavanja"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Titl uživo"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Zvuk je smanjen na bezbednu jačinu"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Zvuk u slušalicama je bio glasan duže nego što se preporučuje"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Jačina zvuka u slušalicama je premašila bezbednosno ograničenje za ovu nedelju"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibracija"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Kontrole za jačinu zvuka za %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Melodija zvona za pozive i obaveštenja je uključena (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> se pušta na"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Zvuk se pušta na"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Tjuner za korisnički interfejs sistema"</string>
<string name="status_bar" msgid="4357390266055077437">"Statusna traka"</string>
<string name="demo_mode" msgid="263484519766901593">"Režim demonstracije za korisnički interfejs sistema"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Hotspot"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Satelit, niste povezani"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Satelit, veza je loša"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, veza je dobra"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, veza je dostupna"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Poslovni profil"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Zabava za neke, ali ne za sve"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Tjuner za korisnički interfejs sistema vam pruža dodatne načine za podešavanje i prilagođavanje Android korisničkog interfejsa. Ove eksperimentalne funkcije mogu da se promene, otkažu ili nestanu u budućim izdanjima. Budite oprezni."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Otvori podešavanja"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Otvori pomoćnika"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Zaključavanje ekrana"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Otvori beleške"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Napravite belešku"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Obavljanje više zadataka sistema istovremeno"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Pokreni podeljeni ekran za aktuelnu aplikaciju na desnoj strani"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Pokreni podeljeni ekran za aktuelnu aplikaciju na levoj strani"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 666d2895066e..2b137fb63258 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Націсніце для прагляду"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Памылка захавання запісу экрана"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Памылка пачатку запісу экрана"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Запіс праблемы"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Ідзе апрацоўка запісу праблемы"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Бягучае апавяшчэнне пра сеанс збору даных аб праблеме"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Ідзе запіс праблемы"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Абагуліць"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Запіс праблемы захаваны"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Націсніце, каб праглядзець"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Пры захаванні запісу праблемы адбылася памылка"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Пры спробе пачаць запіс праблемы адбылася памылка"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Прагляд у поўнаэкранным рэжыме"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Каб выйсці, правядзіце пальцам па экране зверху ўніз."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Зразумела"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Захавана"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"адключыць"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"актываваць"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Аўтаматычнае ўключэнне заўтра"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Такія функцыі, як вызначэнне месцазнаходжання прылады, Хуткае абагульванне і Знайсці прыладу, выкарыстоўваюць Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Узровень зараду: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Гук"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнітура"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Гэта прылада знаходзіцца пад кантролем бацькоў. Бацькі могуць праглядаць і кантраляваць тваю інфармацыю, напрыклад пра праграмы, якія ты выкарыстоўваеш, даныя пра тваё месцазнаходжанне і час карыстання прыладай."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Разблакіравана з дапамогай TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Абарона ад крадзяжу\nПрылада заблакіравана з-за няўдалых спроб"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Налады гуку"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Аўтаматычныя субцітры"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"адключыць"</string>
<string name="sound_settings" msgid="8874581353127418308">"Гук і вібрацыя"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Налады"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Аўтаматычныя субцітры"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Гучнасць паніжана да больш бяспечнага ўзроўню"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Гучнасць у навушніках была вялікай больш часу, чым рэкамендавана"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Гучнасць у навушніках перавысіла ліміт бяспечнага праслухоўвання на гэтым тыдні"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"вібрыраваць"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Рэгулятар гучнасці %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Для выклікаў і апавяшчэнняў уключаны гук (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> прайграецца тут:"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Аўдыявыхад:"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Наладка сістэмнага інтэрфейсу карыстальніка"</string>
<string name="status_bar" msgid="4357390266055077437">"Панэль стану"</string>
<string name="demo_mode" msgid="263484519766901593">"Рэжым дэманстрацыі сістэмнага інтэрфейсу карыстальніка"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"у <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"у <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Хот-спот"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Спадарожнікавая сувязь, няма падключэння"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Спадарожнікавая сувязь, дрэннае падключэнне"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Спадарожнікавая сувязь, добрае падключэнне"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Спадарожнікавая сувязь, падключэнне даступнае"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Працоўны профіль"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Цікава для некаторых, але не для ўсіх"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Наладка сістэмнага інтэрфейсу карыстальніка дае вам дадатковыя спосабы наладжвання і дапасоўвання карыстальніцкага інтэрфейсу Android. Гэтыя эксперыментальныя функцыі могуць змяніцца, перастаць працаваць або знікнуць у будучых версіях. Карыстайцеся з асцярожнасцю."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Адкрыць налады"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Выклікаць Памочніка"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Экран блакіроўкі"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Адкрыць нататкі"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Стварыць нататку"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Шматзадачнасць сістэмы"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Перайсці ў рэжым падзеленага экрана з бягучай праграмай справа"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Перайсці ў рэжым падзеленага экрана з бягучай праграмай злева"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index b27f0e4a0597..aa3da9caa6f9 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Докоснете за преглед"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Грешка при запазването на записа на екрана"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"При стартирането на записа на екрана възникна грешка"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Записване на проблем"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Записът се обработва"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Текущо известие за сесия за събиране на данни за проблем"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Проблемът се записва"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Споделяне"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Записът на проблема е запазен"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Докоснете за преглед"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Грешка при запазването на записа на проблема"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Грешка при стартирането на записа на проблема"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Изглед на цял екран"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"За изход прекарайте пръст надолу от горната част."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Разбрах"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Запазено"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"прекратяване на връзката"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"активиране"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Автоматично включване отново утре"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Bluetooth се използва от различни функции, като например „Бързо споделяне“, „Намиране на устройството ми“ и местоположението на устройството"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батерия: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалки"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Това устройство се управлява от родителя ви. Той може да вижда и управлява информация, като например приложенията, които използвате, местоположението ви и времето на ползване."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Поддържа се отключено от надежден агент"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Защита от кражба\nУ-вото е закл., твърде много опити за откл."</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Настройки за звука"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Медия с автоматични надписи"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"деактивиране"</string>
<string name="sound_settings" msgid="8874581353127418308">"Звук и вибриране"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Настройки"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Надписи на живо"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Силата на звука е намалена до по-безопасно ниво"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Нивото на силата на звука на слушалките е било високо по-дълго, отколкото е препоръчително"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Нивото на силата на звука на слушалките е надвишило безопасния лимит за тази седмица"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"вибриране"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Контроли за силата на звука – %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"При обаждания и известия устройството ще звъни (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Възпроизвеждане на <xliff:g id="LABEL">%s</xliff:g> на"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Аудиото ще се възпроизвежда на"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Тунер на системния потребителски интерфейс"</string>
<string name="status_bar" msgid="4357390266055077437">"Лента на състоянието"</string>
<string name="demo_mode" msgid="263484519766901593">"Демонстрационен режим на системния ПИ"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"в <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"в/ъв <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Точка за достъп"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Сателит, няма връзка"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Сателит, лоша връзка"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Сателит, добра връзка"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Сателит, налице е връзка"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Потребителски профил в Work"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Забавно – но не за всички"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Тунерът на системния потребителски интерфейс ви предоставя допълнителни възможности за прецизиране и персонализиране на практическата работа с Android. Тези експериментални функции може да се променят, повредят или да изчезнат в бъдещите версии. Действайте внимателно."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Отваряне на настройките"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Отваряне на Асистент"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Заключване на екрана"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Отваряне на бележките"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Създаване на бележка"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Едновременно изпълняване на няколко задачи в системата"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Преминаване към разделен екран с текущото приложение отдясно"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Преминаване към разделен екран с текущото приложение отляво"</string>
@@ -1242,7 +1235,7 @@
<string name="assistant_attention_content_description" msgid="4166330881435263596">"Установено е присъствие на потребител"</string>
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Задайте стандартно приложение за бележки от настройките"</string>
<string name="install_app" msgid="5066668100199613936">"Инсталиране на приложението"</string>
- <string name="dismissible_keyguard_swipe" msgid="8377597870094949432">"Прекарайте пръст нагоре, за да продължите"</string>
+ <string name="dismissible_keyguard_swipe" msgid="8377597870094949432">"Плъзнете нагоре, за да продължите"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Да се дублира ли на външния екран?"</string>
<string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Съдържанието на вътрешния ви дисплей ще бъде дублирано. Предният ви дисплей ще бъде изключен."</string>
<string name="mirror_display" msgid="2515262008898122928">"Дублиране на дисплея"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 8da62b2e9fc5..12e24c0a3bd3 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"দেখতে ট্যাপ করুন"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"স্ক্রিন রেকর্ডিং সেভ করার সময় কোনও সমস্যা হয়েছে"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"স্ক্রিন রেকর্ডিং শুরু করার সময় সমস্যা হয়েছে"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Recorder-এ সমস্যা হয়েছে"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"সমস্যা সংক্রান্ত রেকর্ডিং প্রসেস করা হচ্ছে"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"সমস্যা সংগ্রহ সেশনের জন্য অনগোইং নোটিফিকেশন"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"রেকর্ডিং সংক্রান্ত সমস্যা"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"শেয়ার করুন"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"রেকর্ডিং সেভ করতে সমস্যা হয়েছে"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"দেখার জন্য ট্যাপ করুন"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"সমস্যা সংক্রান্ত রেকর্ডিং সেভ করার সময় সমস্যা হয়েছে"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"সমস্যা সংক্রান্ত রেকর্ডিং শুরু করতে সমস্যা হয়েছে"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"ফুল-স্ক্রিনে দেখা হচ্ছে"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"বেরিয়ে যেতে উপর থেকে নিচের দিকে সোয়াইপ করুন।"</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"বুঝেছি"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"সেভ করা আছে"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ডিসকানেক্ট করুন"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"চালু করুন"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"আগামীকাল অটোমেটিক আবার চালু হবে"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"দ্রুত শেয়ার, Find My Device ও ডিভাইসের লোকেশন ব্লুটুথ ব্যবহার করে"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"চার্জ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"অডিও"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"হেডসেট"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"আপনার অভিভাবক এই ডিভাইস ম্যানেজ করেন। আপনার অভিভাবক আপনার ব্যবহার করা অ্যাপ, লোকেশন ও স্ক্রিন টাইমের মতো তথ্যগুলি দেখতে এবং ম্যানেজ করতে পারেন।"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent দিয়ে আনলক করে রাখা হয়েছে"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"চুরি থেকে সুরক্ষা\nডিভাইস লক করা আছে, আনলক করার জন্য অনেকবার চেষ্টা করা হয়েছে"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"সাউন্ড সেটিংস"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"অটোমেটিক মিডিয়া ক্যাপশন দেখুন"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"বন্ধ হবে"</string>
<string name="sound_settings" msgid="8874581353127418308">"সাউন্ড ও ভাইব্রেশন"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"সেটিংস"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"লাইভ ক্যাপশন"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"ভলিউম কমিয়ে আরও নিরাপদ মাত্রায় নামানো হয়েছে"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"সাজেস্ট করা সময়ের চেয়ে অতিরিক্ত সময় ধরে হেডফোনের ভলিউম বেশি করা আছে"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"এই সপ্তাহে হেডফোনের ভলিউম নিরাপদ মাত্রা ছাড়িয়ে গেছে"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ভাইব্রেট করান"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s ভলিউম নিয়ন্ত্রণ"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"কল এবং বিজ্ঞপ্তির রিং হবে (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g>-এ প্লে করা হচ্ছে"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"অডিও প্লে করা হবে"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"সিস্টেম UI টিউনার"</string>
<string name="status_bar" msgid="4357390266055077437">"স্ট্যাটাস বার"</string>
<string name="demo_mode" msgid="263484519766901593">"সিস্টেম UI ডেমো মোড"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> -তে"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"হটস্পট"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"স্যাটেলাইট, কোনও কানেকশন নেই"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"স্যাটেলাইট, খারাপ কানেকশন"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"স্যাটেলাইট, ভালো কানেকশন"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"স্যাটেলাইট, কানেকশন উপলভ্য আছে"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"কাজের প্রোফাইল"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"কিছু ব্যক্তির জন্য মজাদার কিন্তু সকলের জন্য নয়"</string>
<string name="tuner_warning" msgid="1861736288458481650">"এই পরীক্ষামূলক বৈশিষ্ট্যগুলি ভবিষ্যতের সংস্করণগুলির মধ্যে পরিবর্তিত, বিভাজিত এবং অদৃশ্য হয়ে যেতে পারে৷ সাবধানতার সাথে এগিয়ে যান৷ সিস্টেম UI টিউনার আপনাকে Android ব্যবহারকারী ইন্টারফেসের সূক্ষ্ম সমন্বয় এবং কাস্টমাইজ করার অতিরিক্ত উপায়গুলি প্রদান করে৷"</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"সেটিংস খুলুন"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Assistant খুলুন"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"লক স্ক্রিন"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"নোট খুলুন"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"একটি নোট লিখুন"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"সিস্টেম মাল্টিটাস্কিং"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"ডানদিকে থাকা বর্তমান অ্যাপ ব্যবহার করে \'স্প্লিট স্ক্রিন\' যোগ করুন"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"বাঁদিকে থাকা বর্তমান অ্যাপ ব্যবহার করে \'স্প্লিট স্ক্রিন\' যোগ করুন"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 67bd504b7b9d..f79e2d915451 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Dodirnite da vidite"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Greška prilikom pohranjivanja snimka ekrana"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Greška pri pokretanju snimanja ekrana"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Snimač problema"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Obrada snimka problema"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Obavještenje o aktivnosti u pozadini za sesiju prikupljanja podataka o problemu"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Snimanje problema"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Dijelite"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Snimak problema je sačuvan"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Dodirnite da pregledate"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Došlo je do greške prilikom pohranjivanja snimka problema"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Došlo je do greške prilikom pokretanja snimanja problema"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Prikazivanje preko cijelog ekrana"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Da izađete, prevucite s vrha nadolje."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Razumijem"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Sačuvano"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"prekid veze"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiviranje"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatski uključi ponovo sutra"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Funkcije kao što su Quick Share, Pronađi moj uređaj i lokacija uređaja koriste Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> baterije"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Ovim uređajem upravlja tvoj roditelj. Roditelj može vidjeti i upravljati informacijama kao što su aplikacije koje koristiš, lokacija i vrijeme korištenja uređaja."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Pouzdani agent sprečava zaključavanje"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Zaštita od krađe\nUređaj je zaključan, previše pokušaja otključ."</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Postavke zvuka"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatski titlovi za medije"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"onemogući"</string>
<string name="sound_settings" msgid="8874581353127418308">"Zvuk i vibracija"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Postavke"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Automatski titlovi"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Zvuk je smanjen na sigurniju jačinu"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Jačina zvuka slušalica je bila visoka duže od preporučenog"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Jačina zvuka slušalica je premašila sigurno ograničenje za ovu sedmicu"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibriranje"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Kontrole glasnoće za %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Pozivi i obavještenja će zvoniti jačinom (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Reproduciranje: <xliff:g id="LABEL">%s</xliff:g>"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Zvuk će se reprod. na:"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Podešavač za korisnički interfejs sistema"</string>
<string name="status_bar" msgid="4357390266055077437">"Statusna traka"</string>
<string name="demo_mode" msgid="263484519766901593">"Demo način rada Sistemskog UI-ja"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Pristupna tačka"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Satelit, nema veze"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Satelit, slaba veza"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, dobra veza"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, veza je dostupna"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Radni profil"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Zabava za neke, ali ne za sve"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Podešavač za korisnički interfejs sistema vam omogućava dodatne načine da podesite i prilagodite Androidov interfejs. Ove eksperimentalne funkcije se u budućim verzijama mogu mijenjati, kvariti ili nestati. Budite oprezni."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Otvaranje postavki"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Otvaranje Asistenta"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Zaključavanje ekrana"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Otvaranje Bilješki"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Pisanje bilješke"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Multitasking sistema"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Otvaranje podijeljenog ekrana s trenutnom aplikacijom na desnoj strani"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Otvaranje podijeljenog ekrana s trenutnom aplikacijom na lijevoj strani"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 23e4ed4e48f9..583275f366b3 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Toca per veure-la"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"S\'ha produït un error en desar la gravació de la pantalla"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"S\'ha produït un error en iniciar la gravació de pantalla"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Gravadora de problemes"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Processant gravació del problema"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Notificació en curs d\'una sessió de recollida de dades del problema"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"S\'està gravant el problema"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Comparteix"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"S\'ha desat la gravació del problema"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Toca per veure"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"S\'ha produït un error en desar la gravació del problema"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"S\'ha produït un error en iniciar la gravació del problema"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Visualització en pantalla completa"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Per sortir, llisca cap avall des de la part superior."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Entesos"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Desat"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconnecta"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activa"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Torna\'l a activar automàticament demà"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Funcions com ara Quick Share, Troba el meu dispositiu i la ubicació del dispositiu utilitzen el Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de bateria"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Àudio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculars"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"El teu pare o mare gestionen aquest dispositiu, i poden veure i gestionar informació com ara les aplicacions que utilitzes, la teva ubicació i el teu temps de connexió."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Desbloquejat per TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Protecció antirobatoris\nDispositiu bloquejat; massa intents"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Configuració del so"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Subtitula el contingut multimèdia automàticament"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"desactivar"</string>
<string name="sound_settings" msgid="8874581353127418308">"So i vibració"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Configuració"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Subtítols instantanis"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"El volum s\'ha abaixat a un nivell més segur"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"El volum dels auriculars ha estat elevat durant més temps del recomanat"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"El volum dels auriculars ha superat el límit de seguretat d\'aquesta setmana"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Controls de volum %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Les trucades i les notificacions sonaran (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"S\'està reproduint <xliff:g id="LABEL">%s</xliff:g> a"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Es reproduirà a"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Personalitzador d\'interfície d\'usuari"</string>
<string name="status_bar" msgid="4357390266055077437">"Barra d\'estat"</string>
<string name="demo_mode" msgid="263484519766901593">"Mode de demostració de la IU del sistema"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"Hora: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"Dia: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Punt d\'accés Wi-Fi"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Satèl·lit, sense connexió"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Satèl·lit, connexió deficient"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satèl·lit, bona connexió"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satèl·lit, connexió disponible"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Perfil de treball"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Diversió per a uns quants, però no per a tothom"</string>
<string name="tuner_warning" msgid="1861736288458481650">"El Personalitzador d\'interfície d\'usuari presenta opcions addicionals per canviar i personalitzar la interfície d\'usuari d\'Android. És possible que aquestes funcions experimentals canviïn, deixin de funcionar o desapareguin en versions futures. Continua amb precaució."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Obre la configuració"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Obre l\'Assistent"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Pantalla de bloqueig"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Obre les notes"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Crea una nota"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Multitasques del sistema"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Entra al mode de pantalla dividida amb l\'aplicació actual a la dreta"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Entra al mode de pantalla dividida amb l\'aplicació actual a l\'esquerra"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 9805e011130e..6cdb3d8af2a1 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Klepnutím nahrávku zobrazíte"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Při ukládání záznamu obrazovky došlo k chybě"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Při spouštění nahrávání obrazovky došlo k chybě"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Rekordér problémů"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Zpracování záznamu problému"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Upozornění na probíhající shromažďování dat o problému"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Záznam problému"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Sdílet"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Záznam problému byl uložen"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Zobrazíte ho klepnutím"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Při ukládání záznamu problému došlo k chybě"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Při zahájení záznamu problému došlo k chybě"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Zobrazování přes celou obrazovku"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Režim ukončíte přejetím prstem shora dolů."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Rozumím"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Uloženo"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"odpojit"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivovat"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Zítra znovu automaticky zapnout"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Funkce jako Quick Share, Najdi moje zařízení a vyhledávání zařízení používají Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterie: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Sluchátka"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Toto zařízení spravuje rodič. Rodič může zobrazit údaje, jako jsou používané aplikace, tvá poloha a čas strávený na zařízení."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Odemknutí udržováno funkcí TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Ochrana před odcizením\nZamknuto, moc pokusů o odemknutí"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Nastavení zvuku"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatické přepisy médií"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"deaktivovat"</string>
<string name="sound_settings" msgid="8874581353127418308">"Zvuk a vibrace"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Nastavení"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Živý přepis"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Hlasitost byla snížena na bezpečnou úroveň"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Hlasitost sluchátek byla vysoká déle, než je doporučeno"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Hlasitost sluchátek překročila bezpečný limit pro tento týden"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrovat"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Ovládací prvky hlasitosti %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Volání a oznámení budou vyzvánět (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Přehrávání <xliff:g id="LABEL">%s</xliff:g> na"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Zvuk se přehraje na"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Nástroj na ladění uživatelského rozhraní systému"</string>
<string name="status_bar" msgid="4357390266055077437">"Stavový řádek"</string>
<string name="demo_mode" msgid="263484519766901593">"Ukázkový režim uživatelského rozhraní systému"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"v <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Hotspot"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Satelit, bez připojení"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Satelit, špatné připojení"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, dobré připojení"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, připojení je k dispozici"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Pracovní profil"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Zábava, která není pro každého"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Nástroj na ladění uživatelského rozhraní systému vám nabízí další způsoby, jak si vyladit a přizpůsobit uživatelské rozhraní Android. Tyto experimentální funkce mohou v dalších verzích chybět, nefungovat nebo být změněny. Postupujte proto prosím opatrně."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Otevřít nastavení"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Otevřít Asistenta"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Uzamknout obrazovku"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Otevřít poznámky"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Vytvořit poznámku"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Systémový multitasking"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Přepnout na rozdělenou obrazovku s aktuálními aplikacemi napravo"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Přepnout na rozdělenou obrazovku s aktuálními aplikacemi nalevo"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 084ce0a54a51..79b01a84b3bb 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Tryk for at se"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Skærmoptagelsen kunne ikke gemmes"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Skærmoptagelsen kunne ikke startes"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Problemoptagelse"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Behandler optagelse af problem"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Igangværende notifikation om en session med problemindsamling"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Optager problem"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Del"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Optagelse af problem er gemt"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Tryk for at se"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Kunne ikke gemme optagelse af problem"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Fejl under forsøg på at starte optagelse af problem"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Visning i fuld skærm"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Stryg ned fra toppen for at afslutte."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"OK"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gemt"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"afbryd forbindelse"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivér"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktivér automatisk igen i morgen"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Funktioner som f.eks. Quick Share, Find min enhed og enhedslokation anvender Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Lyd"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Denne enhed administreres af din forælder. Din forælder kan se og administrere oplysninger såsom de apps, du bruger, din lokation og din skærmtid."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Holdes oplåst af TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Tyveribeskyttelse\nLåst enhed (for mange oplåsningsforsøg)"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Lydindstillinger"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Undertekster til medier"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"deaktiver"</string>
<string name="sound_settings" msgid="8874581353127418308">"Lyd og vibration"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Indstillinger"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Livetekstning"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Lydstyrken blev reduceret til et mere sikkert niveau"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Høretelefonernes lydstyrke har været høj i længere tid end anbefalet"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Høretelefonernes lydstyrke har overskredet sikkerhedsgrænsen for denne uge"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrer"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s lydstyrkeknapper"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Der afspilles lyd ved opkald og notifikationer (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Afspiller <xliff:g id="LABEL">%s</xliff:g> på"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Lyden afspilles på"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Statusbjælke"</string>
<string name="demo_mode" msgid="263484519766901593">"Demotilstand for systemets brugerflade"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"kl. <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"på <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Hotspot"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Satellit – ingen forbindelse"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Satellit – dårlig forbindelse"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellit – god forbindelse"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellit – forbindelsen er tilgængelig"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Arbejdsprofil"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Sjovt for nogle, men ikke for alle"</string>
<string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner giver dig flere muligheder for at justere og tilpasse Android-brugerfladen. Disse eksperimentelle funktioner kan ændres, gå i stykker eller forsvinde i fremtidige udgivelser. Vær forsigtig, hvis du fortsætter."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Åbn indstillinger"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Åbn Assistent"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Lås skærm"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Åbn noter"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Skriv en note"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Systemmultitasking"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Start opdelt skærm med aktuel app til højre"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Start opdelt skærm med aktuel app til venstre"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index b3d3712671db..43f799bd1b68 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Zum Ansehen tippen"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Fehler beim Speichern der Bildschirmaufzeichnung"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Fehler beim Start der Bildschirmaufzeichnung"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Problem aufzeichnen"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Aufzeichnung wird verarbeitet"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Benachrichtigung über laufende Aufzeichnung eines Problems"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Problem wird aufgezeichnet"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Teilen"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Aufzeichnung des Problems wurde gespeichert"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Zum Ansehen tippen"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Fehler beim Speichern der Aufzeichnung des Problems"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Fehler beim Starten der Aufzeichnung des Problems"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Vollbildmodus wird aktiviert"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Zum Beenden von oben nach unten wischen."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Ok"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gespeichert"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"Verknüpfung aufheben"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivieren"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Morgen automatisch wieder aktivieren"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Für Funktionen wie Quick Share, „Mein Gerät finden“ und den Gerätestandort wird Bluetooth verwendet"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akkustand: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Dieses Gerät wird von deinen Eltern verwaltet. Sie können unter anderem Informationen über deine genutzten Apps, deinen Standort und deine Bildschirmzeit einsehen und verwalten."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Durch TrustAgent entsperrt"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Diebstahlschutz\nGerät gesperrt – zu viele Entsperrversuche"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Toneinstellungen"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Medien autom. untertiteln"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"deaktivieren"</string>
<string name="sound_settings" msgid="8874581353127418308">"Ton &amp; Vibration"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Einstellungen"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Automatische Untertitel"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Auf verträglichere Lautstärke eingestellt"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Die Kopfhörerlautstärke war länger als empfohlen hoch eingestellt"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Die Kopfhörerlautstärke hat für diese Woche das Sicherheitslimit überschritten"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"Vibrieren lassen"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Lautstärkeregler von %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Gerät klingelt bei Anrufen und Benachrichtigungen (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Wiedergabe von <xliff:g id="LABEL">%s</xliff:g> über"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audiowiedergabe über"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Statusleiste"</string>
<string name="demo_mode" msgid="263484519766901593">"Demomodus der System-UI"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"um <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"am <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Hotspot"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Satellit, keine Verbindung"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Satellit, Verbindung schlecht"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellit, Verbindung gut"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellit, Verbindung verfügbar"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Arbeitsprofil"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Für einige ein Vergnügen, aber nicht für alle"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Mit System UI Tuner erhältst du zusätzliche Möglichkeiten, die Android-Benutzeroberfläche anzupassen. Achtung: Diese Testfunktionen können sich ändern, abstürzen oder in zukünftigen Versionen verschwinden."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Einstellungen öffnen"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Assistant öffnen"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Sperrbildschirm"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Notizen öffnen"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Notiz machen"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"System-Multitasking"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Splitscreen aktivieren, aktuelle App rechts"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Splitscreen aktivieren, aktuelle App links"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 825994acda6a..43ab777aa1de 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Πατήστε για προβολή"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Σφάλμα κατά την αποθήκευση της εγγραφής οθόνης"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Σφάλμα κατά την έναρξη της εγγραφής οθόνης"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Εργαλείο καταγραφής προβλημάτων"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Επεξερ. καταγραφής προβλήματος"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Ειδοποίηση σε εξέλιξη για μια περίοδο λειτουργίας συλλογής προβλημάτων"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Πρόβλημα καταγραφής"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Κοινοποίηση"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Η καταγραφή του προβλήματος αποθηκεύτηκε"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Πατήστε για προβολή"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Σφάλμα κατά την αποθήκευση της καταγραφής του προβλήματος"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Σφάλμα κατά την έναρξη της καταγραφής του προβλήματος"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Προβολή σε πλήρη οθόνη"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Για έξοδο, σύρετε προς τα κάτω από το επάνω μέρος."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Το κατάλαβα"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Αποθηκεύτηκε"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"αποσύνδεση"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ενεργοποίηση"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Αυτόματη ενεργοποίηση ξανά αύριο"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Λειτουργίες όπως το Quick Share, η Εύρεση συσκευής και η τοποθεσία της συσκευής χρησιμοποιούν Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Μπαταρία <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ήχος"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ακουστικά"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Αυτή η συσκευή είναι διαχειριζόμενη από τον γονέα σου. Ο γονέας σου μπορεί να βλέπει και να διαχειρίζεται πληροφορίες όπως οι εφαρμογές που χρησιμοποιείς, η τοποθεσία σου και ο χρόνος χρήσης."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Διατήρηση ξεκλειδώματος με TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Προστασία από κλοπή\nΗ συσκ. κλειδ., πάρα πολλές προσπ. ξεκλ."</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Ρυθμίσεις ήχου"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Αυτόματοι υπότιτλοι στο μέσο"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"απενεργοποίηση"</string>
<string name="sound_settings" msgid="8874581353127418308">"Ήχος και δόνηση"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Ρυθμίσεις"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Ζωντανοί υπότιτλοι"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Η ένταση ήχου μειώθηκε σε πιο ασφαλές επίπεδο"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Η ένταση ήχου των ακουστικών ήταν σε υψηλό επίπεδο για μεγαλύτερο διάστημα από αυτό που συνιστάται"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Η ένταση ήχου των ακουστικών ξεπέρασε το ασφαλές όριο για αυτή την εβδομάδα"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"δόνηση"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s στοιχεία ελέγχου έντασης ήχου"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Θα υπάρχει ηχητική ειδοποίηση για κλήσεις και ειδοποιήσεις (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Αναπαραγωγή <xliff:g id="LABEL">%s</xliff:g> σε"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Ο ήχος θα παίξει σε"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Γραμμή κατάστασης"</string>
<string name="demo_mode" msgid="263484519766901593">"Λειτουργία επίδειξης διεπαφής χρήστη συστήματος"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"στις <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"στις <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Σημείο πρόσβασης Wi-Fi"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Δορυφορική, δεν υπάρχει σύνδεση"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Δορυφορική, κακή σύνδεση"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Δορυφορική, καλή σύνδεση"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Δορυφορική, διαθέσιμη σύνδεση"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Προφίλ εργασίας"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Διασκέδαση για ορισμένους, αλλά όχι για όλους"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Το System UI Tuner σάς προσφέρει επιπλέον τρόπους για να τροποποιήσετε και να προσαρμόσετε τη διεπαφή χρήστη Android. Αυτές οι πειραματικές λειτουργίες ενδέχεται να τροποποιηθούν, να παρουσιάσουν σφάλματα ή να καταργηθούν σε μελλοντικές εκδόσεις. Συνεχίστε με προσοχή."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Άνοιγμα ρυθμίσεων"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Άνοιγμα Βοηθού"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Κλείδωμα οθόνης"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Άνοιγμα σημειώσεων"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Δημιουργία σημείωσης"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Πολυδιεργασία συστήματος"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Ενεργοποίηση διαχωρισμού οθόνης με την τρέχουσα εφαρμογή στα δεξιά"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Ενεργοποίηση διαχωρισμού οθόνης με την τρέχουσα εφαρμογή στα αριστερά"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index f2fcb035db77..9d2742a2b9fe 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Tap to view"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Error saving screen recording"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Error starting screen recording"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Issue Recorder"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Processing issue recording"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Ongoing notification for an issue collection session"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Recording issue"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Share"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Issue recording saved"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Tap to view"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Error saving issue recording"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Error starting issue recording"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Viewing full screen"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"To exit, swipe down from the top."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Got it"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Features like Quick Share, Find My Device and device location use Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"This device is managed by your parent. Your parent can see and manage information such as the apps that you use, your location and your screen time."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Kept unlocked by trust agent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Theft protection\nDevice locked, too many unlock attempts"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Sound settings"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatically caption media"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"disable"</string>
<string name="sound_settings" msgid="8874581353127418308">"Sound and vibration"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Settings"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Live Caption"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Volume lowered to safer level"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Headphone volume has been high for longer than recommended"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Headphone volume has exceeded the safe limit for this week"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrate"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s volume controls"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Calls and notifications will ring (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Playing <xliff:g id="LABEL">%s</xliff:g> on"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio will play on"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Status bar"</string>
<string name="demo_mode" msgid="263484519766901593">"System UI demo mode"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"at <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"on <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Hotspot"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Satellite, no connection"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Satellite, poor connection"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellite, good connection"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellite, connection available"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Work profile"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Fun for some but not for all"</string>
<string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner gives you extra ways to tweak and customise the Android user interface. These experimental features may change, break or disappear in future releases. Proceed with caution."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Open settings"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Open Assistant"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Lock screen"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Open notes"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Take a note"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"System multitasking"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Enter split screen with current app to RHS"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Enter split screen with current app to LHS"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 863acb1e0c26..f77f66028397 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -268,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Features like Quick Share, Find My Device, and device location use Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -528,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"This device is managed by your parent. Your parent can see and manage information such as the apps you use, your location, and your screen time."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Kept unlocked by TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Theft protection\nDevice locked, too many unlock attempts"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Sound settings"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatically caption media"</string>
@@ -537,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"disable"</string>
<string name="sound_settings" msgid="8874581353127418308">"Sound and vibration"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Settings"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Live Caption"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Volume lowered to safer level"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Headphone volume has been high for longer than recommended"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Headphone volume has exceeded the safe limit for this week"</string>
@@ -580,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrate"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s volume controls"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Calls and notifications will ring (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Playing <xliff:g id="LABEL">%s</xliff:g> on"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio will play on"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Status bar"</string>
<string name="demo_mode" msgid="263484519766901593">"System UI demo mode"</string>
@@ -728,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Open settings"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Open assistant"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Lock screen"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Open notes"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Take a note"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"System multitasking"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Enter split screen with current app to RHS"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Enter split screen with current app to LHS"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index f2fcb035db77..9d2742a2b9fe 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Tap to view"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Error saving screen recording"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Error starting screen recording"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Issue Recorder"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Processing issue recording"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Ongoing notification for an issue collection session"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Recording issue"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Share"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Issue recording saved"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Tap to view"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Error saving issue recording"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Error starting issue recording"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Viewing full screen"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"To exit, swipe down from the top."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Got it"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Features like Quick Share, Find My Device and device location use Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"This device is managed by your parent. Your parent can see and manage information such as the apps that you use, your location and your screen time."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Kept unlocked by trust agent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Theft protection\nDevice locked, too many unlock attempts"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Sound settings"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatically caption media"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"disable"</string>
<string name="sound_settings" msgid="8874581353127418308">"Sound and vibration"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Settings"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Live Caption"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Volume lowered to safer level"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Headphone volume has been high for longer than recommended"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Headphone volume has exceeded the safe limit for this week"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrate"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s volume controls"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Calls and notifications will ring (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Playing <xliff:g id="LABEL">%s</xliff:g> on"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio will play on"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Status bar"</string>
<string name="demo_mode" msgid="263484519766901593">"System UI demo mode"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"at <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"on <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Hotspot"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Satellite, no connection"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Satellite, poor connection"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellite, good connection"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellite, connection available"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Work profile"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Fun for some but not for all"</string>
<string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner gives you extra ways to tweak and customise the Android user interface. These experimental features may change, break or disappear in future releases. Proceed with caution."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Open settings"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Open Assistant"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Lock screen"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Open notes"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Take a note"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"System multitasking"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Enter split screen with current app to RHS"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Enter split screen with current app to LHS"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index f2fcb035db77..9d2742a2b9fe 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Tap to view"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Error saving screen recording"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Error starting screen recording"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Issue Recorder"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Processing issue recording"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Ongoing notification for an issue collection session"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Recording issue"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Share"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Issue recording saved"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Tap to view"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Error saving issue recording"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Error starting issue recording"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Viewing full screen"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"To exit, swipe down from the top."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Got it"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Features like Quick Share, Find My Device and device location use Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"This device is managed by your parent. Your parent can see and manage information such as the apps that you use, your location and your screen time."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Kept unlocked by trust agent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Theft protection\nDevice locked, too many unlock attempts"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Sound settings"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatically caption media"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"disable"</string>
<string name="sound_settings" msgid="8874581353127418308">"Sound and vibration"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Settings"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Live Caption"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Volume lowered to safer level"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Headphone volume has been high for longer than recommended"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Headphone volume has exceeded the safe limit for this week"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrate"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s volume controls"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Calls and notifications will ring (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Playing <xliff:g id="LABEL">%s</xliff:g> on"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio will play on"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Status bar"</string>
<string name="demo_mode" msgid="263484519766901593">"System UI demo mode"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"at <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"on <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Hotspot"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Satellite, no connection"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Satellite, poor connection"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellite, good connection"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellite, connection available"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Work profile"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Fun for some but not for all"</string>
<string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner gives you extra ways to tweak and customise the Android user interface. These experimental features may change, break or disappear in future releases. Proceed with caution."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Open settings"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Open Assistant"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Lock screen"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Open notes"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Take a note"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"System multitasking"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Enter split screen with current app to RHS"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Enter split screen with current app to LHS"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 6dc6d3b27cf7..6f5c13362405 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -268,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‎‏‏‎‏‏‎‎‎‏‎‏‎‏‎‎‎‏‎‏‏‏‎‎‏‏‎‏‏‎‏‏‏‎‎‎‏‏‎‎‎‏‏‏‎‏‏‎‎‏‎‏‎Saved‎‏‎‎‏‎"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‏‎‎‎‏‎‏‏‏‎‏‏‎‏‏‏‏‏‎‎‏‏‏‏‏‎‎‎‏‎‏‎‎‏‎‎‏‏‏‏‎‏‎‎‏‏‎‏‎‎‏‏‏‏‎‎disconnect‎‏‎‎‏‎"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‏‎‏‏‏‏‎‏‏‎‎‎‎‎‏‎‏‏‎‏‎‏‏‏‎‏‏‎‏‎‏‏‏‎‏‎‏‏‎‎‎‏‏‎‏‎‎‏‎‏‏‎‏‏‎activate‎‏‎‎‏‎"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‏‎‎‎‎‎‏‏‏‎‎‏‎‏‏‎‏‏‏‎‎‎‏‏‎‏‏‎‏‏‏‎‏‎‏‎‏‏‏‎‎‎‎‏‎‎‏‏‎‏‏‎‎‏‎‎Automatically turn on again tomorrow‎‏‎‎‏‎"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‏‏‎‏‏‏‏‎‎‎‏‎‎‏‏‏‎‎‏‏‎‏‎‎‏‏‎‏‎‎‎‏‎‎‎‏‏‎‏‏‎‎‎‎‎‏‎‏‎‎‎Features like Quick Share, Find My Device, and device location use Bluetooth‎‏‎‎‏‎"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎‎‎‎‎‏‎‎‏‏‎‎‏‎‎‏‎‏‎‎‏‏‏‏‎‏‎‏‏‏‏‎‏‏‏‏‏‎‎‎‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>‎‏‎‎‏‏‏‎ battery‎‏‎‎‏‎"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‏‏‎‏‎‏‎‎‎‏‎‎‏‏‎‏‎‎‎‏‎‏‎‏‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‎‎‎‏‏‎‏‎‏‎‏‏‎‎‏‎Audio‎‏‎‎‏‎"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‎‏‎‏‏‏‎‏‎‏‎‎‎‎‏‎‎‏‎‎‏‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‎‏‏‏‎‎‏‏‎‏‏‎‎‏‎‏‎‎‎Headset‎‏‎‎‏‎"</string>
@@ -528,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‎‏‎‏‏‏‎‏‏‏‏‏‎‏‏‎‎‏‏‎‎‏‏‏‎‎‏‎‏‎‎‎‏‎‏‎‎‎‎‎‏‎‎‎‏‏‏‎‏‎‏‎‎This device is managed by your parent. Your parent can see and manage information such as the apps you use, your location, and your screen time.‎‏‎‎‏‎"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‏‎‏‏‎‏‏‏‎‏‎‎‏‎‎‎‏‎‏‏‎‎‏‏‏‏‎‏‎‎‎‏‎‏‏‏‏‎‏‎‏‏‎‏‎‎‏‎‏‎‏‎‎‏‎VPN‎‏‎‎‏‎"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‏‎‎‎‎‎‏‏‎‏‏‏‏‎‎‏‎‎‎‏‎‎‏‏‏‎‏‎‏‏‏‎‎‏‎‎‏‎‏‎‎‎‎‎‏‏‏‎‏‎‎‎‏‏‎Kept unlocked by TrustAgent‎‏‎‎‏‎"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‏‏‎‎‎‏‏‏‎‏‎‎‏‎‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‏‏‎‎‎‎‏‎‏‏‎Theft protection‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎Device locked, too many unlock attempts‎‏‎‎‏‎"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‎‎‎‎‎‎‏‎‏‎‎‏‎‎‎‎‎‎‏‏‎‏‏‏‎‏‎‎‏‏‎‎‏‏‎‏‎‎‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="ZEN_MODE">%1$s</xliff:g>‎‏‎‎‏‏‏‎. ‎‏‎‎‏‏‎<xliff:g id="EXIT_CONDITION">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‏‎‏‏‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‎‎‎‎‏‎‏‎‏‎‎‎‏‏‏‏‏‎‎‎‎‎Sound settings‎‏‎‎‏‎"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‎‏‏‎‎‎‎‎‏‏‎‏‏‏‎‎‎‏‎‎‎‏‎‏‏‏‎‏‏‎‏‏‎‎‏‎‏‎‏‏‎‏‎‏‏‎‏‏‏‎‏‎Automatically caption media‎‏‎‎‏‎"</string>
@@ -537,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‏‏‏‏‎‏‎‎‏‎‏‏‏‏‎‏‎‏‏‎‎‎‏‏‏‎‎‎‎‏‎‏‏‎‎‏‏‏‏‏‎‏‏‏‏‎‏‏‎‏‏‎‏‏‏‏‎disable‎‏‎‎‏‎"</string>
<string name="sound_settings" msgid="8874581353127418308">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‎‎‏‏‎‏‏‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‏‏‎‏‏‏‎‏‏‏‎‎‎‏‏‎‎‏‏‏‎‎‎‏‎‎‎Sound &amp; vibration‎‏‎‎‏‎"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‏‏‏‎‎‎‎‎‏‏‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‎‎‎‏‎‏‏‏‏‏‎‎‏‎‎‏‏‏‎‎‏‏‎‎Settings‎‏‎‎‏‎"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‎‎‏‏‏‎‏‏‎‎‎‏‎‎‎‏‏‏‎‎‏‎‎‎‎‏‏‎‎‏‎‎‎‎‏‏‏‎‏‏‎‎‏‏‏‎‎‎‎‎‎‏‎‏‎Live Caption‎‏‎‎‏‎"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‎‎‏‏‎‎‏‎‎‏‎‎‏‎‎‏‏‎‏‎‎‏‏‎‎‎‏‎‏‎‏‎‏‎‎‎‎‎‏‎‏‎‏‏‎‏‏‏‎‏‏‏‏‎‎‏‎Volume lowered to safer level‎‏‎‎‏‎"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‏‎‏‎‏‏‎‎‏‏‏‎‎‏‎‎‏‎‏‏‏‎‏‏‎‏‏‎‎‏‏‏‏‎‏‎‎‏‎‎‎‏‎‏‏‏‎‎‏‎‎‎‏‏‎‎‎‎Headphone volume has been high for longer than recommended‎‏‎‎‏‎"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‏‎‎‏‏‏‎‎‏‎‏‎‏‏‏‎‏‎‎‏‎‏‏‎‏‏‎‏‎‎‏‎‎‎‎‎‏‎‏‏‎‏‏‏‎‎‎‎‎‏‏‏‎‎‎Headphone volume has exceeded the safe limit for this week‎‏‎‎‏‎"</string>
@@ -580,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‎‏‏‎‏‎‎‎‎‎‏‎‎‎‏‏‎‎‎‎‏‎‎‏‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‎‏‎‏‏‎‎‏‎‎‏‎‏‎vibrate‎‏‎‎‏‎"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‎‏‏‏‏‏‎‎‎‏‏‏‏‏‎‎‏‎‎‏‎‏‎‏‏‏‎‏‏‎‎‎‏‎‏‏‏‎‎‏‎‏‏‏‏‏‎‎‏‎‏‎‏‎‎‎%s volume controls‎‏‎‎‏‎"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‏‏‎‎‏‎‎‏‏‎‏‎‏‏‏‏‏‏‎‎‎‎‏‎‏‏‎‏‎‏‎‏‏‏‏‎‏‏‏‎‏‏‎‏‏‎‏‎‏‎‎Calls and notifications will ring (‎‏‎‎‏‏‎<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎)‎‏‎‎‏‎"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‎‎‎‏‏‏‎‎‏‏‏‎‎‏‎‏‎‏‏‏‎‎‎‎‎‏‎‏‏‏‏‎‏‏‎‏‎‏‎‏‏‎‎‎‎‎‏‏‎‎‏‎‏‎‎‏‎Playing ‎‏‎‎‏‏‎<xliff:g id="LABEL">%s</xliff:g>‎‏‎‎‏‏‏‎ on‎‏‎‎‏‎"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‎‎‎‏‎‏‏‏‎‏‏‏‏‏‎‎‏‏‎‏‏‎‎‏‏‏‎‎‎‎‎‏‎‏‏‎‏‎‏‏‎‏‎‎‎‎‎‏‏‎‏‏‎‏‎‏‎Audio will play on‎‏‎‎‏‎"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‎‏‏‎‏‎‏‏‎‏‎‎‎‏‏‏‏‏‏‏‎‏‎‏‏‎‎‏‎‎‎‎‏‏‏‎‎‎‏‏‎‎‎‎‎‎‎‏‏‎‏‎‏‎‎‏‎System UI Tuner‎‏‎‎‏‎"</string>
<string name="status_bar" msgid="4357390266055077437">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‎‏‏‏‏‎‎‎‏‎‎‎‏‏‏‏‎‏‎‏‏‏‎‎‎‏‎‎‏‎‏‏‏‏‏‏‎‏‎‏‎‎‏‎‏‏‏‎‎‎‏‏‏‏‎‏‎Status bar‎‏‎‎‏‎"</string>
<string name="demo_mode" msgid="263484519766901593">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‏‎‏‎‏‎‎‎‎‎‎‏‎‏‎‏‏‏‎‎‎‏‏‎‎‏‎‏‎‏‎‎‏‎‎‎‎‏‎‏‎‏‎‎‎‎‏‏‎‏‎‏‏‎‎‏‎System UI demo mode‎‏‎‎‏‎"</string>
@@ -728,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‏‏‎‏‎‏‎‎‏‏‏‎‏‏‎‏‏‏‎‎‏‏‏‏‎‏‎‎‏‏‎‏‏‎‏‏‏‎‏‏‎‎‎‏‏‏‎‎‎‎‎‏‎Open settings‎‏‎‎‏‎"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‎‏‏‏‏‎‏‎‏‎‏‎‎‏‏‏‏‏‎‎‏‎‎‏‏‏‏‏‎‏‎‎‎‎‏‎‏‎‏‎‏‎‎‏‎‎‏‏‎‏‏‏‎‎Open assistant‎‏‎‎‏‎"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‎‏‎‎‏‎‏‏‎‎‏‎‎‏‎‏‎‏‎‎‏‏‏‏‎‏‎‏‎‏‎‎‎‏‏‏‏‎‎‏‏‏‎‎‏‏‏‏‎‏‏‏‏‏‎Lock screen‎‏‎‎‏‎"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‏‎‏‎‏‎‏‏‎‎‏‎‏‏‎‎‏‏‏‏‎‎‎‏‏‎‏‎‏‏‎‏‎‎‏‎‎‎‏‏‏‏‎‏‎‏‏‎‎‎‏‎‎‎‏‎Open notes‎‏‎‎‏‎"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‏‏‏‏‎‎‏‏‏‎‏‏‏‎‎‏‎‏‎‏‎‏‎‎‏‏‎‎‎‎‏‏‎‎‏‏‎‎‏‎‏‏‏‎Take a note‎‏‎‎‏‎"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‏‎‎‏‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‏‎‎‏‎‏‎‎‎‏‏‎‏‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‎‎‎‎‎‎‎‏‎System multitasking‎‏‎‎‏‎"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‎‎‎‏‎‎‎‎‎‏‎‏‎‏‏‏‎‎‏‎‏‏‎‏‎‏‎‎‏‎‏‏‏‏‎‏‏‎‎‎‏‎‏‎‎‎‏‎‏‎‎‎‎‎‎‏‎Enter split screen with current app to RHS‎‏‎‎‏‎"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‎‏‏‎‏‎‏‏‏‏‎‎‎‏‏‎‏‏‏‎‏‎‎‎‏‎‎‎‏‎‏‏‎‎‎‏‎‎‎‏‏‎‎‏‏‏‏‎‎‏‎‏‎‎Enter split screen with current app to LHS‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index be60c7e30794..8c64a0767cfd 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Presiona para ver"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Se produjo un error al guardar la grabación de pantalla"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Error al iniciar la grabación de pantalla"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Grabadora de errores"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Procesando grabación del error"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Notificación continua por un error durante la sesión de recopilación de datos"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Error de grabación"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Compartir"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Se guardó la grabación del error"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Presiona para verla"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Se produjo un error al guardar la grabación del problema"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Se produjo un error al iniciar la grabación del problema"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Visualización en pantalla completa"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Para salir, desliza el dedo hacia abajo desde la parte superior."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Entendido"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Guardado"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activar"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Volver a activar automáticamente mañana"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Las funciones como Quick Share, Encontrar mi dispositivo y la ubicación del dispositivo usan Bluetooth."</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batería"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Tu padre o madre administra este dispositivo. Esa persona puede ver y administrar información, como las apps que usas, tu ubicación y el tiempo de uso."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent lo mantiene desbloqueado"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Protección antirrobo\nDispositivo bloqueado; muchos intentos"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Configuración de sonido"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Muestra subtítulos automáticos"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"inhabilitar"</string>
<string name="sound_settings" msgid="8874581353127418308">"Sonido y vibración"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Configuración"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Subtitulado instantáneo"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Se bajó el volumen a un nivel seguro"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"El volumen de los auriculares se mantuvo elevado por más tiempo del recomendado"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Se excedió el límite seguro de volumen de los auriculares para esta semana"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Controles de volumen %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Sonarán las llamadas y notificaciones (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Reproduciendo <xliff:g id="LABEL">%s</xliff:g> en"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Se reproducirá en"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Sintonizador de IU del sistema"</string>
<string name="status_bar" msgid="4357390266055077437">"Barra de estado"</string>
<string name="demo_mode" msgid="263484519766901593">"Modo demostración de la IU del sistema"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"a la(s) <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"el <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Hotspot"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Satélite, sin conexión"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Satélite, conexión inestable"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satélite, buena conexión"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satélite, conexión disponible"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Perfil de trabajo"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Diversión para algunos, pero no para todos"</string>
<string name="tuner_warning" msgid="1861736288458481650">"El sintonizador de IU del sistema te brinda más formas para editar y personalizar la interfaz de usuario de Android. Estas funciones experimentales pueden cambiar, dejar de funcionar o no incluirse en futuras versiones. Procede con precaución."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Abrir configuración"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Abrir Asistente"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Bloquear la pantalla"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Abrir notas"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Crear una nota"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Tareas múltiples del sistema"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Activar pantalla dividida con la app actual en el lado derecho (RHS)"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Activar pantalla dividida con la app actual en el lado izquierdo (LHS)"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 2f8f1cf2a6dc..692bb26fb190 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Toca para verla"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"No se ha podido guardar la grabación de pantalla"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"No se ha podido empezar a grabar la pantalla"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Grabadora de problemas"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Procesando grabación de problema"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Notificación continua de una sesión de obtención de datos del problema"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Grabando problema"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Compartir"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Grabación del problema guardada"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Toca para ver"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"No se ha podido guardar la grabación del problema"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"No se ha podido iniciar la grabación del problema"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Visualización en pantalla completa"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Para salir, desliza de arriba abajo."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Entendido"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Guardado"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activar"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Volver a activar automáticamente mañana"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Funciones como Quick Share, Encontrar mi dispositivo y la ubicación del dispositivo usan Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batería"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Tu padre o madre gestionan este dispositivo y pueden ver y controlar cierta información, como las aplicaciones que utilizas, tu ubicación y tu tiempo de pantalla."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Desbloqueado por TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Protección antirrobo\nDispositivo bloqueado; demasiados intentos"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Ajustes de sonido"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Subtitular automáticamente"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"desactivar"</string>
<string name="sound_settings" msgid="8874581353127418308">"Sonido y vibración"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Ajustes"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Subtítulos automáticos"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Volumen bajado a un nivel más seguro"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"El volumen de los auriculares ha estado alto durante más tiempo del recomendado"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"El volumen de los auriculares ha superado el límite de seguridad de esta semana"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Controles de volumen %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Las llamadas y las notificaciones sonarán (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Reproduciendo <xliff:g id="LABEL">%s</xliff:g> en"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Se reproducirá en"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Configurador de UI del sistema"</string>
<string name="status_bar" msgid="4357390266055077437">"Barra de estado"</string>
<string name="demo_mode" msgid="263484519766901593">"Modo Demo de UI del sistema"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"a las <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Compartir Internet"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Satélite, sin conexión"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Satélite, mala conexión"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satélite, buena conexión"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satélite, conexión disponible"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Perfil de trabajo"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Diversión solo para algunos"</string>
<string name="tuner_warning" msgid="1861736288458481650">"El configurador de UI del sistema te ofrece otras formas de modificar y personalizar la interfaz de usuario de Android. Estas funciones experimentales pueden cambiar, fallar o desaparecer en futuras versiones. Te recomendamos que tengas cuidado."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Abrir ajustes"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Abrir el Asistente"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Pantalla de bloqueo"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Abrir notas"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Escribe una nota"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Función multitarea del sistema"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Iniciar pantalla dividida con esta aplicación en el lado derecho"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Iniciar pantalla dividida con esta aplicación en el lado izquierdo"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 9aa753cc37ca..070f200f2d74 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Puudutage kuvamiseks"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Viga ekraanisalvestise salvestamisel"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Viga ekraanikuva salvestamise alustamisel"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Probleemisalvesti"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Probleemisalvestise töötlemine"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Probleemikogumise seansi taustamärguanne"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Probleemi salvestamine"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Jaga"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Probleemisalvestis on salvestatud"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Kuvamiseks puudutage"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Viga probleemisalvestise salvestamisel"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Viga probleemi salvestamise alustamisel"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Kuvamine täisekraanil"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Väljumiseks pühkige ülevalt alla."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Selge"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvestatud"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"katkesta ühendus"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiveeri"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Lülita automaatselt homme uuesti sisse"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Funktsioonid, nagu Kiirjagamine, Leia mu seade ja seadme asukoht, kasutavad Bluetoothi"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> akut"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Heli"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Peakomplekt"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Seda seadet haldab sinu vanem. Sinu vanem näeb ja saab hallata teavet, näiteks kasutatavaid rakendusi, sinu asukohta ja ekraaniaega."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Avatuna hoiab TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Vargusvastane kaitse\nSeade lukus – liiga palju avamiskatseid"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Heliseaded"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automaatsed subtiitrid"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"keela"</string>
<string name="sound_settings" msgid="8874581353127418308">"Heli ja vibreerimine"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Seaded"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Reaalajas subtiitrid"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Helitugevust vähendati ohutumale tasemele"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Kõrvaklappide helitugevus on olnud suur soovitatavast ajast kauem"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Kõrvaklappide helitugevus on ületanud selle nädala ohutuspiirangu"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibreerimine"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Helitugevuse juhtnupud: %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Kõnede ja märguannete puhul telefon heliseb (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Esitamine jätkub seadmes <xliff:g id="LABEL">%s</xliff:g>"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Heli esitamine jätkub"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Süsteemi kasutajaliidese tuuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Olekuriba"</string>
<string name="demo_mode" msgid="263484519766901593">"Süsteemi kasutajaliidese demorežiim"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"kell <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"kell <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Kuumkoht"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Satelliit, ühendus puudub"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Satelliit, kehv ühendus"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelliit, hea ühendus"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelliit, ühendus on saadaval"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Tööprofiil"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Kõik ei pruugi sellest rõõmu tunda"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Süsteemi kasutajaliidese tuuner pakub täiendavaid võimalusi Androidi kasutajaliidese muutmiseks ja kohandamiseks. Need katselised funktsioonid võivad muutuda, rikki minna või tulevastest versioonidest kaduda. Olge jätkamisel ettevaatlik."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Seadete avamine"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Assistendi avamine"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Lukustuskuva"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Märkmete avamine"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Kirjuta märkus"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Süsteemi multitegumtöö"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Ekraanikuva jagamine, nii et praegune rakendus on paremal"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Ekraanikuva jagamine, nii et praegune rakendus on vasakul"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 0c1880a717ce..36eac0ce42b9 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Sakatu ikusteko"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Errore bat gertatu da pantaila-grabaketa gordetzean"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Errore bat gertatu da pantaila grabatzen hastean"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Arazo-grabagailua"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Arazoaren grabaketa prozesatzen"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Arazo bat biltzeko saio baten aktibo dagoen jakinarazpena"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Arazoa grabatzen"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Partekatu"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Gorde da arazoaren grabaketa"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Sakatu ikusteko"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Errore bat gertatu da arazoa grabatzean"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Errore bat gertatu da arazoa grabatzen hastean"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Pantaila osoa ikusgai"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Irteteko, pasatu hatza goitik behera."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Ados"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gordeta"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"deskonektatu"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktibatu"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktibatu automatikoki berriro bihar"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Quick Share, Bilatu nire gailua, gailuaren kokapena eta beste eginbide batzuek Bluetootha darabilte"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audioa"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Entzungailua"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Zure gurasoak kudeatzen du gailua. Zure gurasoak gailuko informazioa ikusi eta kudea dezake; besteak beste, zer aplikazio erabiltzen dituzun, zure kokapena zein den eta pantaila aurrean zenbat eta noiz egoten zaren."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPNa"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent bidez desblokeatuta"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Lapurreten aurkako babesa\nGailua blokeatuta dago, desblokeatzeko saiakera gehiegi"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Soinuaren ezarpenak"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Ezarri azpitituluak automatikoki"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"desgaitu"</string>
<string name="sound_settings" msgid="8874581353127418308">"Audioa eta dardara"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Ezarpenak"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Istanteko azpitituluak"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Bolumena maila seguruago batera jaitsi da"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Entzungailuen bolumena gomendatutako denbora baino luzaroago egon da ozen"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Entzungailuen bolumenak aste honetarako muga segurua gainditu du"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"dardara"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s gailuaren bolumena kontrolatzeko aukerak"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Tonuak jo egingo du deiak eta jakinarazpenak jasotzean (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> hemen erreproduzitzen:"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audioa erreproduzitzen jarraituko du"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Sistemaren erabiltzaile-interfazearen konfiguratzailea"</string>
<string name="status_bar" msgid="4357390266055077437">"Egoera-barra"</string>
<string name="demo_mode" msgid="263484519766901593">"Sistemaren erabiltzaile-interfazearen demo modua"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"ordua: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"data: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Wifi-gunea"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Satelitea, konexiorik ez"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Satelitea, konexio ahula"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelitea, konexio ona"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelitea, konexioa erabilgarri"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Laneko profila"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Dibertsioa batzuentzat, baina ez guztientzat"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Sistemaren erabiltzaile-interfazearen konfiguratzaileak Android erabiltzaile-interfazea moldatzeko eta pertsonalizatzeko modu gehiago eskaintzen dizkizu. Baliteke eginbide esperimental horiek hurrengo kaleratzeetan aldatuta, etenda edo desagertuta egotea. Kontuz erabili."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Ireki ezarpenak"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Ireki Laguntzailea"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Blokeatu pantaila"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Ireki oharrak"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Egin ohar bat"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Zereginen aldibereko sistemaren exekuzioa"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Sartu pantaila zatituaren eskuineko aldean oraingo aplikazioarekin"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Sartu pantaila zatituaren ezkerreko aldean oraingo aplikazioarekin"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index c0dafa40319d..b3a5e36327db 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"برای مشاهده ضربه بزنید"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"خطا در ذخیره‌سازی ضبط صفحه‌نمایش"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"خطا هنگام شروع ضبط صفحه‌نمایش"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"ضبط‌کننده مشکل"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"درحال پردازش کردن ضبط مشکل"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"اعلان جاری مربوط به جلسه جمع‌آوری مشکل"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"درحال ضبط کردن مشکل"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"هم‌رسانی"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"ضبط مشکل ذخیره شد"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"برای مشاهده ضربه بزنید"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"هنگام ذخیره کردن ضبط مشکل، خطایی پیش آمد"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"هنگام شروع ضبط مشکل، خطایی پیش آمد"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"درحال مشاهده در حالت تمام‌صفحه"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"برای خروج، از بالای صفحه تند به‌پایین بکشید."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"متوجه‌ام"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ذخیره‌شده"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"قطع اتصال"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"فعال کردن"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"فردا دوباره به‌طور خودکار روشن شود"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"ویژگی‌هایی مثل «هم‌رسانی سریع»، «پیدا کردن دستگاهم»، و مکان دستگاه از بلوتوث استفاده می‌کنند"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"شارژ باتری <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"صوت"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"هدست"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"این دستگاه را ولی‌تان مدیریت می‌کند. ولی‌تان می‌تواند اطلاعاتی مثل برنامه‌هایی که استفاده می‌کنید، مکانتان، و مدت تماشای صفحه‌تان را ببیند و مدیریت کند."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"‏با TrustAgent قفل را باز نگه‌دارید"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"محافظت دربرابر سرقت\nدستگاه قفل شد، تعداد تلاش‌ها قفل‌گشایی از حد مجاز بیشتر بود"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. ‏<xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"تنظیمات صدا"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"رسانه زیرنویس خودکار"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"غیرفعال کردن"</string>
<string name="sound_settings" msgid="8874581353127418308">"صدا و لرزش"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"تنظیمات"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"زیرنویس ناشنوایان زنده"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"صدا به سطح ایمن‌تر کاهش یافت"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"صدای هدفون برای مدتی طولانی‌تر از حد توصیه‌شده بلند بوده است"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"صدای هدفون از حد ایمن برای این هفته فراتر رفته است"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"لرزش"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"‏%s کنترل‌های میزان صدا"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"تماس‌ها و اعلان‌ها زنگ می‌خورند (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"درحال پخش <xliff:g id="LABEL">%s</xliff:g> در"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"صدا پخش می‌شود در"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"تنظیم‌کننده واسط کاربری سیستم"</string>
<string name="status_bar" msgid="4357390266055077437">"نوار وضعیت"</string>
<string name="demo_mode" msgid="263484519766901593">"حالت نمایشی واسط کاربری سیستم"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"در <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"در <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"نقطه اتصال"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"ماهواره، اتصال وجود ندارد"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"ماهواره، اتصال ضعیف است"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ماهواره، اتصال خوب است"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ماهواره، اتصال دردسترس است"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"نمایه کاری"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"برای بعضی افراد سرگرم‌کننده است اما نه برای همه"</string>
<string name="tuner_warning" msgid="1861736288458481650">"‏«تنظیم‌کننده واسط کاربری سیستم» روش‌های بیشتری برای تنظیم دقیق و سفارشی کردن واسط کاربری Android در اختیار شما قرار می‌دهد. ممکن است این ویژگی‌های آزمایشی تغییر کنند، خراب شوند یا در نسخه‌های آینده جود نداشته باشند. با احتیاط ادامه دهید."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"باز کردن تنظیمات"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"باز کردن «دستیار»"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"قفل صفحه"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"باز کردن یادداشت‌ها"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"یادداشت‌برداری"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"چندوظیفگی سیستم"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"وارد شدن به صفحهٔ دونیمه با برنامه فعلی در سمت راست"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"وارد شدن به صفحهٔ دونیمه با برنامه فعلی در سمت چپ"</string>
@@ -1242,7 +1235,7 @@
<string name="assistant_attention_content_description" msgid="4166330881435263596">"حضور کاربر شناسایی می‌شود"</string>
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"برنامه پیش‌فرض یادداشت را در «تنظیمات» تنظیم کنید"</string>
<string name="install_app" msgid="5066668100199613936">"نصب برنامه"</string>
- <string name="dismissible_keyguard_swipe" msgid="8377597870094949432">"برای ادامه دادن تند به‌بالا بکشید"</string>
+ <string name="dismissible_keyguard_swipe" msgid="8377597870094949432">"برای ادامه دادن، تند به‌بالا بکشید"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"روی نمایشگر خارجی قرینه‌سازی شود؟"</string>
<string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"نمایشگر داخلی شما قرینه‌سازی می‌شود. نمایشگر جلو خاموش می‌شود."</string>
<string name="mirror_display" msgid="2515262008898122928">"قرینه‌سازی نمایشگر"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 4b45aabfbf93..47e7f2d7dd2d 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Napauta näyttääksesi"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Virhe näyttötallenteen tallentamisessa"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Virhe näytön tallennuksen aloituksessa"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Ongelman tallentaja"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Käsittely: Ongelman tallennus"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Ongelmankeräykseen liittyvä ilmoitus taustalla jatkuvasta toiminnasta"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Tallennusongelma"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Jaa"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Ongelman tallennus tallennettiin"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Näytä napauttamalla"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Virhe ongelman tallenteen tallentamisessa"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Virhe ongelman tallentamisen aloituksessa"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Koko näytön tilassa"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Sulje palkki pyyhkäisemällä alas ruudun ylälaidasta."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Selvä"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Tallennettu"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"katkaise yhteys"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivoi"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Laita automaattisesti päälle taas huomenna"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Ominaisuudet (esim. Quick Share ja Paikanna laite) ja laitteen sijainti käyttävät Bluetoothia"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akun taso <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ääni"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Vanhempasi ylläpitää tätä laitetta. Vanhempasi voi nähdä ja ylläpitää tietoja, esim. käyttämiäsi sovelluksia, sijaintiasi ja käyttöaikaasi."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent pitää lukitusta avattuna"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Varkaussuoja\nLaite lukittu, liian monta avausyritystä"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Ääniasetukset"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Tekstitä media automaatt."</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"poista käytöstä"</string>
<string name="sound_settings" msgid="8874581353127418308">"Ääni ja värinä"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Asetukset"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Livetekstitys"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Äänenvoimakkuus laskettu turvalliselle tasolle"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Äänenvoimakkuus on ollut suuri suositeltua kauemmin"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Kuulokkeiden äänenvoimakkuus on ylittänyt tämän viikon turvarajan"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"värinä"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Äänenvoimakkuuden säädin: %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Puhelut ja ilmoitukset soivat (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Toistetaan: <xliff:g id="LABEL">%s</xliff:g>"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audiota toistetaan laitteella"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Tilapalkki"</string>
<string name="demo_mode" msgid="263484519766901593">"Käyttöliittymän esittelytila"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"kello <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"ajankohtana <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Hotspot"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Satelliitti, ei yhteyttä"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Satelliitti, huono yhteys"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelliitti, hyvä yhteys"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelliitti, yhteys saatavilla"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Työprofiili"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Ei sovellu kaikkien käyttöön"</string>
<string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner antaa lisämahdollisuuksia Android-käyttöliittymän muokkaamiseen. Nämä kokeelliset ominaisuudet voivat muuttua, lakata toimimasta tai kadota milloin tahansa. Jatka omalla vastuullasi."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Avaa asetukset"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Avaa Assistant"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Lukitusnäyttö"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Avaa muistiinpanot"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Tee muistiinpano"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Järjestelmän monikäyttö"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Siirry jaettuun näyttöön (sovellus oikeanpuoleiseen näyttöön)"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Siirry jaettuun näyttöön (sovellus vasemmanpuoleiseen näyttöön)"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 8d0e2774632b..1bb8147b9cdf 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Touchez pour afficher"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Erreur d\'enregistrement de l\'écran"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Une erreur s\'est produite lors du démarrage de l\'enregistrement d\'écran"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Enregistreur de problèmes"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Trait. de l\'enreg. du problème"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Notification continue pour une session de collecte d\'un problème"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Enregistrement du problème"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Partager"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"L\'enregistrement du problème a été enregistré"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Touchez ici pour afficher l\'enregistrement"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Erreur lors de l\'enregistrement du problème"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Erreur lors du démarrage de l\'enregistrement du problème"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Affichage plein écran"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Pour quitter, balayez du haut vers le bas."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"OK"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Enregistré"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"Déconnecter"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"Activer"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Activer le Bluetooth automatiquement demain"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Les fonctionnalités comme le Partage rapide, Localiser mon appareil et la position de l\'appareil utilisent le Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Pile : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Écouteurs"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Cet appareil est géré par ton parent. Ton parent peut voir et gérer de l\'information, comme les applications que tu utilises, ta position et ton temps d\'utilisation des écrans."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"RPV"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Maintenu déverrouillé par TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Protection c. le vol\nAppareil verrouillé, trop de tentatives"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Paramètres sonores"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Sous-titrer automatiquement"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"désactiver"</string>
<string name="sound_settings" msgid="8874581353127418308">"Son et vibration"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Paramètres"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Sous-titres instantanés"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Volume réduit à un niveau plus sécuritaire"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Le niveau du volume des écouteurs est resté élevé au-delà de la durée recommandée"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Le niveau du volume des écouteurs a dépassé la limite de sécurité pour cette semaine"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibration"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Commandes de volume de %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Les appels et les notifications seront annoncés par une sonnerie (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Lecture de <xliff:g id="LABEL">%s</xliff:g> sur"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Lecture audio sur"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Barre d\'état"</string>
<string name="demo_mode" msgid="263484519766901593">"Mode Démo de l\'interface système"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"à <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"le <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Point d\'accès sans fil"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Aucune connexion satellite"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Connexion satellite faible"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Bonne connexion satellite"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Connexion satellite accessible"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Profil professionnel"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Divertissant pour certains, mais pas pour tous"</string>
<string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner vous propose de nouvelles manières d\'adapter et de personnaliser l\'interface utilisateur d\'Android. Ces fonctionnalités expérimentales peuvent être modifiées, cesser de fonctionner ou disparaître dans les versions futures. À utiliser avec prudence."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Ouvrir les paramètres"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Ouvrir l\'Assistant"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Écran de verrouillage"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Ouvrir les notes"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Prendre une note"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Multitâche du système"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Passer à l\'écran divisé avec l\'application actuelle à droite"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Passer à l\'écran divisé avec l\'application actuelle à gauche"</string>
@@ -1242,7 +1235,7 @@
<string name="assistant_attention_content_description" msgid="4166330881435263596">"La présence d\'un utilisateur est détectée"</string>
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Définir l\'application de prise de notes par défaut dans les Paramètres"</string>
<string name="install_app" msgid="5066668100199613936">"Installer l\'application"</string>
- <string name="dismissible_keyguard_swipe" msgid="8377597870094949432">"Balayez vers le haut pour continuer"</string>
+ <string name="dismissible_keyguard_swipe" msgid="8377597870094949432">"Balayer vers le haut pour continuer"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Dupliquer l\'écran sur un moniteur externe?"</string>
<string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Votre écran intérieur sera dupliqué. Votre écran frontal sera désactivé."</string>
<string name="mirror_display" msgid="2515262008898122928">"Dupliquer l\'écran"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 49e6d08feaab..84160af98955 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -81,7 +81,7 @@
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Essayez de nouveau de faire une capture d\'écran"</string>
<string name="screenshot_failed_to_save_text" msgid="7232739948999195960">"Impossible d\'enregistrer la capture d\'écran"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Les captures d\'écran ne sont pas autorisées par l\'application ni par votre organisation"</string>
- <string name="screenshot_blocked_by_admin" msgid="5486757604822795797">"La prise de captures d\'écran est bloquée par votre administrateur informatique"</string>
+ <string name="screenshot_blocked_by_admin" msgid="5486757604822795797">"La capture d\'écran est bloquée par votre administrateur informatique"</string>
<string name="screenshot_edit_label" msgid="8754981973544133050">"Modifier"</string>
<string name="screenshot_edit_description" msgid="3333092254706788906">"Modifier la capture d\'écran"</string>
<string name="screenshot_share_description" msgid="2861628935812656612">"Partager la capture d\'écran"</string>
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Appuyez pour afficher"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Erreur lors de l\'enregistrement de l\'écran"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Erreur lors du démarrage de l\'enregistrement de l\'écran"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Enregistreur de problèmes"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Enregistrement du problème"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Notification d\'activité en cours concernant la session d\'enregistrement d\'un problème"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Problème d\'enregistrement"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Partager"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Problème enregistré"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Appuyez pour afficher"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Erreur lors de l\'enregistrement du problème"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Erreur lors du démarrage de l\'enregistrement du problème"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Affichage en plein écran"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Pour quitter, balayez l\'écran du haut vers le bas."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"OK"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Enregistré"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"dissocier"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activer"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Réactiver automatiquement demain"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Certaines fonctionnalités telles que Quick Share, Localiser mon appareil ou encore la position de l\'appareil utilisent le Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batterie"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Casque"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Cet appareil est géré par tes parents. Ils peuvent voir et gérer certaines informations, telles que les applications que tu utilises, ta position et ton temps d\'utilisation de l\'appareil."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Maintenu déverrouillé par TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Protection contre le vol\nAppareil verrouillé, trop de tentatives de déverrouillage"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Paramètres audio"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Sous-titrer automatiquement"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"désactiver"</string>
<string name="sound_settings" msgid="8874581353127418308">"Son et vibreur"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Paramètres"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Sous-titres instantanés"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Volume réduit à un niveau plus sûr"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Le volume du casque est élevé depuis plus longtemps que recommandé"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Le volume du casque a dépassé la limite de sécurité pour cette semaine"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"activer le vibreur"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Commandes de volume %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Les appels et les notifications sonneront (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Diffusion de <xliff:g id="LABEL">%s</xliff:g> sur"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"L\'audio se mettra en marche"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Barre d\'état"</string>
<string name="demo_mode" msgid="263484519766901593">"Mode démo de l\'UI du système"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"à <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"le <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Point d\'accès"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Pas de connexion satellite"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Mauvaise connexion satellite"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Bonne connexion satellite"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Connexion satellite disponible"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Profil professionnel"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Divertissant pour certains, mais pas pour tous"</string>
<string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner vous propose de nouvelles manières d\'adapter et de personnaliser l\'interface utilisateur Android. Ces fonctionnalités expérimentales peuvent être modifiées, cesser de fonctionner ou disparaître dans les versions futures. À utiliser avec prudence."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Ouvrir les paramètres"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Ouvrir l\'Assistant"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Verrouiller l\'écran"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Ouvrir les notes"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Créer une note"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Multitâche du système"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Passer en écran partagé avec l\'appli actuelle affichée à droite"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Passer en écran partagé avec l\'appli actuelle affichée à gauche"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index a03adbf97837..b78199a149cf 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Toca para ver o contido"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Produciuse un erro ao gardar a gravación da pantalla"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Produciuse un erro ao iniciar a gravación da pantalla"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Gravadora de problemas"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Procesando gravación problema"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Notificación de actividade en curso para unha sesión de rexistro dun problema"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Gravando problema"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Compartir"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Gardouse a gravación do problema"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Toca para ver o contido"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Produciuse un erro ao gardar a gravación do problema"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Produciuse un erro ao iniciar a gravación do problema"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Vendo pantalla completa"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Para saír, pasa o dedo cara abaixo desde a parte superior."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Entendido"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gardouse"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activar"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Volver activar automaticamente mañá"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"As funcións como Quick Share, Localizar o meu dispositivo ou a de localización do dispositivo utilizan o Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batería"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"O teu pai ou nai xestiona este dispositivo e pode ver e xestionar información como as aplicacións que usas, a túa localización e o tempo diante da pantalla."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Desbloqueado por un axente de confianza"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Protección antirroubo\nDisp. bloq., demasiados intentos desb."</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Configuración do son"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Crear subtítulos automáticos"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"desactiva"</string>
<string name="sound_settings" msgid="8874581353127418308">"Son e vibración"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Configuración"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Subtítulos instantáneos"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"O volume baixouse ata un nivel máis seguro"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Usaches os auriculares cun volume alto durante máis tempo do recomendado"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"O volume dos auriculares superou o límite de seguranza desta semana"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Controis de volume de %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"As chamadas e as notificacións soarán (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Reproducindo <xliff:g id="LABEL">%s</xliff:g> en"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio reproducido en"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Configurador da IU do sistema"</string>
<string name="status_bar" msgid="4357390266055077437">"Barra de estado"</string>
<string name="demo_mode" msgid="263484519766901593">"Modo de demostración da IU do sistema"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"ás <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"o <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Zona wifi"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Satélite, sen conexión"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Satélite, mala conexión"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satélite, boa conexión"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satélite, conexión dispoñible"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Perfil de traballo"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Diversión só para algúns"</string>
<string name="tuner_warning" msgid="1861736288458481650">"O configurador da IU do sistema ofréceche formas adicionais de modificar e personalizar a interface de usuario de Android. Estas funcións experimentais poden cambiar, interromperse ou desaparecer en futuras versións. Continúa con precaución."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Abrir configuración"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Abrir Asistente"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Pantalla de bloqueo"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Abrir notas"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Crear nota"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Multitarefa do sistema"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Activar pantalla dividida con esta aplicación no lado dereito"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Activar pantalla dividida con esta aplicación no lado esquerdo"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index a74ad24f0164..d84b1f553f8b 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"જોવા માટે ટૅપ કરો"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"સ્ક્રીન રેકોર્ડિંગ સાચવવામાં ભૂલ આવી"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"સ્ક્રીનને રેકૉર્ડ કરવાનું શરૂ કરવામાં ભૂલ"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"સમસ્યા રેકોર્ડર"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"સમસ્યા રેકોર્ડિંગ ચાલુ છે"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"કોઈ સમસ્યા સંગ્રહના સત્ર માટે ચાલુ નોટિફિકેશન"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"રેકોર્ડિંગની સમસ્યા"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"શેર કરો"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"સમસ્યા રેકોર્ડિંગ સાચવવામાં આવ્યું"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"જોવા માટે ટૅપ કરો"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"સમસ્યા રેકોર્ડિંગને સાચવવામાં ભૂલ આવી"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"સમસ્યા રેકોર્ડિંગને શરૂ કરવામાં ભૂલ આવી"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"પૂર્ણ સ્ક્રીન જોઈ રહ્યાં છે"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"બહાર નીકળવા માટે, સૌથી ઉપરથી નીચેની તરફ સ્વાઇપ કરો."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"સમજાઈ ગયું"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"સાચવેલું"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ડિસ્કનેક્ટ કરો"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"સક્રિય કરો"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"આવતીકાલે ફરીથી ઑટોમૅટિક રીતે ચાલુ કરો"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"ક્વિક શેર, Find My Device અને ડિવાઇસના લોકેશન જેવી સુવિધાઓ બ્લૂટૂથનો ઉપયોગ કરે છે"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> બૅટરી"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ઑડિયો"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"હૅડસેટ"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"આ ડિવાઇસ તમારા માતાપિતા દ્વારા મેનેજ કરવામાં આવે છે. તમે જેનો ઉપયોગ કરો છો તે ઍપ, તમારું સ્થાન અને તમારા સ્ક્રીન સમય જેવી માહિતીને તમારા માતાપિતા જોઈ અને મેનેજ કરી શકે છે."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent દ્વારા અનલૉક રાખેલું"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"ચોરીથી સુરક્ષા\nડિવાઇસ અનલૉક કર્યું, અનલૉક કરવાના ઘણા પ્રયાસો"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"સાઉન્ડ સેટિંગ"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"મીડિયામાં કૅપ્શન ઑટોમૅટિક રીતે ઉમેરો"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"બંધ કરો"</string>
<string name="sound_settings" msgid="8874581353127418308">"સાઉન્ડ અને વાઇબ્રેશન"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"સેટિંગ"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"લાઇવ કૅપ્શન"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"વૉલ્યૂમને વધુ સલામત લેવલ સુધી ઘટાડ્યું"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"હૅડફોનનું વૉલ્યૂમ સુઝાવ આપેલા સમય કરતાં વધારે સમય સુધી ઊંચા વૉલ્યૂમ પર રહ્યું છે"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"હૅડફોનનું વૉલ્યૂમ આ અઠવાડિયા માટેની સુરક્ષિત મર્યાદા કરતાં વધારે છે"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"વાઇબ્રેટ"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s વૉલ્યૂમ નિયંત્રણો"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"કૉલ અને નોટિફિકેશનની રિંગ (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>) પર વાગશે"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> વગાડી રહ્યાં છીએ"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"ઑડિયો આની પર વાગશે"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"સિસ્ટમ UI ટ્યૂનર"</string>
<string name="status_bar" msgid="4357390266055077437">"સ્ટેટસ બાર"</string>
<string name="demo_mode" msgid="263484519766901593">"સિસ્ટમ UI ડેમો મોડ"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> વાગ્યે"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> એ"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"હૉટસ્પૉટ"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"સૅટલાઇટ, કોઈ કનેક્શન નથી"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"સૅટલાઇટ, નબળું કનેક્શન"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"સૅટલાઇટ, સારું કનેક્શન"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"સૅટલાઇટ, કનેક્શન ઉપલબ્ધ છે"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"ઑફિસની પ્રોફાઇલ"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"કેટલાક માટે મજા પરંતુ બધા માટે નહીં"</string>
<string name="tuner_warning" msgid="1861736288458481650">"સિસ્ટમ UI ટ્યૂનર તમને Android વપરાશકર્તા ઇન્ટરફેસને ટ્વીક અને કસ્ટમાઇઝ કરવાની વધારાની રીતો આપે છે. ભાવિ રીલિઝેસમાં આ પ્રાયોગિક સુવિધાઓ બદલાઈ, ભંગ અથવા અદૃશ્ય થઈ શકે છે. સાવધાની સાથે આગળ વધો."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"સેટિંગ ખોલો"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Assistant ખોલો"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"લૉક સ્ક્રીન"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"નોંધ ખોલો"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"નોંધ લો"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"સિસ્ટમ દ્વારા એકથી વધુ કાર્યો કરવા"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"જમણી બાજુ પર હાલની ઍપ સાથે વિભાજિત સ્ક્રીનમાં દાખલ થાઓ"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"ડાબી બાજુ પર હાલની ઍપ સાથે વિભાજિત સ્ક્રીનમાં દાખલ થાઓ"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 372d83d4769a..8d1ac11b933f 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"देखने के लिए टैप करें"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"स्क्रीन रिकॉर्डिंग सेव करते समय गड़बड़ी हुई"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"स्क्रीन को रिकॉर्ड करने में गड़बड़ी आ रही है"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"समस्या का डेटा सेव करने वाला टूल"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"समस्या का डेटा प्रोसेस हो रहा"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"समस्या का डेटा इकट्ठा करने के लिए बैकग्राउंड में जारी गतिविधि की सूचना"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"समस्या का डेटा इकट्ठा किया जा रहा है"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"शेयर करें"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"समस्या का डेटा सेव किया गया"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"देखने के लिए टैप करें"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"समस्या का डेटा सेव करते समय गड़बड़ी हुई"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"समस्या का डेटा सेव करने की प्रोसेस शुरू करते समय गड़बड़ी हुई"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"फ़ुल स्क्रीन मोड पर देखा जा रहा है"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"बाहर निकलने के लिए, सबसे ऊपर से नीचे की ओर स्वाइप करें."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"ठीक है"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"सेव किया गया"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"डिसकनेक्ट करें"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"चालू करें"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"कल फिर से अपने-आप चालू हो जाएगा"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"क्विक शेयर, Find My Device, और डिवाइस की जगह की जानकारी जैसी सुविधाएं ब्लूटूथ का इस्तेमाल करती हैं"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> बैटरी"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ऑडियो"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"इस डिवाइस का प्रबंधन आपके अभिभावक करते हैं. अभिभावक आपके डिवाइस से जुड़ी जानकारी देख सकते हैं. साथ ही, इसे प्रबंधित कर सकते हैं. इनमें आपके इस्तेमाल किए गए ऐप्लिकेशन, जगह की जानकारी, और डिवाइस के इस्तेमाल में बिताए गए समय जैसी जानकारी शामिल है."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"वीपीएन"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent की वजह से अनलॉक रखा गया है"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"चोरी से सुरक्षा\nडिवाइस लॉक हो गया, अनलॉक की कई कोशिशें की गईं"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"साउंड सेटिंग"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"ऑडियो-वीडियो से अपने-आप कैप्शन बनना"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"बंद करें"</string>
<string name="sound_settings" msgid="8874581353127418308">"आवाज़ और वाइब्रेशन"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"सेटिंग"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"लाइव कैप्शन"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"आवाज़ को कम करके, सुरक्षित लेवल पर सेट कर दिया गया है"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"हेडफ़ोन की आवाज़ सुझाए गए समय से देर तक ज़्यादा रही"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"इस हफ़्ते के लिए हेडफ़ोन की आवाज़, सुझाई गई सीमा से ज़्यादा हो गई है"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"वाइब्रेशन की सुविधा चालू करें"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s की आवाज़ कम या ज़्यादा करने की सुविधा"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"कॉल और सूचनाएं आने पर घंटी बजेगी (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> को चलाया जा रहा है"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"ऑडियो इसमें चलेगा"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"सिस्टम यूज़र इंटरफ़ेस (यूआई) ट्यूनर"</string>
<string name="status_bar" msgid="4357390266055077437">"स्टेटस बार"</string>
<string name="demo_mode" msgid="263484519766901593">"सिस्टम यूज़र इंटरफ़ेस (यूआई) डेमो मोड"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> बजे"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> पर"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"हॉटस्पॉट"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"सैटलाइट कनेक्शन उपलब्ध नहीं है"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"सैटलाइट कनेक्शन खराब है"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"सैटलाइट कनेक्शन अच्छा है"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"सैटलाइट कनेक्शन उपलब्ध है"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"वर्क प्रोफ़ाइल"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"कुछ के लिए मज़ेदार लेकिन सबके लिए नहीं"</string>
<string name="tuner_warning" msgid="1861736288458481650">"सिस्टम यूज़र इंटरफ़ेस (यूआई) ट्यूनर, आपको Android यूज़र इंटरफ़ेस में सुधार लाने और उसे अपनी पसंद के हिसाब से बदलने के कुछ और तरीके देता है. प्रयोग के तौर पर इस्तेमाल हो रहीं ये सुविधाएं आगे चल कर रिलीज़ की जा सकती हैं, रोकी जा सकती हैं या दिखाई देना बंद हो सकती हैं. सावधानी से आगे बढ़ें."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"सेटिंग खोलें"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Google Assistant खोलें"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"लॉक स्क्रीन"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Notes ऐप्लिकेशन खोलें"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"नोट करें"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"सिस्टम मल्टीटास्किंग"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"स्प्लिट स्क्रीन का इस्तेमाल करके, मौजूदा ऐप्लिकेशन को दाईं ओर ले जाएं"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"स्प्लिट स्क्रीन का इस्तेमाल करके, मौजूदा ऐप्लिकेशन को बाईं ओर ले जाएं"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 196cddb0c896..d4e460ddf9ca 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Dodirnite za prikaz"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Pogreška prilikom spremanja snimke zaslona"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Pogreška prilikom pokretanja snimanja zaslona"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Snimač poteškoća"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Obrada snimke poteškoće"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Obavijest o aktivnosti u pozadini za sesiju prikupljanja poteškoća"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Snimanje poteškoće"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Dijeljenje"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Snimka poteškoće spremljena"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Dodirnite za prikaz"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Pogreška pri spremanju snimke poteškoće"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Pogreška pri pokretanju snimke poteškoće"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Gledanje preko cijelog zaslona"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Za izlaz prijeđite prstom od vrha prema dolje."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Shvaćam"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Spremljeno"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"prekini vezu"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiviraj"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatski ponovo uključi sutra"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Značajke kao što su brzo dijeljenje, Pronađi moj uređaj i lokacija uređaja upotrebljavaju Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> baterije"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Ovim uređajem upravlja tvoj roditelj. Tvoj roditelj može vidjeti podatke kao što su aplikacije kojima se koristiš, lokaciju i vrijeme upotrebe te upravljati njima."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Otključano održava TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Zaštita od krađe\nUređaj zaključan, previše pokušaja otključavanja"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Postavke zvuka"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatski titlovi za medije"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"onemogući"</string>
<string name="sound_settings" msgid="8874581353127418308">"Zvuk i vibracija"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Postavke"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Automatski titlovi"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Glasnoća je stišana na sigurniju razinu"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Glasnoća u slušalicama pojačana je dulje nego što se preporučuje"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Glasnoća slušalica premašila je sigurno ograničenje za ovaj tjedan"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibriranje"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Kontrole glasnoće – %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Telefon će zvoniti za pozive i obavijesti (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Reproducira se – <xliff:g id="LABEL">%s</xliff:g>"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Zvuk će se reproducirati"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Ugađanje korisničkog sučelja sustava"</string>
<string name="status_bar" msgid="4357390266055077437">"Traka statusa"</string>
<string name="demo_mode" msgid="263484519766901593">"Demo način korisničkog sučelja sustava"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"u <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Žarišna točka"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Satelit, nije uspostavljena veza"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Satelit, slaba veza"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, dobra veza"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, veza je dostupna"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Poslovni profil"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Zabava za neke, ali ne za sve"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Ugađanje korisničkog sučelja sustava pruža vam dodatne načine za prilagodbu korisničkog sučelja Androida. Te se eksperimentalne značajke mogu promijeniti, prekinuti ili nestati u budućim izdanjima. Nastavite uz oprez."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Otvori postavke"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Otvori Asistenta"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Zaključavanje zaslona"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Otvori bilješke"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Zapišite bilješku"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Obavljanje više zadataka sustava"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Otvori podijeljeni zaslon s trenutačnom aplikacijom s desne strane"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Otvori podijeljeni zaslon s trenutačnom aplikacijom s lijeve strane"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 47f740eeddaf..7c62c7a813e8 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Koppintson a megtekintéshez"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Hiba történt a képernyőrögzítés mentése során"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Hiba a képernyőrögzítés indításakor"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Problémafelvevő"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Problémafelvétel feldolgozása…"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Folyamatban lévő értesítés egy problémagyűjtési munkamenethez"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Problémafelvétel folyamatban…"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Megosztás"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Problémafelvétel mentve"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Koppintson a megtekintéshez"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Hiba történt a problémafelvétel mentésekor"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Hiba történt a problémafelvétel elindításakor"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Megtekintése teljes képernyőn"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Kilépéshez csúsztassa ujját fentről lefelé."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Értem"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Mentve"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"leválasztás"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiválás"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatikus visszakapcsolás holnap"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Az olyan funkciók, mint a Quick Share, a Készülékkereső és az eszköz helyadatai Bluetootht használnak"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akkumulátor: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Hang"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Az eszközt a szülőd felügyeli. A szülőd megtekintheti és kezelheti például a használt alkalmazásokra, a tartózkodási helyre és a képernyőidőre vonatkozó adatokat."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Feloldva tartva TrustAgent által"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Lopásgátlás\nTúl sok feloldási kísérlet, eszköz zárolva"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Hangbeállítások"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatikus feliratozás"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"letiltás"</string>
<string name="sound_settings" msgid="8874581353127418308">"Hang és rezgés"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Beállítások"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Élő feliratozás"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Hangerő biztonságos szintre csökkentve"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"A fejhallgató hangereje az ajánlottnál hosszabb ideig volt nagy"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"A fejhallgató hangereje túllépte a biztonságos határt a hétre vonatkozóan"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"rezgés"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s hangerőszabályzók"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"A hívásoknál és értesítéseknél csörög a telefon (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> lejátszása itt:"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Hang lejátszása itt:"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Kezelőfelület-hangoló"</string>
<string name="status_bar" msgid="4357390266055077437">"Állapotsor"</string>
<string name="demo_mode" msgid="263484519766901593">"A rendszer kezelőfelületének demómódja"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"ekkor: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"ezen a napon: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Hotspot"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Műhold, nincs kapcsolat"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Műhold, gyenge kapcsolat"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Műhold, jó kapcsolat"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Műhold, van rendelkezésre álló kapcsolat"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Munkaprofil"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Egyeseknek tetszik, másoknak nem"</string>
<string name="tuner_warning" msgid="1861736288458481650">"A Kezelőfelület-hangoló az Android felhasználói felületének szerkesztéséhez és testreszabásához nyújt további megoldásokat. Ezek a kísérleti funkciók változhatnak vagy megsérülhetnek a későbbi kiadásokban, illetve eltűnhetnek azokból. Körültekintően járjon el."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Beállítások megnyitása"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"A Segéd megnyitása"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Lezárási képernyő"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Jegyzetek megnyitása"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Jegyzetelés"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Rendszermultitasking"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Osztott képernyő aktiválása; az aktuális alkalmazás kerüljön jobbra"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Osztott képernyő aktiválása; az aktuális alkalmazás kerüljön balra"</string>
@@ -1242,7 +1235,7 @@
<string name="assistant_attention_content_description" msgid="4166330881435263596">"Felhasználói jelenlét észlelve"</string>
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Állítson be alapértelmezett jegyzetkészítő alkalmazást a Beállításokban"</string>
<string name="install_app" msgid="5066668100199613936">"Alkalmazás telepítése"</string>
- <string name="dismissible_keyguard_swipe" msgid="8377597870094949432">"A folytatáshoz csúsztassa gyorsan fel az ujját"</string>
+ <string name="dismissible_keyguard_swipe" msgid="8377597870094949432">"A folytatáshoz csúsztassa felfelé az ujját"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Tükrözi a kijelzőt a külső képernyőre?"</string>
<string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"A belső kijelző tükrözve lesz. Az elülső kijelző ki lesz kapcsolva."</string>
<string name="mirror_display" msgid="2515262008898122928">"Kijelző tükrözése"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index f8f30cd1ea45..8427fc3b683f 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Հպեք՝ դիտելու համար"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Չհաջողվեց պահել էկրանի տեսագրությունը"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Չհաջողվեց սկսել տեսագրումը"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Խնդիրների տեսագրիչ"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Մշակում ենք տեսագրությունը"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Խնդրի տեսագրման մասին ընթացիկ ծանուցում"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Տեսագրում ենք խնդիրը"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Կիսվել"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Տեսագրությունը պահվեց"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Հպեք՝ դիտելու համար"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Չհաջողվեց պահել տեսագրությունը"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Չհաջողվեց սկսել տեսագրումը"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Լիաէկրան դիտակերպ"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Դուրս գալու համար վերևից մատը սահեցրեք դեպի ներքև։"</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Եղավ"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Պահված է"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"անջատել"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ակտիվացնել"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Վաղը նորից ավտոմատ միացնել"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Գործառույթները, ինչպիսիք են Quick Share-ը, «Գտնել իմ սարքը» գործառույթը և սարքի տեղորոշումը, օգտագործում են Bluetooth-ը"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Մարտկոցի լիցքը՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Աուդիո"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ականջակալ"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Այս սարքը կառավարում է ձեր ծնողը։ Նա կարող է դիտել և փոփոխել որոշակի տեղեկություններ, օրինակ՝ հավելվածները, որոնք դուք օգտագործում եք, ձեր տեղադրությունը և սարքի օգտագործման ժամանակը։"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Ապակողպվում է TrustAgent-ի միջոցով"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Գողությունից պաշտպանություն\nՍարքը կողպվել է, ապակողպման շատ փորձեր"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Ձայնի կարգավորումներ"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Ավտոմատ ավելացնել ենթագրեր"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"անջատել"</string>
<string name="sound_settings" msgid="8874581353127418308">"Ձայն և թրթռոց"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Կարգավորումներ"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Կենդանի ենթագրեր"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Ձայնն իջեցվեց անվտանգ մակարդակի"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Ձայնը բարձր է եղել առաջարկված ժամանակահատվածից ավելի երկար"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Ականջակալների ձայնի ուժգնությունը այս շաբաթ գերազանցել է անվտանգ մակարդակի շեմը"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"միացնել թրթռոցը"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Ձայնի ուժգնության կառավարներ` %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Զանգերի և ծանուցումների համար հեռախոսի ձայնը միացված է (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g>. նվագարկվում է"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Աուդիոն կնվագարկվի"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Համակարգի ՕՄ-ի կարգավորիչ"</string>
<string name="status_bar" msgid="4357390266055077437">"Կարգավիճակի գոտի"</string>
<string name="demo_mode" msgid="263484519766901593">"Համակարգի միջերեսի ցուցադրական ռեժիմ"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>-ին"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>-ին"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Թեժ կետ"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Արբանյակային կապ չկա"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Արբանյակային թույլ կապ"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Արբանյակային լավ կապ"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Հասանելի է արբանյակային կապ"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Աշխատանքային պրոֆիլ"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Զվարճանք մեկ՝ որոշակի մարդու համար"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Համակարգի ՕՄ-ի կարգավորիչը հնարավորություն է տալիս հարմարեցնել Android-ի օգտատիրոջ միջերեսը: Այս փորձնական գործառույթները կարող են հետագա թողարկումների մեջ փոփոխվել, խափանվել կամ ընդհանրապես չհայտնվել: Եթե շարունակում եք, զգուշացեք:"</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Բացել կարգավորումները"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Բացել Օգնականը"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Կողպէկրան"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Բացել նշումները"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Ստեղծել նշում"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Համակարգի բազմախնդրության ռեժիմ"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Միացնել էկրանի տրոհումը՝ ընթացիկ հավելվածն աջ կողմում"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Միացնել էկրանի տրոհումը՝ ընթացիկ հավելվածը ձախ կողմում"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 8fca71fb6f1d..a2f05b95026f 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Ketuk untuk melihat"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Terjadi error saat menyimpan rekaman layar"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Terjadi error saat memulai perekaman layar"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Perekam Masalah"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Memproses rekaman masalah"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Notifikasi berkelanjutan untuk sesi pengumpulan masalah"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Merekam masalah"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Bagikan"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Rekaman masalah disimpan"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Ketuk untuk melihat"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Terjadi error saat menyimpan rekaman masalah"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Terjadi error saat memulai perekaman masalah"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Melihat layar penuh"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Untuk keluar, geser layar ke bawah dari atas."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Oke"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Disimpan"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"putuskan koneksi"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktifkan"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Otomatis aktifkan lagi besok"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Fitur seperti Quick Share, Temukan Perangkat Saya, dan lokasi perangkat menggunakan Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterai <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Perangkat ini dikelola oleh orang tuamu. Orang tuamu bisa melihat dan mengelola berbagai informasi, seperti aplikasi yang kamu gunakan, lokasimu, dan lama pemakaian perangkat."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Tetap terbuka kuncinya oleh TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Perlindungan pencurian\nPerangkat dikunci, terlalu banyak upaya pembukaan kunci"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Setelan suara"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Otomatis beri teks di media"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"nonaktifkan"</string>
<string name="sound_settings" msgid="8874581353127418308">"Suara &amp; getaran"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Setelan"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Teks Otomatis"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Volume diturunkan ke level yang lebih aman"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Volume headphone tinggi untuk waktu lebih lama dari yang direkomendasikan"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Volume headphone telah melampaui batas aman untuk minggu ini"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"getar"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s kontrol volume"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Panggilan telepon dan notifikasi akan berdering (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Memutar <xliff:g id="LABEL">%s</xliff:g> di"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio akan diputar di"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Penyetel Antarmuka Pengguna Sistem"</string>
<string name="status_bar" msgid="4357390266055077437">"Bilah status"</string>
<string name="demo_mode" msgid="263484519766901593">"Mode demo UI sistem"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"pukul <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"pada <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Hotspot"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Satelit, tidak ada koneksi"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Satelit, koneksi buruk"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, koneksi baik"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, koneksi tersedia"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Profil kerja"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Tidak semua orang menganggapnya baik"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Penyetel Antarmuka Pengguna Sistem memberikan cara tambahan untuk mengubah dan menyesuaikan antarmuka pengguna Android. Fitur eksperimental ini dapat berubah, rusak, atau menghilang dalam rilis di masa mendatang. Lanjutkan dengan hati-hati."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Buka setelan"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Buka Asisten"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Kunci layar"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Buka catatan"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Buat catatan"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Multitasking sistem"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Masuk ke layar terpisah dengan aplikasi saat ini ke RHS"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Masuk ke layar terpisah dengan aplikasi saat ini ke LHS"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 7cec52355331..9c9a29f513fc 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Ýttu til að skoða"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Villa við að vista skjáupptöku"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Villa við að hefja upptöku skjás"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Upptökutæki fyrir vandamál"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Vinnur úr upptöku af vandamáli"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Áframhaldandi tilkynning fyrir lotu vandamálasafns"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Tekur upp vandamál"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Deila"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Upptaka af vandamáli var vistuð"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Ýttu til að skoða"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Villa kom upp við að vista upptöku af vandamáli"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Villa kom upp við að hefja upptöku af vandamáli"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Notar allan skjáinn"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Strjúktu niður frá efsta hluta skjásins til að hætta."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Ég skil"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Vistað"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"aftengja"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"virkja"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Kveikja sjálfkrafa aftur á morgun"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Eiginleikar á borð við flýtideilingu, „Finna tækið mitt“ og staðsetningu tækis nota Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> rafhlöðuhleðsla"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Hljóð"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Höfuðtól"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Foreldri þitt stjórnar þessu tæki. Foreldri þitt getur séð og stjórnað upplýsingum eins og forritunum sem þú notar, staðsetningu þinni og skjátímanum."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Haldið opnu af TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Þjófavörn\nTækinu var læst vegna of margra tilrauna til að taka það úr lás"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Hljóðstillingar"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Sjálfvirkir skjátextar"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"slökkva"</string>
<string name="sound_settings" msgid="8874581353127418308">"Hljóð og titringur"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Stillingar"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Skjátextar í rauntíma"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Hljóð lækkað í öruggari hljóðstyrk"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Hljóðstyrkur í heyrnartólum hefur verið hár í lengri tíma en mælt er með"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Hljóðstyrkur í heyrnartólum hefur náð öryggismörkum fyrir þessa viku"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"titringur"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s stýringar á hljóstyrk"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Símhringingar og tilkynningar heyrast (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Í spilun í <xliff:g id="LABEL">%s</xliff:g>"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Hljóð heldur áfram að spilast"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Fínstillingar kerfisviðmóts"</string>
<string name="status_bar" msgid="4357390266055077437">"Stöðustika"</string>
<string name="demo_mode" msgid="263484519766901593">"Prufustilling kerfisviðmóts"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Heitur reitur"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Gervihnöttur, engin tenging"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Gervihnöttur, léleg tenging"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Gervihnöttur, góð tenging"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Gervihnöttur, tenging tiltæk"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Vinnusnið"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Þetta er ekki allra"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Fínstillingar kerfisviðmóts gera þér kleift að fínstilla og sérsníða notendaviðmót Android. Þessir tilraunaeiginleikar geta breyst, bilað eða horfið í síðari útgáfum. Gakktu því hægt um gleðinnar dyr."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Opna stillingar"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Opna Hjálpara"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Lásskjár"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Opna glósur"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Skrifa glósu"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Fjölvinnsla kerfis"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Opna skjáskiptingu hægra megin með núverandi forriti"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Opna skjáskiptingu vinstra megin með núverandi forriti"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index f95ce26c9a93..ec58af8fe1af 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Tocca per visualizzare"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Errore durante il salvataggio della registrazione dello schermo"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Errore durante l\'avvio della registrazione dello schermo"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Registratore dei problemi"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Elaborazione registrazione…"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Notifica continua per una sessione di raccolta di problemi"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Problema con la registrazione"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Condividi"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Registrazione del problema salvata"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Tocca per visualizzare"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Impossibile salvare la registrazione del problema"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Impossibile avviare la registrazione del problema"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Visualizzazione a schermo intero"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Per uscire, scorri dall\'alto verso il basso."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"OK"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Dispositivo salvato"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnetti"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"attiva"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Riattiva automaticamente domani"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Funzionalità come Quick Share, Trova il mio dispositivo e la posizione del dispositivo usano il Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batteria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auricolare"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Questo dispositivo è gestito da uno dei tuoi genitori, il quale può visualizzare e gestire informazioni come le app che usi, la tua posizione e il tuo tempo di utilizzo."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Sbloccato da TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Protezione da furti\nDisp. bloccato, troppi tentat. di sblocco"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Impostazioni audio"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Sottotitoli automatici"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"disattiva"</string>
<string name="sound_settings" msgid="8874581353127418308">"Suoni e vibrazione"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Impostazioni"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Sottotitoli in tempo reale"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Volume abbassato a un livello più sicuro"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Il volume delle cuffie è rimasto alto per un periodo superiore a quello consigliato"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Il volume delle cuffie ha superato il limite di sicurezza per questa settimana"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrazione"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Controlli del volume %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"La suoneria sarà attiva per chiamate e notifiche (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> in riproduzione su"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio riprodotto su:"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Ottimizzatore UI di sistema"</string>
<string name="status_bar" msgid="4357390266055077437">"Barra di stato"</string>
<string name="demo_mode" msgid="263484519766901593">"Modalità demo dell\'interfaccia utente di sistema"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"alle <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Hotspot"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Satellitare, nessuna connessione"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Satellitare, connessione debole"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellitare, connessione buona"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellitare, connessione disponibile"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Profilo di lavoro"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Il divertimento riservato a pochi eletti"</string>
<string name="tuner_warning" msgid="1861736288458481650">"L\'Ottimizzatore UI di sistema mette a disposizione altri metodi per modificare e personalizzare l\'interfaccia utente di Android. Queste funzioni sperimentali potrebbero cambiare, interrompersi o scomparire nelle versioni successive. Procedi con cautela."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Apri impostazioni"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Apri l\'assistente"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Blocca lo schermo"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Apri note"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Scrivi una nota"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Multitasking di sistema"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Attiva lo schermo diviso con l\'app corrente a destra"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Attiva lo schermo diviso con l\'app corrente a sinistra"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index bad575c17677..c9a71c71867b 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"יש להקיש כדי להציג"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"שגיאה בשמירה של הקלטת המסך"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"שגיאה בהפעלה של הקלטת המסך"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"בעיה במכשיר ההקלטה"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"מתבצע עיבוד של בעיית ההקלטה"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"התראה מתמשכת לסשן איסוף הבעיה"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"בעיית הקלטה"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"שיתוף"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"בעיית ההקלטה נשמרה"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"אפשר להקיש כדי להציג"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"שגיאה בשמירה של בעיית ההקלטה"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"שגיאה בהפעלה של בעיית ההקלטה"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"צפייה במסך מלא"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"כדי לצאת, מחליקים למטה מהחלק העליון של המסך."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"הבנתי"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"נשמר"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ניתוק"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"הפעלה"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"החיבור יופעל שוב אוטומטית מחר"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"‏תכונות כמו \'שיתוף מהיר\', \'איפה המכשיר שלי\' ומיקום המכשיר משתמשות בחיבור Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> סוללה"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"אודיו"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"אוזניות"</string>
@@ -299,7 +292,7 @@
<string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"אין רשתות זמינות"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="483130889414601732">"‏אין רשתות Wi-Fi זמינות"</string>
<string name="quick_settings_wifi_secondary_label_transient" msgid="7501659015509357887">"בתהליך הפעלה…"</string>
- <string name="quick_settings_cast_title" msgid="2279220930629235211">"העברת מסך"</string>
+ <string name="quick_settings_cast_title" msgid="2279220930629235211">"‏הפעלת Cast למסך"</string>
<string name="quick_settings_casting" msgid="1435880708719268055">"‏מתבצעת העברה (cast)"</string>
<string name="quick_settings_cast_device_default_name" msgid="6988469571141331700">"מכשיר ללא שם"</string>
<string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"אין מכשירים זמינים"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"מכשיר זה מנוהל על ידי ההורה שלך. להורה שלך יש אפשרות לצפות בפרטים כמו האפליקציות שבשימוש, המיקום וזמן המסך שלך, ולנהל אותם."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"הנעילה נמנעת על ידי סביבה מהימנה"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"הגנה מפני גניבה\nהמכשיר ננעל, יותר מדי ניסיונות לביטול הנעילה"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>‏. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"הגדרות צליל"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"הוספת כתוביות באופן אוטומטי למדיה"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"השבתה"</string>
<string name="sound_settings" msgid="8874581353127418308">"צליל ורטט"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"הגדרות"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"כתוביות מיידיות"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"עוצמת הקול הוחלשה לרמה בטוחה יותר"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"עוצמת הקול של האוזניות הייתה גבוהה במשך יותר זמן מהמומלץ"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"עוצמת הקול של האוזניות חרגה ממגבלת הבטיחות לשבוע הזה"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"רטט"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"‏בקרי עוצמת קול של %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"הטלפון יצלצל כשמתקבלות שיחות והתראות (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"הפעלה של <xliff:g id="LABEL">%s</xliff:g> במכשיר"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"האודיו יופעל במכשיר"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"שורת סטטוס"</string>
<string name="demo_mode" msgid="263484519766901593">"מצב הדגמה בממשק המשתמש של המערכת"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"בשעה <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"ב-<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"‏נקודת אינטרנט (hotspot)"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"לוויין, אין חיבור"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"לוויין, חיבור באיכות ירודה"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"לוויין, חיבור באיכות טובה"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"לוויין, יש חיבור זמין"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"פרופיל עבודה"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"מהנה בשביל חלק מהאנשים, אבל לא בשביל כולם"</string>
<string name="tuner_warning" msgid="1861736288458481650">"‏התכונה System UI Tuner מספקת לך דרכים נוספות להתאים אישית את ממשק המשתמש של Android. התכונות הניסיוניות האלה עשויות להשתנות, לא לעבוד כראוי או להיעלם בגרסאות עתידיות. יש להמשיך בזהירות."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"פתיחת ההגדרות"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"‏לפתיחת Google Assistant"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"מסך הנעילה"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"פתיחה של אפליקציית הפתקים"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"כתיבת הערה"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"ריבוי משימות מערכת"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"‏כניסה למסך מפוצל עם האפליקציה הנוכחית ל-RHS"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"‏כניסה למסך מפוצל עם האפליקציה הנוכחית ל-LHS"</string>
@@ -799,7 +792,7 @@
<string name="right_keycode" msgid="2480715509844798438">"קוד מפתח ימני"</string>
<string name="left_icon" msgid="5036278531966897006">"סמל שמאלי"</string>
<string name="right_icon" msgid="1103955040645237425">"סמל ימני"</string>
- <string name="drag_to_add_tiles" msgid="8933270127508303672">"יש ללחוץ ולגרור כדי להוסיף כרטיסי מידע"</string>
+ <string name="drag_to_add_tiles" msgid="8933270127508303672">"יש ללחוץ ולגרור כדי להוסיף לחצנים"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"יש ללחוץ ולגרור כדי לסדר מחדש את כרטיסי המידע"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"אפשר לגרור לכאן כדי להסיר"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"יש צורך ב-<xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> אריחים לפחות"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 89ca0761ed93..dc33b0ddc15d 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -268,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"保存しました"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"接続を解除"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"有効化"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明日自動的に ON に戻す"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"クイック共有、デバイスを探す、デバイスの位置情報などの機能は Bluetooth を使用します"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"バッテリー <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"オーディオ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ヘッドセット"</string>
@@ -528,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"このデバイスは保護者によって管理されています。保護者は、あなたが使用するアプリ、あなたの現在地、デバイスの利用時間などの情報を確認したり、管理したりできます。"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"信頼エージェントがロック解除を管理"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"盗難防止\nデバイスをロック - ロック解除に繰り返し失敗"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>。<xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"音声の設定"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"メディアの自動字幕起こし"</string>
@@ -537,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"無効にする"</string>
<string name="sound_settings" msgid="8874581353127418308">"音とバイブレーション"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"設定"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"自動字幕起こし"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"音量を安全なレベルまで下げました"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"推奨時間よりも長くヘッドフォンが大音量で設定されています"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"ヘッドフォンの音量が今週一週間の安全基準とされる音量、時間を超えています"</string>
@@ -580,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"バイブレーション"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s の音量調節"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"着信音と通知音が鳴ります(<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> を再生:"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"音声の再生形式:"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"システムUI調整ツール"</string>
<string name="status_bar" msgid="4357390266055077437">"ステータスバー"</string>
<string name="demo_mode" msgid="263484519766901593">"システム UI デモモード"</string>
@@ -603,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"アクセスポイント"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"衛生、接続利用不可"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"衛生、接続不安定"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"衛生、接続状態良好"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"衛生、接続利用可能"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"仕事用プロファイル"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"一部の方のみお楽しみいただける限定公開ツール"</string>
<string name="tuner_warning" msgid="1861736288458481650">"システムUI調整ツールでは、Androidユーザーインターフェースの調整やカスタマイズを行えます。これらの試験運用機能は今後のリリースで変更となったり、中止となったり、削除されたりする可能性がありますのでご注意ください。"</string>
@@ -732,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"設定を開く"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"アシスタントを開く"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"画面をロック"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"メモを開く"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"メモを入力する"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"システム マルチタスク"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"分割画面にして現在のアプリを右側に設定する"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"分割画面にして現在のアプリを左側に設定する"</string>
@@ -1233,7 +1235,7 @@
<string name="assistant_attention_content_description" msgid="4166330881435263596">"会話を始められます"</string>
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"[設定] でデフォルトのメモアプリを設定してください"</string>
<string name="install_app" msgid="5066668100199613936">"アプリをインストール"</string>
- <string name="dismissible_keyguard_swipe" msgid="8377597870094949432">"上にスワイプして続行"</string>
+ <string name="dismissible_keyguard_swipe" msgid="8377597870094949432">"上にスワイプして継続"</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"外部ディスプレイにミラーリングしますか?"</string>
<string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"インナー ディスプレイがミラーリングされます。フロント ディスプレイはオフになります。"</string>
<string name="mirror_display" msgid="2515262008898122928">"ディスプレイをミラーリングする"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 774dfe469602..d78bc2072734 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"შეეხეთ სანახავად"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"ეკრანის ჩანაწერის შენახვისას შეცდომა მოხდა"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"ეკრანის ჩაწერის დაწყებისას წარმოიქმნა შეცდომა"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"ჩანაწერის რეკორდერი"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"პრობლემის ჩანაწერის დამუშავება"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"მიმდინარე შეტყობინება პრობლემების შეგროვების სესიისთვის"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"ჩანაწერთან დაკავშირებული პრობლემა"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"გაზიარება"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"პრობლემის ჩანაწერი შენახულია"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"შეეხეთ სანახავად"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"პრობლემის ჩანაწერის შენახვისას წარმოიქმნა შეცდომა"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"პრობლემის ჩაწერის დაწყებისას წარმოიქმნა შეცდომა"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"მიმდინარეობს სრულ ეკრანზე ნახვა"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"გასვლისთვის გადაფურცლეთ ზემოდან ქვემოთ."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"გასაგებია"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"შენახული"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"კავშირის გაწყვეტა"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"გააქტიურება"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ხელახლა ავტომატურად ჩართვა ხვალ"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"ფუნქციები, როგორებიცაა „სწრაფი გაზიარება“, „ჩემი მოწყობილობის პოვნა“ და „მოწყობილობის მდებარეობა“ იყენებენ Bluetooth-ს"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ბატარეა"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"აუდიო"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ყურსაცვამი"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"ამ მოწყობილობას თქვენი მშობელი მართავს. თქვენი მშობელი ხედავს და მართავს ისეთ ინფორმაციას, როგორიც არის თქვენ მიერ გამოყენებული აპები, თქვენი მდებარეობა და თქვენ მიერ ეკრანთან გატარებული დრო."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"განბლოკილია TrustAgent-ის მიერ"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"მოპარვისაგან დაცვა\nდაიბლოკა განბლოკვის ბევრი მცდელობის გამო."</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"ხმის პარამეტრები"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"მედიის ავტომ. სუბტიტრირება"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"გამორთვა"</string>
<string name="sound_settings" msgid="8874581353127418308">"ხმა და ვიბრაცია"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"პარამეტრები"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"ავტოსუბტიტრები"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"ხმა დაწეულია უსაფრთხო დონემდე"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"ხმა მაღალი იყო რეკომენდებულზე მეტი ხნის განმავლობაში"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"ყურსასმენების ხმამ ამ კვირაში უსაფრთხოების ლიმიტს გადააჭარბა"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ვიბრაცია"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s-ის ხმის მართვის საშუალებები"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"ზარებისა და შეტყობინებების მიღებისას დაირეკება (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"უკრავს <xliff:g id="LABEL">%s</xliff:g>:"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"აუდიო დაიკვრება"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"სისტემის UI ტუნერი"</string>
<string name="status_bar" msgid="4357390266055077437">"სტატუსის ზოლი"</string>
<string name="demo_mode" msgid="263484519766901593">"სისტემის UI-ს დემო-რეჟიმი"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>-ზე"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>-ზე"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"წვდომის წერტილი"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"სატელიტური კავშირი არ არის"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"სუსტი სატელიტური კავშირი"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"კარგი სატელიტური კავშირი"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ხელმისაწვდომია სატელიტური კავშირი"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"სამსახურის პროფილი"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"ზოგისთვის გასართობია, მაგრამ არა ყველასთვის"</string>
<string name="tuner_warning" msgid="1861736288458481650">"სისტემის UI ტუნერი გაძლევთ დამატებით გზებს Android-ის სამომხმარებლო ინტერფეისის პარამეტრების დაყენებისთვის. ეს ექსპერიმენტული მახასიათებლები შეიძლება შეიცვალოს, შეწყდეს ან გაქრეს მომავალ ვერსიებში. სიფრთხილით გააგრძელეთ."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"პარამეტრების გახსნა"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"ასისტენტის გახსნა"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"ჩაკეტილი ეკრანი"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"ჩანიშვნების გახსნა"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"ჩაინიშნეთ"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"სისტემის მრავალამოცანიანი რეჟიმი"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"ეკრანის გაყოფის შეყვანა მიმდინარე აპით RHS-ში"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"ეკრანის გაყოფის შეყვანა მიმდინარე აპით LHS-ში"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 24dda1b542b6..407da0b9b8fa 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Көру үшін түртіңіз."</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Экран жазбасын сақтау кезінде қате шықты."</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Экрандағы бейнені жазу кезінде қате шықты."</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Мәселені жазу құралы"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Мәселе жазбасы өңделіп жатыр"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Мәселе туралы дерек жинау сеансына арналған ағымдағы хабарландыру"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Мәселе жазылып жатыр"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Бөлісу"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Мәселе жазбасы сақталды"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Көру үшін түртіңіз."</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Мәселе жазбасын сақтау кезінде қате шықты."</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Мәселені жазуды бастау кезінде қате шықты."</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Толық экранда көру"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Шығу үшін жоғарыдан төмен қарай сырғытыңыз."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Түсінікті"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Сақталды"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ажырату"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"іске қосу"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ертең автоматты түрде қосылсын"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Quick Share, Find My Device сияқты функциялар мен құрылғы локациясы Bluetooth пайдаланады."</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батарея деңгейі: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Aудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string>
@@ -435,10 +428,8 @@
<string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Осы бөлмеде виджеттер қосыңыз, оларды өшіріңіз және ретін өзгертіңіз."</string>
<string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Басқа виджеттер қосыңыз."</string>
<string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Виджеттерді бейімдеу үшін ұзақ басып тұрыңыз."</string>
- <!-- no translation found for button_to_configure_widgets_text (4191862850185256901) -->
- <skip />
- <!-- no translation found for edit_widget (9030848101135393954) -->
- <skip />
+ <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Виджеттерді реттеу"</string>
+ <string name="edit_widget" msgid="9030848101135393954">"Виджетті өзгерту"</string>
<string name="button_to_remove_widget" msgid="3948204829181214098">"Өшіру"</string>
<string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Виджет қосу"</string>
<string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Дайын"</string>
@@ -539,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Бұл құрылғыны ата-анаңыз басқарады. Ата-анаңыз сіз пайдаланатын қолданбалар, геодерегіңіз және пайдалану уақытыңыз сияқты ақпаратты көре және басқара алады."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent арқылы құлпы ашылды."</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Ұрлықтан қорғау\nҚұрылғы құлыпталған, құлыпты ашуға тым көп әрекет жасалды."</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Дыбыс параметрлері"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Автоматты субтитр қосу"</string>
@@ -548,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"өшіру"</string>
<string name="sound_settings" msgid="8874581353127418308">"Дыбыс және діріл"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Параметрлер"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Live Caption"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Дыбыс қауіпсіз деңгейге түсірілді"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Құлақаспаптың жоғары дыбыс деңгейі ұсынылған уақыттан ұзақ қосылып тұрды."</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Құлақаспаптың дыбыс деңгейі осы аптадағы қауіпсіз шектен асып кетті."</string>
@@ -591,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"дірілдету"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Дыбысты басқару элементтері: %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Қоңыраулар мен хабарландырулар дыбысы қосулы (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> ойнатылатын құрылғы:"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Аудио ойнатылатын құрылғы:"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Жүйелік пайдаланушылық интерфейс тюнері"</string>
<string name="status_bar" msgid="4357390266055077437">"Күйін көрсету жолағы"</string>
<string name="demo_mode" msgid="263484519766901593">"Жүйе интерфейсінің демо режимі"</string>
@@ -614,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Хотспот"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Жерсерік, байланыс жоқ."</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Жерсерік, байланыс нашар."</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Жерсерік, байланыс жақсы."</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Жерсерік, байланыс бар."</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Жұмыс профилі"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Кейбіреулерге қызық, бірақ барлығына емес"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Жүйелік пайдаланушылық интерфейс тюнері Android пайдаланушылық интерфейсін реттеудің қосымша жолдарын береді. Бұл эксперименттік мүмкіндіктер болашақ шығарылымдарда өзгеруі, бұзылуы немесе жоғалуы мүмкін. Сақтықпен жалғастырыңыз."</string>
@@ -743,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Параметрлерді ашу"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Assistant-ті ашу"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Экранды құлыптау"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Ескертпелерді ашу"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Ескертпе жазу"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Жүйе мультитаскингі"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Бөлінген экран режиміне кіру (ағымдағы қолданбаны оңға орналастыру)"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Бөлінген экран режиміне кіру (ағымдағы қолданбаны солға орналастыру)"</string>
@@ -975,8 +966,7 @@
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Шетке жылжыту және көрсету"</string>
<string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Өшіру"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ауыстыру"</string>
- <!-- no translation found for accessibility_floating_button_action_edit (1688227814600463987) -->
- <skip />
+ <string name="accessibility_floating_button_action_edit" msgid="1688227814600463987">"Өзгерту"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Құрылғыны басқару элементтері"</string>
<string name="controls_providers_title" msgid="6879775889857085056">"Басқару элементтері қосылатын қолданбаны таңдаңыз"</string>
<string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# басқару элементі қосылды.}other{# басқару элементі қосылды.}}"</string>
@@ -1245,8 +1235,7 @@
<string name="assistant_attention_content_description" msgid="4166330881435263596">"Пайдаланушы анықталды."</string>
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Параметрлерден әдепкі жазба қолданбасын орнатыңыз."</string>
<string name="install_app" msgid="5066668100199613936">"Қолданбаны орнату"</string>
- <!-- no translation found for dismissible_keyguard_swipe (8377597870094949432) -->
- <skip />
+ <string name="dismissible_keyguard_swipe" msgid="8377597870094949432">"Жалғастыру үшін жоғары қарай сырғытыңыз."</string>
<string name="connected_display_dialog_start_mirroring" msgid="6237895789920854982">"Сыртқы экран арқылы да көрсету керек пе?"</string>
<string name="connected_display_dialog_dual_display_stop_warning" msgid="4174707498892447947">"Ішкі экран көшірмесі көрсетіледі. Алдыңғы экран өшіріледі."</string>
<string name="mirror_display" msgid="2515262008898122928">"Көрсету"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 81d2cef3c38f..eaca65732975 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"ចុចដើម្បីមើល"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"មានបញ្ហាក្នុងការរក្សាទុក​ការថតវីដេអូអេក្រង់"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"មានបញ្ហា​ក្នុងការ​ចាប់ផ្ដើម​ថត​អេក្រង់"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"កម្មវិធីកត់ត្រាបញ្ហា"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"កំពុងដំណើរការការកត់ត្រាបញ្ហា"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"ការជូនដំណឹងដែលកំពុងបន្តសម្រាប់វគ្គប្រមូលបញ្ហា"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"កំពុងកត់ត្រាបញ្ហា"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"ចែករំលែក"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"បានរក្សាទុកកំណត់ត្រាបញ្ហា"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"ចុចដើម្បីមើល"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"មានបញ្ហាក្នុងការរក្សាទុកកំណត់ត្រាបញ្ហា"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"មានបញ្ហាក្នុងការចាប់ផ្ដើមកត់ត្រាបញ្ហា"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"កំពុងមើលពេញអេក្រង់"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"ដើម្បីចាកចេញ សូមអូសពីលើចុះក្រោម។"</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"យល់ហើយ"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"បាន​រក្សាទុក"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ផ្ដាច់"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"បើកដំណើរការ"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"បើកដោយស្វ័យប្រវត្តិម្ដងទៀតនៅថ្ងៃស្អែក"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"មុខងារដូចជា Quick Share, រកឧបករណ៍របស់ខ្ញុំ និងប៊្លូធូសប្រើប្រាស់ទីតាំងឧបករណ៍"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"ថ្ម <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"សំឡេង"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"កាស"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"ឧបករណ៍​នេះ​ស្ថិត​ក្រោម​ការ​គ្រប់គ្រង​របស់មាតាបិតាអ្នក។ មាតាបិតារបស់អ្នកអាចមើល និងគ្រប់គ្រងព័ត៌មាន​ដូចជា កម្មវិធីដែលអ្នកប្រើ ទីតាំងរបស់អ្នក និងរយៈពេលប្រើប្រាស់ឧបករណ៍របស់អ្នកជាដើម។"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"បាន​ដោះសោ​ដោយភ្នាក់ងារ​​ទុកចិត្ត"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"ការការពារ​ចោរ​លួច\nបានចាក់សោឧបករណ៍ ការព្យាយាមដោះសោច្រើនដងពេក"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"ការកំណត់សំឡេង"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"ដាក់អក្សររត់លើមេឌៀដោយស្វ័យប្រវត្តិ"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"បិទ"</string>
<string name="sound_settings" msgid="8874581353127418308">"សំឡេង និងការញ័រ"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"ការកំណត់"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"អក្សររត់ក្នុងពេលជាក់ស្ដែង"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"បានបន្ថយកម្រិតសំឡេងមកកម្រិតដែលកាន់តែមានសុវត្ថិភាព"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"កម្រិតសំឡេងកាសមានកម្រិតខ្ពស់យូរជាងរយៈពេលដែលបានណែនាំ"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"កម្រិតសំឡេងកាសបានលើសដែនកំណត់សុវត្ថិភាពសម្រាប់សប្ដាហ៍នេះ"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ញ័រ"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s របារ​បញ្ជា​កម្រិត​សំឡេង"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"ការ​ហៅ​ទូរសព្ទ និង​ការជូន​ដំណឹង​នឹង​រោទ៍ (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"កំពុងចាក់​​ <xliff:g id="LABEL">%s</xliff:g> នៅ​លើ"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"សំឡេងនឹងលេងនៅលើ"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"កម្មវិធីសម្រួល UI ប្រព័ន្ធ"</string>
<string name="status_bar" msgid="4357390266055077437">"របារស្ថានភាព"</string>
<string name="demo_mode" msgid="263484519766901593">"មុខងារ​សាកល្បង​ UI ប្រព័ន្ធ"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"នៅ <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"នៅ <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"ហតស្ប៉ត"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"ផ្កាយរណប មិនមានការតភ្ជាប់ទេ"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"ផ្កាយរណប ការតភ្ជាប់ខ្សោយ"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ផ្កាយរណប មានការតភ្ជាប់ល្អ"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ផ្កាយរណប អាចតភ្ជាប់បាន"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"កម្រងព័ត៌មានការងារ"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"ល្អសម្រាប់អ្នកប្រើមួយចំនួន តែមិនសម្រាប់គ្រប់គ្នាទេ"</string>
<string name="tuner_warning" msgid="1861736288458481650">"កម្មវិធីសម្រួល UI ប្រព័ន្ធផ្តល់ជូនអ្នកនូវមធ្យោបាយបន្ថែមទៀតដើម្បីកែសម្រួល និងប្តូរចំណុចប្រទាក់អ្នកប្រើ Android តាមបំណង។ លក្ខណៈពិសេសសាកល្បងនេះអាចនឹងផ្លាស់ប្តូរ បំបែក ឬបាត់បង់បន្ទាប់ពីការចេញផ្សាយនាពេលអនាគត។ សូមបន្តដោយប្រុងប្រយ័ត្ន។"</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"បើកការកំណត់"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"បើក​ជំនួយការ"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"ចាក់​សោ​អេក្រង់"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"បើក​កំណត់ចំណាំ"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"កត់​ចំណាំ"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"ការដំណើរការបានច្រើននៃប្រព័ន្ធ"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"ចូលក្នុងមុខងារបំបែកអេក្រង់ដោយប្រើកម្មវិធីបច្ចុប្បន្ននៅខាងស្ដាំដៃ"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"ចូលក្នុងមុខងារ​បំបែកអេក្រង់ដោយប្រើកម្មវិធីបច្ចុប្បន្ននៅខាងឆ្វេងដៃ"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 31dbbb55994c..64179ba03a3a 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"ವೀಕ್ಷಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡಿಂಗ್ ಸೇವ್‌ ಮಾಡುವಾಗ ದೋಷ ಎದುರಾಗಿದೆ"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡಿಂಗ್ ಪ್ರಾರಂಭಿಸುವಾಗ ದೋಷ ಕಂಡುಬಂದಿದೆ"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"ಸಮಸ್ಯೆ ರೆಕಾರ್ಡರ್"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"ಸಮಸ್ಯೆ ರೆಕಾರ್ಡಿಂಗ್ ಪ್ರಕ್ರಿಯೆ…"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"ಸಮಸ್ಯೆ ಸಂಗ್ರಹಣೆಯ ಸೆಶನ್‌ಗಾಗಿ ಜರುಗುತ್ತಿರುವ ನೋಟಿಫಿಕೇಶನ್"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"ರೆಕಾರ್ಡಿಂಗ್ ಸಮಸ್ಯೆ"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"ಹಂಚಿಕೊಳ್ಳಿ"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"ಸಮಸ್ಯೆಯ ರೆಕಾರ್ಡಿಂಗ್ ಅನ್ನು ಸೇವ್ ಮಾಡಲಾಗಿದೆ"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"ವೀಕ್ಷಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"ಸಮಸ್ಯೆಯ ರೆಕಾರ್ಡಿಂಗ್ ಅನ್ನು ಸೇವ್‌ ಮಾಡುವಾಗ ದೋಷ ಎದುರಾಗಿದೆ"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"ಸಮಸ್ಯೆಯ ರೆಕಾರ್ಡಿಂಗ್ ಅನ್ನು ಪ್ರಾರಂಭಿಸುವಲ್ಲಿ ದೋಷ ಎದುರಾಗಿದೆ"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"ಪೂರ್ಣ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ವೀಕ್ಷಿಸಲಾಗುತ್ತಿದೆ"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"ನಿರ್ಗಮಿಸಲು, ಮೇಲಿನಿಂದ ಕೆಳಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"ಅರ್ಥವಾಯಿತು"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ಸೇವ್ ಮಾಡಲಾಗಿದೆ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ಡಿಸ್‌ಕನೆಕ್ಟ್ ಮಾಡಿ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ಸಕ್ರಿಯಗೊಳಿಸಿ"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ನಾಳೆ ಪುನಃ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಆನ್ ಮಾಡಿ"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"ಕ್ವಿಕ್ ಶೇರ್, Find My Device ನಂತಹ ಫೀಚರ್‌ಗಳು ಮತ್ತು ಸಾಧನದ ಸ್ಥಳ ಬ್ಲೂಟೂತ್ ಬಳಸುತ್ತವೆ"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ಬ್ಯಾಟರಿ"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ಆಡಿಯೋ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ಹೆಡ್‌ಸೆಟ್"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"ಈ ಸಾಧನವನ್ನು ನಿಮ್ಮ ಪೋಷಕರು ನಿರ್ವಹಿಸುತ್ತಿದ್ದಾರೆ. ನೀವು ಬಳಸುವ ಆ್ಯಪ್‌ಗಳು, ನಿಮ್ಮ ಸ್ಥಳ ಮತ್ತು ನಿಮ್ಮ ವೀಕ್ಷಣಾ ಅವಧಿಯಂತಹ ಮಾಹಿತಿಯನ್ನು ನಿಮ್ಮ ಪೋಷಕರು ನೋಡಬಹುದು ಮತ್ತು ನಿರ್ವಹಿಸಬಹುದು."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent ನಿಂದ ಅನ್‌ಲಾಕ್ ಮಾಡಲಾಗಿದೆ"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"ಕಳ್ಳತನದ ರಕ್ಷಣೆ\nಸಾಧನ ಲಾಕ್ ಆಗಿದೆ, ಅನ್‌ಲಾಕ್‌ಗೆ ಹೆಚ್ಚು ಪ್ರಯತ್ನ..."</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"ಸೌಂಡ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"ಸ್ವಯಂಚಾಲಿತ ಶೀರ್ಷಿಕೆ ಮಾಧ್ಯಮ"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string>
<string name="sound_settings" msgid="8874581353127418308">"ಧ್ವನಿ &amp; ವೈಬ್ರೇಷನ್"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"ಲೈವ್ ಕ್ಯಾಪ್ಶನ್"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"ವಾಲ್ಯೂಮ್ ಅನ್ನು ಸುರಕ್ಷಿತ ಮಟ್ಟಕ್ಕೆ ತಗ್ಗಿಸಲಾಗಿದೆ"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"ಶಿಫಾರಸು ಮಾಡಿದ್ದಕ್ಕಿಂತಲೂ ದೀರ್ಘಕಾಲ ಹೆಡ್‌ಫೋನ್‌ನ ವಾಲ್ಯೂಮ್‌ ಹೆಚ್ಚಿಗೆ ಇದೆ"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"ಹೆಡ್‌ಫೋನ್‌ನ ವಾಲ್ಯೂಮ್ ಈ ವಾರದ‌ ಮಟ್ಟಿಗೆ ಸುರಕ್ಷಿತ ಮಿತಿಯನ್ನು ಮೀರಿದೆ"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ವೈಬ್ರೇಟ್‌"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s ವಾಲ್ಯೂಮ್ ನಿಯಂತ್ರಕಗಳು"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"(<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>) ನಲ್ಲಿ ಕರೆಗಳು ಮತ್ತು ಅಧಿಸೂಚನೆಗಳು ರಿಂಗ್ ಆಗುತ್ತವೆ"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> ನಲ್ಲಿ ಪ್ಲೇ ಆಗು..."</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"ಇಲ್ಲಿ ಆಡಿಯೋ ಪ್ಲೇ..."</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"ಸಿಸ್ಟಂ UI ಟ್ಯೂನರ್"</string>
<string name="status_bar" msgid="4357390266055077437">"ಸ್ಥಿತಿ ಪಟ್ಟಿ"</string>
<string name="demo_mode" msgid="263484519766901593">"ಸಿಸ್ಟಂ UI ಡೆಮೋ ಮೋಡ್"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> ರಲ್ಲಿ"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> ರಂದು"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"ಹಾಟ್‌ಸ್ಪಾಟ್"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"ಸ್ಯಾಟಲೈಟ್‌, ಯಾವುದೇ ಕನೆಕ್ಷನ್ ಇಲ್ಲ"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"ಸ್ಯಾಟಲೈಟ್‌, ಕನೆಕ್ಷನ್ ಕಳಪೆಯಾಗಿದೆ"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ಸ್ಯಾಟಲೈಟ್‌, ಕನೆಕ್ಷನ್ ಉತ್ತಮವಾಗಿದೆ"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ಸ್ಯಾಟಲೈಟ್, ಕನೆಕ್ಷನ್ ಲಭ್ಯವಿದೆ"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"ಕೆಲಸದ ಪ್ರೊಫೈಲ್"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"ಕೆಲವರಿಗೆ ಮೋಜು ಆಗಿದೆ ಎಲ್ಲರಿಗೆ ಇಲ್ಲ"</string>
<string name="tuner_warning" msgid="1861736288458481650">"ಸಿಸ್ಟಂ UI ಟ್ಯೂನರ್ ನಿಮಗೆ Android ಬಳಕೆದಾರ ಅಂತರಸಂಪರ್ಕವನ್ನು ಸರಿಪಡಿಸಲು ಮತ್ತು ಕಸ್ಟಮೈಸ್ ಮಾಡಲು ಹೆಚ್ಚುವರಿ ಮಾರ್ಗಗಳನ್ನು ನೀಡುತ್ತದೆ. ಈ ಪ್ರಾಯೋಗಿಕ ವೈಶಿಷ್ಟ್ಯಗಳು ಭವಿಷ್ಯದ ಬಿಡುಗಡೆಗಳಲ್ಲಿ ಬದಲಾಗಬಹುದು, ವಿರಾಮವಾಗಬಹುದು ಅಥವಾ ಕಾಣಿಸಿಕೊಳ್ಳದಿರಬಹುದು. ಎಚ್ಚರಿಕೆಯಿಂದ ಮುಂದುವರಿಯಿರಿ."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"ಸೆಟ್ಟಿಂಗ್‍ಗಳನ್ನು ತೆರೆಯಿರಿ"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"assistant ಅನ್ನು ತೆರೆಯಿರಿ"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಮಾಡಿ"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"ಟಿಪ್ಪಣಿಗಳನ್ನು ತೆರೆಯಿರಿ"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"ಟಿಪ್ಪಣಿಯನ್ನು ತೆಗೆದುಕೊಳ್ಳಿ"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"ಸಿಸ್ಟಂ ಮಲ್ಟಿಟಾಸ್ಕಿಂಗ್"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"RHS ಗೆ ಇರುವ ಪ್ರಸ್ತುತ ಆ್ಯಪ್ ಸಹಾಯದಿಂದ ಸ್ಕ್ರೀನ್ ಬೇರ್ಪಡಿಸಿ ಮೋಡ್ ನಮೂದಿಸಿ"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"LHS ಗೆ ಇರುವ ಪ್ರಸ್ತುತ ಆ್ಯಪ್ ಸಹಾಯದಿಂದ ಸ್ಕ್ರೀನ್ ಬೇರ್ಪಡಿಸಿ ಮೋಡ್ ನಮೂದಿಸಿ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index fe6ce1323366..26825e0286e3 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"탭하여 보기"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"화면 녹화 저장 중에 오류가 발생했습니다."</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"화면 녹화 시작 중 오류 발생"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"문제 녹화 도구"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"문제 녹화 처리 중"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"문제 수집 섹션에 대한 진행 중 알림"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"녹화 문제"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"공유"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"문제 녹화가 저장되었습니다."</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"탭하여 보기"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"문제 녹화 저장 중에 오류가 발생했습니다."</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"문제 녹화 시작 중에 오류가 발생했습니다."</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"전체 화면 모드"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"종료하려면 위에서 아래로 스와이프합니다."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"확인"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"저장됨"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"연결 해제"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"실행"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"내일 다시 자동으로 사용 설정"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Quick Share, 내 기기 찾기, 기기 위치 등의 기능에서 블루투스를 사용합니다."</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"배터리 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"오디오"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"헤드셋"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"부모님이 관리하는 기기입니다. 부모님이 내가 사용하는 앱, 내 위치, 기기 사용 시간과 같은 정보를 보고 관리할 수 있습니다."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent가 잠금 해제함"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"도난 방지\n기기 잠김, 잠금 해제 시도 횟수가 너무 많음"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"소리 설정"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"미디어 자막 자동 생성"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"사용 중지"</string>
<string name="sound_settings" msgid="8874581353127418308">"소리 및 진동"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"설정"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"실시간 자막"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"볼륨을 안전한 수준으로 낮춤"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"헤드폰 볼륨이 권장 시간보다 오래 높은 상태였습니다."</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"헤드폰 볼륨이 이번 주 안전 한도를 초과했습니다."</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"진동"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s 볼륨 컨트롤"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"전화 및 알림이 오면 벨소리가 울림(<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> 재생 위치:"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"오디오 재생 위치:"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"시스템 UI 튜너"</string>
<string name="status_bar" msgid="4357390266055077437">"상태 표시줄"</string>
<string name="demo_mode" msgid="263484519766901593">"시스템 UI 데모 모드"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"시간: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"일시: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"핫스팟"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"위성, 연결되지 않음"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"위성, 연결 상태 나쁨"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"위성, 연결 상태 양호"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"위성, 연결 가능"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"직장 프로필"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"마음에 들지 않을 수도 있음"</string>
<string name="tuner_warning" msgid="1861736288458481650">"시스템 UI 튜너를 사용하면 Android 사용자 인터페이스를 변경 및 맞춤설정할 수 있습니다. 이러한 실험실 기능은 향후 출시 버전에서는 변경되거나 다운되거나 사라질 수 있습니다. 신중하게 진행하시기 바랍니다."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"설정 열기"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"어시스턴트 열기"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"잠금 화면"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"메모 열기"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"메모 작성"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"시스템 멀티태스킹"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"현재 앱을 오른쪽으로 보내는 화면 분할 입력"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"현재 앱을 왼쪽으로 보내는 화면 분할 입력"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index d3803ecfdb20..79546862454b 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Көрүү үчүн таптаңыз"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Экран тартылган жок"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Экранды жаздырууну баштоодо ката кетти"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Маселе жаздыргыч"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Маселе жаздырылууда"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Маселе тууралуу маалымат чогултулуп жатканы жөнүндө учурдагы билдирме"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Жаздыруу маселеси"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Бөлүшүү"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Жаздырылган маселе сакталды"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Көрүү үчүн таптаңыз"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Жаздырылган маселе сакталган жок"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Маселени жаздыруу башталбай койду"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Толук экран режимин көрүү"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Чыгуу үчүн экранды ылдый сүрүп коюңуз."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Түшүндүм"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Сакталды"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ажыратуу"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"иштетүү"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Эртең автоматтык түрдө кайра күйгүзүү"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Тез Бөлүшүү, \"Түзмөгүм кайда?\" жана түзмөктүн турган жерин аныктоо сыяктуу функциялар Bluetooth\'ду колдонот"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батареянын деңгээли <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Бул түзмөктү ата-энең башкарат. Ата-энең сен иштеткен колдонмолорду, кайда жүргөнүңдү жана түзмөктү канча убакыт колдонгонуңду көрүп, башкарып турат."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Ишеним агенти кулпусун ачты"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Түзмөктүн уурдалышынан коргоо\nТүзмөк кулпуланды. Кулпуну ачууга өтө көп аракет жасалды"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Добуштун параметрлери"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Автоматтык коштомо жазуулар"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"өчүрүү"</string>
<string name="sound_settings" msgid="8874581353127418308">"Үн жана дирилдөө"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Параметрлер"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Ыкчам коштомо жазуулар"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Үндүн катуулугу коопсуз деңгээлге чейин акырындатылды"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Гарнитуранын үнүн катуу чыгарып, сунушталгандан узагыраак угуп жатасыз"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Гарнитуранын үнүнүн катуулугу бул аптада коопсуз деңгээлден жогору болду"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"дирилдөө"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s үндү башкаруу элементтери"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Чалуулар менен эскертмелердин үнү чыгарылат (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> аркылуу ойнотулууда"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Аудио төмөнкүдө ойнотулат:"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Абал тилкеси"</string>
<string name="demo_mode" msgid="263484519766901593">"Системанын интерфейсинин демо режими"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> болгондо"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> болгондо"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Байланыш түйүнү"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Спутник, байланыш жок"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Спутник, байланыш начар"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Спутник, байланыш жакшы"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Спутник, байланыш бар"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Жумуш профили"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Баарына эле жага бербейт"</string>
<string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner Android колдонуучу интерфейсин жөнгө салып жана ыңгайлаштыруунун кошумча ыкмаларын сунуштайт. Бул сынамык функциялар кийинки чыгарылыштарда өзгөрүлүп, бузулуп же жоголуп кетиши мүмкүн. Абайлап колдонуңуз."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Параметрлерди ачуу"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Жардамчыны ачуу"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Экранды кулпулоо"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Кыска жазууларды ачуу"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Кыска жазуу түзүү"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Системанын бир нече тапшырма аткаруусу"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Учурда оң жактагы колдонмо менен экранды бөлүүнү иштетүү"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Учурда сол жактагы колдонмо менен экранды бөлүүнү иштетүү"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 35094938a297..4c1bd4eb0d14 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"ແຕະເພື່ອເບິ່ງ"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"ເກີດຂໍ້ຜິດພາດໃນການບັນທຶກໜ້າຈໍ"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"ເກີດຄວາມຜິດພາດໃນການບັນທຶກໜ້າຈໍ"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"ຕົວບັນທຶກບັນຫາ"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"ກຳລັງປະມວນຜົນການບັນທຶກບັນຫາ"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"ການແຈ້ງເຕືອນທີ່ດຳເນີນຢູ່ສຳລັບເຊດຊັນການຮວບຮວມບັນຫາ"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"ບັນຫາການບັນທຶກ"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"ແບ່ງປັນ"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"ການບັນທຶກບັນຫາຖືກບັນທຶກໄວ້ແລ້ວ"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"ແຕະເພື່ອເບິ່ງ"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"ເກີດຂໍ້ຜິດພາດໃນການບັນທຶກບັນຫາ"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"ເກີດຂໍ້ຜິດພາດໃນການເລີ່ມຕົ້ນການບັນທຶກບັນຫາ"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"ກຳລັງ​ເບິ່ງ​ເຕັມ​​ຈໍ"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"ເພື່ອອອກ, ໃຫ້ປັດລົງຈາກເທິງສຸດ."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"ເຂົ້າໃຈແລ້ວ"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ບັນທຶກແລ້ວ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ຕັດການເຊື່ອມຕໍ່"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ເປີດນຳໃຊ້"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ເປີດໃຊ້ໂດຍອັດຕະໂນມັດອີກຄັ້ງມື້ອື່ນ"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"ຄຸນສົມບັດຕ່າງໆເຊັ່ນ: ການແຊຣ໌ດ່ວນ, ຊອກຫາອຸປະກອນຂອງຂ້ອຍ ແລະ ສະຖານທີ່ອຸປະກອນໃຊ້ Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"ແບັດເຕີຣີ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ສຽງ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ຊຸດຫູຟັງ"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"ອຸປະກອນນີ້ແມ່ນຈັດການໂດຍພໍ່ແມ່ຂອງທ່ານ. ພໍ່ແມ່ຂອງທ່ານສາມາດເບິ່ງ ແລະ ຈັດການຂໍ້ມູນໄດ້ ເຊັ່ນ: ແອັບທີ່ທ່ານໃຊ້, ສະຖານທີ່ ແລະ ເວລາໜ້າຈໍຂອງທ່ານ."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"ປັອດລັອກປະໄວ້ໂດຍ TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"ການປ້ອງກັນການຖືກລັກ\nຂອງອຸປະກອນຖືກລັອກ, ພະຍາຍາມປົດລັອກຫຼາຍເທື່ອເກີນໄປ"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"ການຕັ້ງຄ່າສຽງ"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"ສ້າງຄຳບັນຍາຍມີເດຍໂດຍອັດຕະໂນມັດ"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"ປິດນຳໃຊ້"</string>
<string name="sound_settings" msgid="8874581353127418308">"ສຽງ ແລະ ການສັ່ນເຕືອນ"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"ການຕັ້ງຄ່າ"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"ຄຳບັນຍາຍສົດ"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"ຫຼຸດລະດັບສຽງໃຫ້ຢູ່ໃນລະດັບທີ່ປອດໄພຂຶ້ນແລ້ວ"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"ຫູຟັງຢູ່ໃນລະດັບສຽງທີ່ດັງເປັນໄລຍະເວລາດົນກວ່າທີ່ແນະນຳ"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"ລະດັບສຽງຂອງຫູຟັງໄດ້ເກີນຂີດຈຳກັດທີ່ປອດໄພສຳລັບອາທິດນີ້"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ສັ່ນເຕືອນ"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"ການຄວບຄຸມສຽງ %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"ການໂທ ແລະ ການແຈ້ງເຕືອນຈະມີສຽງດັງ (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"ກຳລັງຫຼິ້ນ <xliff:g id="LABEL">%s</xliff:g> ໃນ"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"ສຽງຈະຫຼິ້ນຕໍ່ໄປ"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"ແຖບສະຖານະ"</string>
<string name="demo_mode" msgid="263484519766901593">"ໂໝດເດໂມສ່ວນຕິດຕໍ່ຜູ້ໃຊ້ລະບົບ"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"ເວ​ລາ <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"ວັນ <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"​ຮັອດ​ສະ​ປອດ"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"ດາວທຽມ, ບໍ່ມີການເຊື່ອມຕໍ່"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"ດາວທຽມ, ການເຊື່ອມຕໍ່ບໍ່ດີ"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ດາວທຽມ, ການເຊື່ອມຕໍ່ດີ"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ດາວທຽມ, ການເຊື່ອມຕໍ່ທີ່ພ້ອມນຳໃຊ້"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"​ໂປຣ​ໄຟລ໌​ບ່ອນ​ເຮັດ​ວຽກ"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"ມ່ວນຊື່ນສຳລັບບາງຄົນ ແຕ່ບໍ່ແມ່ນສຳລັບທຸກຄົນ"</string>
<string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner ໃຫ້ທ່ານມີວິທີພິເສດຕື່ມອີກໃນການປັບປ່ຽນ ແລະຕົບແຕ່ງສ່ວນຕໍ່ປະສານຜູ້ໃຊ້ຂອງ Android. ຄຸນສົມບັດທົດລອງໃຊ້ເຫຼົ່ານີ້ອາດຈະປ່ຽນແປງ, ຢຸດເຊົາ ຫຼືຫາຍໄປໃນການວາງຈຳໜ່າຍໃນອະນາຄົດ. ຈົ່ງດຳເນີນຕໍ່ດ້ວຍຄວາມລະມັດລະວັງ."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"ເປີດການຕັ້ງຄ່າ"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"ເປີດຜູ້ຊ່ວຍ"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"ໜ້າຈໍລັອກ"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"ເປີດບັນທຶກ"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"ຈົດບັນທຶກ"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"ການເຮັດຫຼາຍໜ້າວຽກພ້ອມກັນຂອງລະບົບ"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"ເຂົ້າສູ່ແບ່ງໜ້າຈໍດ້ວຍແອັບປັດຈຸບັນໄປຫາ RHS"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"ເຂົ້າສູ່ແບ່ງໜ້າຈໍດ້ວຍແອັບປັດຈຸບັນໄປຫາ LHS"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index c773f257945a..9a261e05f709 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Palieskite, kad peržiūrėtumėte"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Išsaugant ekrano įrašą įvyko klaida"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Pradedant ekrano vaizdo įrašymą iškilo problema"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Problemų įrašytuvas"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Apdorojamas problemos įrašas"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Šiuo metu rodomas problemos duomenų rinkimo seanso pranešimas"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Įrašoma problema"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Bendrinti"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Problemos įrašas išsaugotas"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Palieskite, kad peržiūrėtumėte"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Išsaugant problemos įrašą įvyko klaida"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Pradedant problemos įrašą įvyko klaida"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Peržiūrima viso ekrano režimu"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Jei norite išeiti, perbraukite žemyn iš viršaus."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Supratau"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Išsaugota"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"atjungti"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"suaktyvinti"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatiškai vėl įjungti rytoj"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Tokioms funkcijoms kaip „Spartusis bendrinimas“, „Rasti įrenginį“ ir įrenginio vietovė naudojamas „Bluetooth“ ryšys"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akumuliatorius: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Garsas"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Virtualiosios realybės įrenginys"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Šį įrenginį tvarko vienas iš tavo tėvų. Jis gali peržiūrėti ir tvarkyti informaciją, pvz., tavo naudojamas programas, vietovę ir įrenginio naudojimo laiką."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Atrakinta taikant „TrustAgent“"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Apsauga nuo vagystės\nĮrenginys užrakintas, per daug bandymų atrakinti"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Garso nustatymai"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Taikyti aut. medij. subtitr."</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"išjungti"</string>
<string name="sound_settings" msgid="8874581353127418308">"Garsas ir vibravimas"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Nustatymai"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Subtitrai realiuoju laiku"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Garsumas sumažintas iki saugesnio lygio"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Ausinių garsumo lygis buvo aukštas ilgiau, nei rekomenduojama"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Ausinių garsumo lygis viršijo šios savaitės saugaus garsumo lygio apribojimą"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibruoti"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Garsumo valdikliai: %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Skambučiai ir pranešimai skambės (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Leidžiama „<xliff:g id="LABEL">%s</xliff:g>“"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Garsas bus leidžiamas"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Sistemos naudotojo sąsajos derinimo priemonė"</string>
<string name="status_bar" msgid="4357390266055077437">"Būsenos juosta"</string>
<string name="demo_mode" msgid="263484519766901593">"Sistemos NS demonstracinis režimas"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Viešosios interneto prieigos taškas"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Palydovas, nėra ryšio"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Palydovas, prastas ryšys"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Palydovas, geras ryšys"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Palydovas, pasiekiamas ryšys"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Darbo profilis"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Smagu, bet ne visada"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Sistemos naudotojo sąsajos derinimo priemonė suteikia papildomų galimybių pagerinti ir tinkinti „Android“ naudotojo sąsają. Šios eksperimentinės funkcijos gali pasikeisti, nutrūkti ar išnykti iš būsimų laidų. Tęskite atsargiai."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Atidaryti nustatymus"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Atidaryti Padėjėją"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Užrakinti ekraną"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Atidaryti pastabas"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Sukurti pastabą"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Kelių užduočių atlikimas sistemoje"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Eiti į išskaidyto ekrano režimą su dabartine programa dešinėje"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Eiti į išskaidyto ekrano režimą su dabartine programa kairėje"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index a26c7444b71a..a9a1a3c366a4 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Pieskarieties, lai skatītu"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Saglabājot ekrāna ierakstu, radās kļūda."</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Sākot ierakstīt ekrāna saturu, radās kļūda."</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Problēmu ierakstītājs"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Apstrādā problēmas ierakstu"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Aktīvs paziņojums par problēmu vākšanas sesiju"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Notiek problēmas reģistrēšana"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Kopīgot"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Problēmas ieraksts ir saglabāts."</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Pieskarieties, lai skatītu"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Saglabājot problēmas ierakstu, radās kļūda."</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Sākot problēmas ierakstīšanu, radās kļūda."</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Skatīšanās pilnekrāna režīmā"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Lai izietu, no augšdaļas velciet lejup."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Labi"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saglabāta"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"atvienot"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivizēt"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automātiski atkal ieslēgt rīt"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Tādas funkcijas kā “Ātrā kopīgošana”, “Atrast ierīci” un ierīces atrašanās vietas noteikšana izmanto tehnoloģiju Bluetooth."</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akumulators: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Austiņas"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Šo ierīci pārvalda viens no jūsu vecākiem. Vecāki var skatīt un pārvaldīt tādu informāciju kā jūsu izmantotās lietotnes, atrašanās vieta un izmantošanas ilgums."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Bloķēšanu liedzis TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Aizsardzība pret zādzību\nIerīce bloķēta; pārāk daudz atbloķēšanas mēģinājumu"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Skaņas iestatījumi"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Autom. paraksti multividei"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"atspējot"</string>
<string name="sound_settings" msgid="8874581353127418308">"Skaņa un vibrācija"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Iestatījumi"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Subtitri reāllaikā"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Skaļums samazināts līdz drošākam līmenim"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Austiņu skaļums ir bijis liels ilgāk, nekā ieteicams."</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Austiņu skaļums ir pārsniedzis šīs nedēļas drošo ierobežojumu."</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrēt"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s skaļuma vadīklas"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Zvani un paziņojumi aktivizēs zvana signālu (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> — atskaņošana šeit:"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio tiks atskaņots šeit:"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Sistēmas saskarnes regulators"</string>
<string name="status_bar" msgid="4357390266055077437">"Statusa josla"</string>
<string name="demo_mode" msgid="263484519766901593">"Sistēmas lietotāja saskarnes demonstrācijas režīms"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"plkst. <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Tīklājs"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Satelīts, nav savienojuma"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Satelīts, vājš savienojums"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelīts, labs savienojums"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelīts, ir pieejams savienojums"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Darba profils"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Jautri dažiem, bet ne visiem"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Sistēmas saskarnes regulators sniedz papildu veidus, kā mainīt un pielāgot Android lietotāja saskarni. Nākamajās versijās šīs eksperimentālās funkcijas var tikt mainītas, bojātas vai to darbība var tikt pārtraukta. Turpinot esiet uzmanīgs."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Atvērt iestatījumus"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Atvērt Asistentu"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Bloķēt ekrānu"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Atvērt piezīmes"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Piezīmes izveide"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Sistēmas vairākuzdevumu režīms"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Pāriet ekrāna sadalīšanas režīmā ar pašreizējo lietotni pa labi"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Pāriet ekrāna sadalīšanas režīmā ar pašreizējo lietotni pa kreisi"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 4add5808a81a..20a93f7df6cf 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Допрете за прегледување"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Грешка при зачувувањето на снимката од екранот"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Грешка при почетокот на снимањето на екранот"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Проблем со „Диктафон“"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Се обработува проб. со снимање"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Тековно известување за сесија за проблем со прибирање"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Проблем со снимање"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Споделување"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Проблемот со снимање е зачуван"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Допрете за да прегледате"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Грешка при зачувување на проблемот со снимање"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Грешка при започнување на проблемот со снимање"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Се прикажува на цел екран"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"За да излезете, повлечете одозгора надолу."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Сфатив"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Зачувано"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"прекини врска"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"активирај"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Автоматски вклучи повторно утре"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Функциите како „Брзо споделување“, „Најди го мојот уред“ и локација на уредот користат Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> батерија"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалки"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Родителот управува со уредов. Родителот може да прегледува и управува со податоците, како што се апликациите што ги користиш, твојата локација и времето поминато на уредот."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"ВПН"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Се одржува отклучен од TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Заштита од кражби\nЗаклучено. Премногу обиди за отклучување."</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Поставки за звукот"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Автоматски титлови"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"оневозможи"</string>
<string name="sound_settings" msgid="8874581353127418308">"Звук и вибрации"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Поставки"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Автоматски титлови"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Јачината на звукот е намалена на побезбедно ниво"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Јачината на звукот на слушалките беше висока подолго од препорачаното"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Јачината на звукот на слушалките го надмина безбедното ограничување за седмицава"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"вибрации"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Контроли на јачината на звукот за %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Повиците и известувањата ќе ѕвонат (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g>: пуштено на"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Аудиото ќе се пушти на"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Адаптер на УИ на системот"</string>
<string name="status_bar" msgid="4357390266055077437">"Статусна лента"</string>
<string name="demo_mode" msgid="263484519766901593">"Демо-режим на кориснички интерфејс на систем"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"во <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"во <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Точка на пристап"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Нема сателитска врска"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Слаба сателитска врска"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Добра сателитска врска"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Достапна е сателитска врска"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Работен профил"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Забава за некои, но не за сите"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Адаптерот на УИ на системот ви дава дополнителни начини за дотерување и приспособување на корисничкиот интерфејс на Android. Овие експериментални функции можеби ќе се изменат, расипат или ќе исчезнат во следните изданија. Продолжете со претпазливост."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Отворете „Поставки“"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Отворете го „Помошникот“"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Заклучен екран"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Отворете „Белешки“"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Фатете белешка"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Системски мултитаскинг"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Активирајте поделен екран со тековната апликација десно"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Активирајте поделен екран со тековната апликација лево"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 3208b20c7e98..fc2bc47b64dc 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"കാണാൻ ടാപ്പ് ചെയ്യുക"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"സ്ക്രീൻ റെക്കോർഡിംഗ് സംരക്ഷിക്കുന്നതിൽ പിശക്"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"സ്ക്രീൻ റെക്കോർഡിംഗ് ആരംഭിക്കുന്നതിൽ പിശക്"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"പ്രശ്‌ന റെക്കോർഡർ"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"പ്രശ്‌ന റെക്കോർഡിംഗ് പ്രോസസ് ചെയ്യുന്നു"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"ഒരു പ്രശ്‌ന ശേഖരണ സെഷന്റെ ഓൺ‌ഗോയിംഗ് അറിയിപ്പ്"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"റെക്കോർഡിംഗിൽ പ്രശ്നം"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"പങ്കിടുക"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"പ്രശ്‌ന റെക്കോർഡിംഗ് സംരക്ഷിച്ചു"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"കാണാൻ ടാപ്പ് ചെയ്യുക"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"പ്രശ്‌ന റെക്കോർഡിംഗ് സംരക്ഷിക്കുന്നതിൽ പിശക്"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"പ്രശ്‌ന റെക്കോർഡിംഗ് ആരംഭിക്കുന്നതിൽ പിശക്"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"പൂർണ്ണ സ്‌ക്രീനിൽ കാണുന്നു"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"പുറത്തുകടക്കാൻ, മുകളിൽ നിന്ന് താഴോട്ട് സ്വൈപ്പ് ചെയ്യുക."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"മനസ്സിലായി"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"സംരക്ഷിച്ചു"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"വിച്ഛേദിക്കുക"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"സജീവമാക്കുക"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"നാളെ വീണ്ടും സ്വയമേവ ഓണാക്കുക"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"ക്വിക്ക് ഷെയർ, Find My Device, ഉപകരണ ലൊക്കേഷൻ എന്നിവ പോലുള്ള ഫീച്ചറുകൾ Bluetooth ഉപയോഗിക്കുന്നു"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ബാറ്ററി"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ഓഡിയോ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ഹെഡ്‌സെറ്റ്"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"ഈ ഉപകരണം മാനേജ് ചെയ്യുന്നത് നിങ്ങളുടെ രക്ഷിതാവാണ്. നിങ്ങൾ ഉപയോഗിക്കുന്ന ആപ്പുകൾ, സ്‌ക്രീൻ സമയം, ലൊക്കേഷൻ എന്നിവ പോലുള്ള വിവരങ്ങൾ നിങ്ങളുടെ രക്ഷിതാവിന് കാണാനും നിയന്ത്രിക്കാനുമാകും."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent ഉപയോഗിച്ച് അൺലോക്ക് ചെയ്‌തത്"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"മോഷണ പരിരക്ഷ\nഉപകരണം ലോക്ക് ചെയ്തു, നിരവധി അൺലോക്ക് ശ്രമങ്ങൾ"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"ശബ്‌ദ ക്രമീകരണം"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"മീഡിയയ്ക്ക് സ്വയമേവ ക്യാപ്ഷൻ"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"പ്രവർത്തനരഹിതമാക്കുക"</string>
<string name="sound_settings" msgid="8874581353127418308">"ശബ്‌ദവും വൈബ്രേഷനും"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"ക്രമീകരണം"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"തത്സമയ ക്യാപ്ഷൻ"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"സുരക്ഷിതമായ തലത്തിലേക്ക് വോളിയം കുറച്ചു"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"നിർദ്ദേശിച്ചിരിക്കുന്നതിനേക്കാൾ കൂടുതൽ സമയം ഹെഡ്‌ഫോണിന്റെ വോളിയം ഉയർന്ന നിലയിലായിരുന്നു"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"ഹെഡ്‌ഫോണിന്റെ വോളിയം ഈ ആഴ്‌ചത്തെ സുരക്ഷിത പരിധി കവിഞ്ഞു"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"വൈബ്രേറ്റ് ചെയ്യുക"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s ശബ്‌ദ നിയന്ത്രണങ്ങൾ"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"കോളുകളും അറിയിപ്പുകളും ലഭിക്കുമ്പോൾ റിംഗ് ചെയ്യും (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> എന്നതിൽ പ്ലേ ചെയ്യുന്നു"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"ഓഡിയോ പ്ലേ ചെയ്യും"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"സിസ്റ്റം UI ട്യൂണർ"</string>
<string name="status_bar" msgid="4357390266055077437">"സ്റ്റാറ്റസ് ബാർ"</string>
<string name="demo_mode" msgid="263484519766901593">"സിസ്റ്റം UI ഡെമോ മോഡ്"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>-ന്"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>-ന്"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"ഹോട്ട്‌സ്‌പോട്ട്"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"സാറ്റലൈറ്റ്, കണക്ഷൻ ഇല്ല"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"സാറ്റലൈറ്റ്, മോശം കണക്ഷൻ"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"സാറ്റലൈറ്റ്, മികച്ച കണക്ഷൻ"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"സാറ്റലൈറ്റ്, കണക്ഷൻ ലഭ്യമാണ്"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"ഔദ്യോഗിക പ്രൊഫൈൽ"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"ചിലർക്ക് വിനോദം, എന്നാൽ എല്ലാവർക്കുമില്ല"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Android ഉപയോക്തൃ ഇന്റർഫേസ് ആവശ്യമുള്ള രീതിയിൽ മാറ്റുന്നതിനും ഇഷ്ടാനുസൃതമാക്കുന്നതിനും സിസ്റ്റം UI ട്യൂണർ നിങ്ങൾക്ക് അധിക വഴികൾ നൽകുന്നു. ഭാവി റിലീസുകളിൽ ഈ പരീക്ഷണാത്മക ഫീച്ചറുകൾ മാറ്റുകയോ നിർത്തുകയോ അപ്രത്യക്ഷമാവുകയോ ചെയ്തേക്കാം. ശ്രദ്ധയോടെ മുന്നോട്ടുപോകുക."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"ക്രമീകരണം തുറക്കുക"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Assistant തുറക്കുക"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"ലോക്ക് സ്‌ക്രീൻ"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"കുറിപ്പുകൾ തുറക്കുക"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"ഒരു കുറിപ്പെടുക്കുക"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"സിസ്റ്റം മൾട്ടിടാസ്‌കിംഗ്"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"നിലവിലെ ആപ്പ് വലതുവശത്ത് വരുന്ന രീതിയിൽ സ്ക്രീൻ വിഭജന മോഡിൽ കടക്കുക"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"നിലവിലെ ആപ്പ് ഇടതുവശത്ത് വരുന്ന രീതിയിൽ സ്ക്രീൻ വിഭജന മോഡിൽ കടക്കുക"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 93c22c4dfcc2..f5bf05a4d671 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Харахын тулд товшино уу"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Дэлгэцийн бичлэгийг хадгалахад алдаа гарлаа"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Дэлгэцийн бичлэгийг эхлүүлэхэд алдаа гарлаа"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Асуудал бичигч"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Асуудлын бичлэгийг боловсруулж байна"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Асуудал цуглуулах харилцан үйлдлийн үргэлжилж буй мэдэгдэл"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Асуудлыг бичиж байна"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Хуваалцах"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Асуудлын бичлэгийг хадгалсан"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Товшиж харна уу"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Асуудлын бичлэгийг хадгалахад алдаа гарлаа"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Асуудлын бичлэгийг эхлүүлэхэд алдаа гарлаа"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Бүтэн дэлгэцээр үзэж байна"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Гарах бол дээрээс доош шударна уу."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Ойлголоо"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Хадгалсан"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"салгах"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"идэвхжүүлэх"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Маргааш автоматаар дахин асаах"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Түргэн хуваалцах, Миний төхөөрөмжийг олох зэрэг онцлогууд болон төхөөрөмжийн байршил Bluetooth-г ашигладаг"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> батарей"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Чихэвч"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Энэ төхөөрөмжийг таны эцэг эх удирддаг. Таны эцэг эх таны хэрэглэдэг апп, байршил, дэлгэцийн цаг зэрэг мэдээллийг харж, удирдах боломжтой."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent-р түгжээгүй байлгасан"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Хулгайн хамгаалалт\nТөхөөрөмж түгжигдсэн, түгжээг тайлах хэт олон оролдлого"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Дууны тохиргоо"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Медиад автоматаар тайлбар нэмэх"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"идэвхгүй болгох"</string>
<string name="sound_settings" msgid="8874581353127418308">"Дуу, чичиргээ"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Тохиргоо"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Шууд тайлбар"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Дууны түвшнийг илүү аюулгүй түвшин рүү багасгасан"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Чихэвчийн дууны түвшин санал болгосноос удаан хугацааны туршид өндөр байсан"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Чихэвчийн дууны түвшин энэ долоо хоногийн аюулгүй хязгаараас хэтэрсэн"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"чичрэх"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s түвшний хяналт"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Дуудлага болон мэдэгдлийн хонх дуугарна (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> дээр тоглуулж байна"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Аудиог дараахад тоглуулна"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Системийн UI Тохируулагч"</string>
<string name="status_bar" msgid="4357390266055077437">"Статус самбар"</string>
<string name="demo_mode" msgid="263484519766901593">"Системийн UI демо горим"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> цагт"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>-т"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Сүлжээний цэг"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Хиймэл дагуул, холболт байхгүй"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Хиймэл дагуул, холболт муу байна"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Хиймэл дагуул, холболт сайн байна"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Хиймэл дагуул, холболт боломжтой"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Ажлын профайл"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Зарим хүнд хөгжилтэй байж болох ч бүх хүнд тийм биш"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Системийн UI Tохируулагч нь Android хэрэглэгчийн интерфэйсийг тааруулах, өөрчлөх нэмэлт аргыг зааж өгөх болно. Эдгээр туршилтын тохиргоо нь цаашид өөрчлөгдөх, эвдрэх, алга болох магадлалтай. Үйлдлийг болгоомжтой хийнэ үү."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Тохиргоог нээх"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Туслахыг нээх"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Түгжээтэй дэлгэц"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Тэмдэглэлүүдийг нээх"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Тэмдэглэл хөтлөх"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Систем олон ажил зэрэг хийх"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Одоогийн аппаар баруун гар талд дэлгэц хуваахад орох"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Одоогийн аппаар зүүн гар талд дэлгэц хуваахад орох"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 40f88178fd8e..f9d4aca9efcd 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"पाहण्यासाठी टॅप करा"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"स्क्रीन रेकॉर्डिंग सेव्ह करताना एरर आली"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"स्क्रीन रेकॉर्डिंग सुरू करताना एरर आली"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"समस्या रेकॉर्डर"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"प्रक्रियेच्या समस्येचे रेकॉर्डिंग"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"समस्या गोळा करण्याच्या सेशनसाठीची सद्य सूचना"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"समस्या रेकॉर्ड करत आहे"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"शेअर करा"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"समस्येचे रेकॉर्डिंग सेव्ह केले"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"पाहण्यासाठी टॅप करा"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"समस्येचे रेकॉर्डिंग सेव्ह करताना एरर आली"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"समस्येचे रेकॉर्डिंग सुरू करताना एरर आली"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"पूर्ण स्क्रीन पाहत आहात"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"बाहेर पडण्यासाठी, वरून खाली स्वाइप करा."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"समजले"</string>
@@ -157,7 +148,7 @@
<string name="accessibility_scanning_face" msgid="3093828357921541387">"चेहरा स्कॅन करत आहे"</string>
<string name="accessibility_send_smart_reply" msgid="8885032190442015141">"पाठवा"</string>
<string name="cancel" msgid="1089011503403416730">"रद्द करा"</string>
- <string name="biometric_dialog_confirm" msgid="2005978443007344895">"कंफर्म करा"</string>
+ <string name="biometric_dialog_confirm" msgid="2005978443007344895">"कन्फर्म करा"</string>
<string name="biometric_dialog_try_again" msgid="8575345628117768844">"पुन्हा प्रयत्न करा"</string>
<string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"ऑथेंटिकेशन रद्द करण्यासाठी टॅप करा"</string>
<string name="biometric_dialog_face_icon_description_idle" msgid="4351777022315116816">"कृपया पुन्हा प्रयत्न करा"</string>
@@ -214,7 +205,7 @@
<string name="keyguard_face_unlock_unavailable" msgid="1581949044193418736">"फेस अनलॉक उपलब्ध नाही"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"ब्लूटूथ कनेक्‍ट केले."</string>
<string name="accessibility_bluetooth_device_icon" msgid="9163840051642587982">"ब्लूटूथ डिव्‍हाइस आयकन"</string>
- <string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"डिव्हाइसचे तपशील कॉंफिगर करण्यासाठी क्लिक करा"</string>
+ <string name="accessibility_bluetooth_device_settings_gear" msgid="3314916468105272540">"डिव्हाइसचे तपशील कॉन्फिगर करण्यासाठी क्लिक करा"</string>
<string name="accessibility_bluetooth_device_settings_see_all" msgid="9111952496905423543">"सर्व डिव्हाइस पाहण्यासाठी क्लिक करा"</string>
<string name="accessibility_bluetooth_device_settings_pair_new_device" msgid="2435184865793496966">"नवीन डिव्हाइस पेअर करण्यासाठी क्लिक करा"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"बॅटरीच्या चार्जिंगची टक्केवारी माहित नाही."</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"सेव्ह केले"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"डिस्कनेक्ट करा"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ॲक्टिव्हेट करा"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"उद्या पुन्हा आपोआप सुरू करा"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Quick Share, Find My Device आणि डिव्हाइस स्थान यांसारखी वैशिष्ट्ये ब्लूटूथ वापरतात"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> बॅटरी"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ऑडिओ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"हे डिव्हाइस तुमच्या पालकाने व्यवस्थापित केले आहे. तुम्ही वापरत असलेली ॲप्स, तुमचे स्थान आणि तुमचा स्क्रीन वेळ यांसारखी माहिती तुमचे पालक पाहू आणि व्यवस्‍थापित करू शकतात."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent ने अनलॉक ठेवले"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"चोरीपासून संरक्षण\nडिव्हाइस लॉक केले, अनलॉक करायचे खूप प्रयत्न"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"आवाज सेटिंग्ज"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"मीडियाला आपोआप सबटायटल द्या"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"बंद करा"</string>
<string name="sound_settings" msgid="8874581353127418308">"आवाज आणि व्हायब्रेशन"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"सेटिंग्ज"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"लाइव्ह कॅप्शन"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"व्हॉल्यूम सुरक्षित पातळीपर्यंत कमी केला"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"व्हॉल्यूम हा शिफारस केलेल्या वेळेपेक्षा जास्त वेळ उच्च आहे"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"हेडफोनच्या व्हॉल्यूमने या आठवड्यासाठीची सुरक्षिततेची मर्यादा ओलांडली आहे"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"व्हायब्रेट करा"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s व्हॉल्यूम नियंत्रण"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"कॉल आणि सूचना वाजतील (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> वर प्ले करत आहे"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"यावर ऑडिओ प्ले होईल"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"सिस्टम UI ट्युनर"</string>
<string name="status_bar" msgid="4357390266055077437">"स्टेटस बार"</string>
<string name="demo_mode" msgid="263484519766901593">"सिस्टम UI डेमो मोड"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> वाजता"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> रोजी"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"हॉटस्पॉट"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"सॅटेलाइट, कनेक्शन नाही"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"सॅटेलाइट, खराब कनेक्शन"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"सॅटेलाइट, चांगले कनेक्शन"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"सॅटेलाइट, कनेक्शन उपलब्ध"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"कार्य प्रोफाईल"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"सर्वांसाठी नाही तर काहींसाठी मजेदार असू शकते"</string>
<string name="tuner_warning" msgid="1861736288458481650">"सिस्टम UI ट्युनर आपल्‍याला Android यूझर इंटरफेस ट्विक आणि कस्टमाइझ करण्‍याचे अनेक प्रकार देते. ही प्रयोगात्मक वैशिष्‍ट्ये बदलू शकतात, खंडित होऊ शकतात किंवा भविष्‍यातील रिलीज मध्‍ये कदाचित दिसणार नाहीत. सावधगिरी बाळगून पुढे सुरू ठेवा."</string>
@@ -655,7 +648,7 @@
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> हे संभाषण वैशिष्ट्यांना सपोर्ट करत नाही"</string>
<string name="notification_unblockable_desc" msgid="2073030886006190804">"या सूचनांमध्ये सुधारणा केली जाऊ शकत नाही."</string>
<string name="notification_unblockable_call_desc" msgid="5907328164696532169">"कॉलशी संबंधित सूचनांमध्ये फेरबदल केला जाऊ शकत नाही."</string>
- <string name="notification_multichannel_desc" msgid="7414593090056236179">"या सूचनांचा संच येथे कॉंफिगर केला जाऊ शकत नाही"</string>
+ <string name="notification_multichannel_desc" msgid="7414593090056236179">"या सूचनांचा संच येथे कॉन्फिगर केला जाऊ शकत नाही"</string>
<string name="notification_delegate_header" msgid="1264510071031479920">"प्रॉक्सी केलेल्या सूचना"</string>
<string name="notification_channel_dialog_title" msgid="6856514143093200019">"सर्व <xliff:g id="APP_NAME">%1$s</xliff:g> वरील सूचना"</string>
<string name="see_more_title" msgid="7409317011708185729">"आणखी पहा"</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"सेटिंग्ज उघडा"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Assistant उघडा"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"लॉक स्क्रीन"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"टिपा उघडा"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"नोंद घ्या"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"सिस्टीम मल्टिटास्किंग"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"उजव्या बाजूला सध्याचे अ‍ॅप असलेल्या स्प्लिट स्क्रीनवर जा"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"डाव्या बाजूला सध्याचे अ‍ॅप असलेल्या स्प्लिट स्क्रीनवर जा"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 72e44ff4afd8..dafcfe009769 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Ketik untuk lihat"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Ralat semasa menyimpan rakaman skrin"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Ralat semasa memulakan rakaman skrin"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Perakam Masalah"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Memproses rakaman masalah"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Pemberitahuan sedang berlangsung untuk sesi pengumpulan masalah"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Merakamkan masalah"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Kongsi"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Rakaman masalah disimpan"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Ketik untuk lihat"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Ralat menyimpan rakaman masalah"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Ralat memulakan rakaman masalah"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Melihat skrin penuh"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Untuk keluar, leret ke bawah dari bahagian atas."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"OK"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Disimpan"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"putuskan sambungan"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktifkan"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Dihidupkan sekali lagi esok secara automatik"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Ciri seperti Quick Share, Find My Device dan lokasi peranti menggunakan Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> bateri"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Set Kepala"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Peranti ini diurus oleh ibu bapa anda. Ibu bapa anda dapat melihat dan mengurus maklumat seperti apl yang anda gunakan, lokasi dan masa skrin anda."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Dibiarkan tidak berkunci oleh TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Perlindungan kecurian\nDikunci, banyak percubaan membuka kunci"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Tetapan bunyi"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Sari kata media automatik"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"lumpuhkan"</string>
<string name="sound_settings" msgid="8874581353127418308">"Bunyi &amp; getaran"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Tetapan"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Sari Kata Langsung"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Kelantangan dikurangkan kepada tahap yang lebih selamat"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Kelantangan fon kepala tinggi melebihi tempoh yang disyorkan"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Kelantangan fon kepala anda telah melebihi had selamat untuk minggu ini"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"getar"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s kawalan kelantangan"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Panggilan dan pemberitahuan akan berdering (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Memainkan <xliff:g id="LABEL">%s</xliff:g> pada"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio dimainkan pada"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Penala UI Sistem"</string>
<string name="status_bar" msgid="4357390266055077437">"Bar status"</string>
<string name="demo_mode" msgid="263484519766901593">"Mod tunjuk cara UI sistem"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"pada <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"pada <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Tempat liputan"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Satelit, tiada sambungan"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Satelit, sambungan yang lemah"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, sambungan yang baik"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, sambungan tersedia"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Profil kerja"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Menarik untuk sesetengah orang tetapi bukan untuk semua"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Penala UI Sistem memberi anda cara tambahan untuk mengolah dan menyesuaikan antara muka Android. Ciri eksperimen ini boleh berubah, rosak atau hilang dalam keluaran masa hadapan. Teruskan dengan berhati-hati."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Buka tetapan"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Buka Assistant"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Kunci skrin"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Buka nota"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Catat nota"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Berbilang tugas sistem"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Masuk skrin pisah dengan apl semasa pada sisi kanan"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Masuk skrin pisah dengan apl semasa pada sisi kiri"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index f475621ab80e..e52b899870d2 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"ကြည့်ရှုရန် တို့ပါ"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"ဖန်သားပြင်ရိုက်ကူးမှုကို သိမ်းရာတွင် အမှားရှိသည်"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"ဖန်သားပြင် ရိုက်ကူးမှု စတင်ရာတွင် အမှားအယွင်းရှိနေသည်"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"ပြဿနာရိုက်ကူးစနစ်"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"ပြဿနာရိုက်ကူးမှု လုပ်နေသည်"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"ပြဿနာစုစည်းခြင်း စက်ရှင်အတွက် လုပ်ဆောင်နေဆဲ အကြောင်းကြားချက်"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"ပြဿနာကို ရိုက်ကူးနေသည်"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"မျှဝေရန်"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"ပြဿနာရိုက်ကူးမှုကို သိမ်းလိုက်ပါပြီ"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"ကြည့်ရန်တို့ပါ"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"ပြဿနာရိုက်ကူးမှုကို သိမ်း၍မရပါ"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"ပြဿနာရိုက်ကူးမှုကို စတင်၍မရပါ"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"ဖန်သားပြင်အပြည့် ကြည့်နေသည်"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"ထွက်ရန် အပေါ်မှ အောက်သို့ ပွတ်ဆွဲပါ။"</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"နားလည်ပြီ"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"သိမ်းထားသည်"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ချိတ်ဆက်မှုဖြုတ်ရန်"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"စသုံးရန်"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"မနက်ဖြန် အလိုအလျောက် ထပ်ဖွင့်ရန်"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"‘အမြန် မျှဝေပါ’၊ Find My Device နှင့် စက်ပစ္စည်းတည်နေရာကဲ့သို့ တူးလ်များသည် ဘလူးတုသ်သုံးသည်"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ဘက်ထရီ"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"အသံ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"မိုက်ခွက်ပါနားကြပ်"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"ဤစက်ပစ္စည်းကို သင့်မိဘက စီမံခန့်ခွဲသည်။ သင့်မိဘက သင်သုံးသောအက်ပ်များ၊ သင်၏တည်နေရာနှင့် အသုံးပြုချိန် ကဲ့သို့သော အချက်အလက်များကို မြင်နိုင်ပြီး စီမံခန့်ခွဲနိုင်သည်။"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent ဖြင့် ဆက်ဖွင့်ထားရန်"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"သူခိုးကာကွယ်ရေး\nစက်လော့ခ်ကျ၊ အကြိမ်များစွာဖွင့်ရန်ကြိုးစားထား"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>။ <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"အသံဆက်တင်များ"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"အလိုအလျောက် စာတန်းထိုးရန်"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"ပိတ်ရန်"</string>
<string name="sound_settings" msgid="8874581353127418308">"အသံနှင့် တုန်ခါမှု"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"ဆက်တင်များ"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"တိုက်ရိုက်စာတန်း"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"အသံကို ဘေးကင်းသည့်အဆင့်သို့ လျှော့ချလိုက်သည်"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"နားကြပ်အသံသည် အကြံပြုထားသည်ထက် အချိန်ကြာရှည်စွာ ကျယ်လောင်နေသည်"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"နားကြပ်အသံသည် ဤအပတ်အတွက် ဘေးကင်းသည့်ကန့်သတ်ချက်ထက် ကျော်သွားပါပြီ"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"တုန်ခါမှု"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s အသံအတိုးအလျှော့ ခလုတ်များ"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"ခေါ်ဆိုမှုများနှင့် အကြောင်းကြားချက်များအတွက် အသံမြည်နှုန်း (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>) ဖြစ်သည်"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> ကို ဖွင့်နေသည်"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"အောက်တွင်အသံဖွင့်မည်"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"စနစ် UI ဖမ်းစက်"</string>
<string name="status_bar" msgid="4357390266055077437">"အခြေအနေပြနေရာ"</string>
<string name="demo_mode" msgid="263484519766901593">"စနစ် UI စရုပ်ပြမုဒ်"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> ၌"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> တွင်"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"ဟော့စပေါ့"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"ဂြိုဟ်တု၊ ချိတ်ဆက်မှု မရှိပါ"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"ဂြိုဟ်တု၊ ချိတ်ဆက်မှု မကောင်းပါ"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ဂြိုဟ်တု၊ ချိတ်ဆက်မှု ကောင်းသည်"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ဂြိုဟ်တု၊ ချိတ်ဆက်မှု ရနိုင်သည်"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"အလုပ် ပရိုဖိုင်"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"အချို့သူများ အတွက် ပျော်စရာ ဖြစ်ပေမဲ့ အားလုံး အတွက် မဟုတ်ပါ"</string>
<string name="tuner_warning" msgid="1861736288458481650">"စနစ် UI Tuner က သင့်အတွက် Android အသုံးပြုသူ အင်တာဖေ့စ်ကို ပြောင်းရန်နှင့် စိတ်ကြိုက်ပြုလုပ်ရန် နည်းလမ်း အပိုများကို သင့်အတွက် စီစဉ်ပေးသည်။ အနာဂတ်ဗားရှင်းများတွင် ဤစမ်းသပ်အင်္ဂါရပ်များမှာ ပြောင်းလဲ၊ ပျက်စီး သို့မဟုတ် ပျောက်ကွယ်သွားနိုင်သည်။ သတိဖြင့် ရှေ့ဆက်ပါ။"</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"ဆက်တင်များ ဖွင့်ရန်"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Assistant ဖွင့်ရန်"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"လော့ခ်မျက်နှာပြင်"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"မှတ်စုများ ဖွင့်ရန်"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"မှတ်စုရေးရန်"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"စနစ်က တစ်ပြိုင်နက် များစွာလုပ်ခြင်း"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"လက်ရှိအက်ပ်ကို မျက်နှာပြင် ခွဲ၍ပြသမှု၏ ညာဘက်တွင်ထည့်ရန်"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"လက်ရှိအက်ပ်ကို မျက်နှာပြင် ခွဲ၍ပြသမှု၏ ဘယ်ဘက်တွင်ထည့်ရန်"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 48f0aee75afa..ed6e2931ed5f 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Trykk for å se"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Feil ved lagring av skjermopptaket"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Feil ved start av skjermopptaket"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Funksjon for opptak av problemer"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Opptak, databehandlingsproblem"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Pågående varsel for en innsamlingsøkt for et problem"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Tar opp problem"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Del"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Problemopptaket er lagret"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Trykk for å se"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Feil ved lagring av problemopptaket"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Feil ved start av problemopptaket"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Visning i fullskjerm"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Sveip ned fra toppen for å avslutte."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Greit"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Lagret"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"koble fra"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiver"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Slå på igjen i morgen automatisk"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Funksjoner som Quick Share, Finn enheten min og enhetsposisjon bruker Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Lyd"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Hodetelefoner"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Denne enheten administreres av forelderen din. Forelderen din kan se og administrere informasjon, for eksempel appene du bruker, posisjonen din og skjermtiden din."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Holdes opplåst med TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Tyveribeskyttelse\nEnheten er låst – mange opplåsingsforsøk"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Lydinnstillinger"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatisk medieteksting"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"deaktiver"</string>
<string name="sound_settings" msgid="8874581353127418308">"Lyd og vibrering"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Innstillinger"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Direkteteksting"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Volumet er senket til et tryggere nivå"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Volumet på hodetelefonene har vært høyt lenger enn anbefalt"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Volumet på hodetelefonene har overskredet sikkerhetsgrensen for denne uken"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrer"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s volumkontroller"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Anrop og varsler ringer (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Spiller av <xliff:g id="LABEL">%s</xliff:g> på"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Lyden spilles av på"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Statusrad"</string>
<string name="demo_mode" msgid="263484519766901593">"Demomodus for systemgrensesnitt"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"kl. <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Wifi-sone"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Satellitt – ingen tilkobling"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Satellitt – dårlig tilkobling"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellitt – god tilkobling"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellitt – tilkobling tilgjengelig"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Work-profil"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Gøy for noen – ikke for alle"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Med System UI Tuner har du flere måter å justere og tilpasse Android-brukergrensesnittet på. Disse eksperimentelle funksjonene kan endres, avbrytes eller fjernes i fremtidige utgivelser. Fortsett med forbehold."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Åpne innstillingene"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Åpne assistenten"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Låseskjerm"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Åpne notatene"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Ta et notat"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Multitasking på systemet"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Åpne delt skjerm med den aktive appen til høyre"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Åpne delt skjerm med den aktive appen til venstre"</string>
@@ -799,7 +792,7 @@
<string name="right_keycode" msgid="2480715509844798438">"Høyre-tastkode"</string>
<string name="left_icon" msgid="5036278531966897006">"Venstre-ikon"</string>
<string name="right_icon" msgid="1103955040645237425">"Høyre-ikon"</string>
- <string name="drag_to_add_tiles" msgid="8933270127508303672">"Hold og dra for å legge til ruter"</string>
+ <string name="drag_to_add_tiles" msgid="8933270127508303672">"Hold og dra for å legge til brikker"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Hold og dra for å flytte på rutene"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Dra hit for å fjerne"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Du trenger minst <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> infobrikker"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index d6f1cb4b6eeb..fb7680daf152 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"हेर्नका लागि ट्याप गर्नुहोस्"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"स्क्रिन रेकर्डिङ सेभ गर्ने क्रममा त्रुटि भयो"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"स्क्रिन रेकर्ड गर्न थाल्ने क्रममा त्रुटि भयो"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"समस्यासम्बन्धी जानकारी रेकर्ड गर्ने रेकर्डर"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"समस्याको रेकर्डिङ प्रोसेस गरिँदै छ"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"समस्यासम्बन्धी जानकारी सङ्कलन गर्ने जारी सत्रसम्बन्धी सूचना"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"रेकर्डिङसम्बन्धी समस्या"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"सेयर गर्नुहोस्"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"समस्याको रेकर्डिङ सेभ गरिएको छ"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"हेर्नका लागि ट्याप गर्नुहोस्"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"समस्याको रेकर्डिङ सेभ गर्ने क्रममा त्रुटि भयो"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"समस्यासम्बन्धी जानकारी रेकर्ड गर्न थाल्ने क्रममा त्रुटि भयो"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"फुल स्क्रिन हेरिँदै छ"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"बाहिरिन सिरानबाट तलतिर स्वाइप गर्नुहोस्।"</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"बुझेँ"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"सेभ गरिएको छ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"डिस्कनेक्ट गर्नुहोस्"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"एक्टिभेट गर्नुहोस्"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"भोलि फेरि स्वतः अन गरियोस्"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"क्विक सेयर, Find My Device र डिभाइसको लोकेसन जस्ता सुविधाहरूले ब्लुटुथ प्रयोग गर्छन्"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ब्याट्री"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"अडियो"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"यो डिभाइस तपाईंका अभिभावक व्यवस्थापन गर्नुहुन्छ। तपाईंका अभिभावक तपाईंले प्रयोग गर्ने एप, तपाईंको स्थान र तपाईंले यन्त्र चलाएर बिताउने समय जस्ता जानकारी हेर्न तथा व्यवस्थापन गर्न सक्नुहुन्छ।"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent ले खुला राखेको"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"चोरीबाट सुरक्षा\nडिभाइस लक गरिएको छ, अत्यधिक धेरै पटक अनलक गर्ने प्रयास गरिएको छ"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"ध्वनिसम्बन्धी सेटिङहरू"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"मिडियाको स्वत: क्याप्सन बनाउनुहोस्"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"असक्षम पार्नुहोस्"</string>
<string name="sound_settings" msgid="8874581353127418308">"साउन्ड तथा भाइब्रेसन"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"सेटिङ"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"लाइभ क्याप्सन"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"भोल्युम घटाएर सुरक्षित स्तरमा पुर्‍याइएको छ"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"हेडफोनको भोल्युम धेरै बेरदेखि उच्च छ"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"यो हप्ता हेडफोनको भोल्युमले सुरक्षित स्तरको सीमा नाघेको छ"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"कम्पन गर्नुहोस्"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s भोल्युमका नियन्त्रणहरू"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"कल तथा सूचनाहरू आउँदा घन्टी बज्ने छ (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> प्ले गरिँदै छ"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"अडियो प्ले भइरहने छ"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"सिस्टम UI ट्युनर"</string>
<string name="status_bar" msgid="4357390266055077437">"स्थिति पट्टी"</string>
<string name="demo_mode" msgid="263484519766901593">"सिस्टम UI को डेमो मोड"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> मा"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> मा"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Hotspot"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"स्याटलाइट कनेक्सन उपलब्ध छैन"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"स्याटलाइट, खराब कनेक्सन"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"स्याटलाइट, राम्रो कनेक्सन"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"स्याटलाइट, कनेक्सन उपलब्ध छ"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"कार्य प्रोफाइल"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"केहीका लागि रमाइलो हुन्छ तर सबैका लागि होइन"</string>
<string name="tuner_warning" msgid="1861736288458481650">"सिस्टम UI ट्युनरले तपाईँलाई Android प्रयोगकर्ता इन्टरफेस आफू अनुकूल गर्न र ट्विक गर्न थप तरिकाहरू प्रदान गर्छ। यी प्रयोगात्मक सुविधाहरू भावी विमोचनमा परिवर्तन हुन, बिग्रिन वा हराउन सक्ने छन्। सावधानीपूर्वक अगाडि बढ्नुहोस्।"</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"सेटिङ खोल्नुहोस्"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"एसिस्टेन्ट खोल्नुहोस्"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"स्क्रिन लक गर्नुहोस्"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"नोटहरू खोल्नुहोस्"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"नोट लेख"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"सिस्टम मल्टिटास्किङ"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"हालको एप दायाँतर्फ रहने गरी स्प्लिट स्क्रिन मोड सुरु गर्नुहोस्"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"हालको एप बायाँतर्फ रहने गरी स्प्लिट स्क्रिन मोड सुरु गर्नुहोस्"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 55f43f88ca6f..a649617a5559 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Tik om te bekijken"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Fout bij opslaan van schermopname"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Fout bij starten van schermopname"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Problemen opnemen"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Probleemopname verwerken"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Melding over actieve activiteit voor een sessie voor probleemverzameling"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Probleem opnemen"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Delen"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Probleemopname opgeslagen"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Tik om te bekijken"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Fout bij opslaan van probleemopname"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Fout bij starten van probleemopname"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Volledig scherm wordt getoond"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Swipe omlaag vanaf de bovenkant van het scherm om af te sluiten."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"OK"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Opgeslagen"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"loskoppelen"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activeren"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Morgen weer automatisch aanzetten"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Functies zoals Quick Share, Vind mijn apparaat en apparaatlocatie maken gebruik van bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batterijniveau"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Dit apparaat wordt beheerd door je ouder. Je ouder kan informatie bekijken en beheren, zoals de apps die je gebruikt, je locatie en je schermtijd."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Ontgrendeld gehouden door TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Diefstalbeveiliging\nApparaat vergrendeld, te veel ontgrendelpogingen"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Geluidsinstellingen"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatisch ondertitelen"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"uitzetten"</string>
<string name="sound_settings" msgid="8874581353127418308">"Geluid en trillen"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Instellingen"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Live ondertiteling"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Volume verlaagd naar een veiliger niveau"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Het hoofdtelefoonvolume is langer dan de aanbevolen tijd hoog geweest"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Het hoofdtelefoonvolume overschrijdt de veiligheidslimiet voor deze week"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"trillen"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s-volumeknoppen"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Geluid bij gesprekken en meldingen (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> wordt afgespeeld op"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio wordt afgespeeld op"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Systeem-UI-tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Statusbalk"</string>
<string name="demo_mode" msgid="263484519766901593">"Demomodus voor systeemgebruikersinterface"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"om <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"op <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Hotspot"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Satelliet, geen verbinding"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Satelliet, slechte verbinding"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelliet, goede verbinding"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelliet, verbinding beschikbaar"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Werkprofiel"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Leuk voor sommige gebruikers, maar niet voor iedereen"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Met Systeem-UI-tuner beschikt u over extra manieren om de Android-gebruikersinterface aan te passen. Deze experimentele functies kunnen veranderen, vastlopen of verdwijnen in toekomstige releases. Ga voorzichtig verder."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Instellingen openen"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Assistent openen"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Scherm vergrendelen"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Notities openen"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Notitie maken"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Systeem-multitasking"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Gesplitst scherm openen met huidige app rechts"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Gesplitst scherm openen met huidige app links"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 0c51f5d2a1bd..b0f5022c9c17 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"ଦେଖିବାକୁ ଟାପ୍ କରନ୍ତୁ"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"ସ୍କ୍ରିନ ରେକର୍ଡିଂ ସେଭ କରିବାରେ ତ୍ରୁଟି"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"ସ୍କ୍ରିନ୍ ରେକର୍ଡିଂ ଆରମ୍ଭ କରିବାରେ ତ୍ରୁଟି"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"ସମସ୍ୟା ରେକର୍ଡର"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"ସମସ୍ୟାର ରେକର୍ଡିଂର ପ୍ରକ୍ରିୟାକରଣ"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"ଏକ ସମସ୍ୟା ସଂଗ୍ରହ ସେସନ ପାଇଁ ଚାଲୁଥିବା ବିଜ୍ଞପ୍ତି"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"ରେକର୍ଡିଂରେ ସମସ୍ୟା"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"ସେୟାର କରନ୍ତୁ"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"ସମସ୍ୟାର ରେକର୍ଡିଂକୁ ସେଭ କରାଯାଇଛି"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"ଦେଖିବାକୁ ଟାପ୍ କରନ୍ତୁ"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"ସମସ୍ୟାର ରେକର୍ଡିଂ କରିବାରେ ତ୍ରୁଟି"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"ସମସ୍ୟାର ରେକର୍ଡିଂ ଆରମ୍ଭ କରିବାରେ ତ୍ରୁଟି"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନରେ ଦେଖିବା"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"ବାହାରି ଯିବା ପାଇଁ, ଶୀର୍ଷରୁ ତଳକୁ ସ୍ୱାଇପ କରନ୍ତୁ।"</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"ବୁଝିଗଲି"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ସେଭ କରାଯାଇଛି"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ଡିସକନେକ୍ଟ କରନ୍ତୁ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ଚାଲୁ କରନ୍ତୁ"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ଆସନ୍ତାକାଲି ସ୍ୱତଃ ପୁଣି ଚାଲୁ ହେବ"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Quick Share, Find My Device ଏବଂ ଡିଭାଇସ ଲୋକେସନ ପରି ଫିଚରଗୁଡ଼ିକ ବ୍ଲୁଟୁଥ ବ୍ୟବହାର କରେ"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ବ୍ୟାଟେରୀ"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ଅଡିଓ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ହେଡସେଟ୍‍"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"ଏହି ଡିଭାଇସ୍ ଆପଣଙ୍କ ବାପାମାଙ୍କ ଦ୍ୱାରା ପରିଚାଳିତ। ଆପଣଙ୍କ ବାପାମା ଆପଣ ବ୍ୟବହାର କରୁଥିବା ଆପ୍ସ, ଆପଣଙ୍କ ଲୋକେସନ୍ ଓ ସ୍କ୍ରିନ୍ ସମୟ ପରି ସୂଚନା ଦେଖିପାରିବେ ଏବଂ ପରିଚାଳନା କରିପାରିବେ।"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent ଦ୍ୱାରା ଅନ୍‌ଲକ୍ ରହିଛି"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"ଥେଫ୍ଟ ସୁରକ୍ଷା\nଡିଭାଇସ ଲକ କରାଯାଇଛି, ଅନେକଗୁଡ଼ିଏ ଅନଲକ ପ୍ରଚେଷ୍ଟା"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"ସାଉଣ୍ଡ ସେଟିଂସ୍"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"ସ୍ବଚାଳିତ କ୍ୟାପ୍ସନ୍ ମିଡିଆ"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"ଅକ୍ଷମ କରନ୍ତୁ"</string>
<string name="sound_settings" msgid="8874581353127418308">"ସାଉଣ୍ଡ ଓ ଭାଇବ୍ରେସନ"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"ସେଟିଂସ"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"ଲାଇଭ କେପ୍ସନ"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"ଭଲ୍ୟୁମକୁ ସୁରକ୍ଷିତ ଲେଭେଲକୁ କମ କରାଯାଇଛି"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"ସୁପାରିଶ ଭଲ୍ୟୁମ ଠାରୁ ହେଡଫୋନର ଭଲ୍ୟୁମ ଅଧିକ ଅଛି"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"ଏହି ସପ୍ତାହ ପାଇଁ ହେଡଫୋନର ଭଲ୍ୟୁମ ସୁରକ୍ଷିତ ସୀମାକୁ ଅତିକ୍ରମ କରିଛି"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ଭାଇବ୍ରେଟ୍"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s ଭଲ୍ୟୁମ୍ ନିୟନ୍ତ୍ରଣ"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"କଲ୍ ଓ ବିଜ୍ଞପ୍ତି ପାଇଁ (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)ରେ ରିଙ୍ଗ ହେବ"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g>ରେ ପ୍ଲେ କରାଯାଉଛି"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"ଅଡିଓ ପ୍ଲେ ହେବ"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"ସିଷ୍ଟମ୍ UI ଟ୍ୟୁନର୍‍"</string>
<string name="status_bar" msgid="4357390266055077437">"ଷ୍ଟାଟସ୍‍ ବାର୍‍"</string>
<string name="demo_mode" msgid="263484519766901593">"ସିଷ୍ଟମ୍‌ UI ଡେମୋ ମୋଡ୍‌"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> ହେଲେ"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> ବେଳେ"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"ହଟସ୍ପଟ୍‌"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"ସାଟେଲାଇଟ, କୌଣସି କନେକ୍ସନ ନାହିଁ"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"ସାଟେଲାଇଟ, ଦୁର୍ବଳ କନେକ୍ସନ"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ସାଟେଲାଇଟ, ଭଲ କନେକ୍ସନ"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ସାଟେଲାଇଟ, କନେକ୍ସନ ଉପଲବ୍ଧ"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"ୱର୍କ ପ୍ରୋଫାଇଲ୍‌"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"କେତେକଙ୍କ ପାଇଁ ମଜାଦାର, କିନ୍ତୁ ସମସ୍ତଙ୍କ ପାଇଁ ନୁହେଁ"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Android ୟୁଜର୍‍ ଇଣ୍ଟରଫେସ୍‍ ବଦଳାଇବାକୁ ତଥା ନିଜ ପସନ୍ଦ ଅନୁଯାୟୀ କରିବାକୁ ସିଷ୍ଟମ୍‍ UI ଟ୍ୟୁନର୍‍ ଆପଣଙ୍କୁ ଅତିରିକ୍ତ ଉପାୟ ପ୍ରଦାନ କରେ। ଏହି ପରୀକ୍ଷାମୂଳକ ସୁବିଧାମାନ ବଦଳିପାରେ, ଭାଙ୍ଗିପାରେ କିମ୍ବା ଭବିଷ୍ୟତର ରିଲିଜ୍‌ଗୁଡ଼ିକରେ ନଦେଖାଯାଇପାରେ। ସତର୍କତାର ସହ ଆଗକୁ ବଢ଼ନ୍ତୁ।"</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"ସେଟିଂସ ଖୋଲନ୍ତୁ"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Assistant ଖୋଲନ୍ତୁ"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"ଲକ ସ୍କ୍ରିନ"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Notes ଖୋଲନ୍ତୁ"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"ଏକ ନୋଟ ଲେଖନ୍ତୁ"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"ସିଷ୍ଟମ ମଲ୍ଟିଟାସ୍କିଂ"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"RHSରେ ବର୍ତ୍ତମାନର ଆପ ସହ ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନକୁ ପ୍ରବେଶ କରାନ୍ତୁ"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"LHSରେ ବର୍ତ୍ତମାନର ଆପ ସହ ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନକୁ ପ୍ରବେଶ କରାନ୍ତୁ"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 79bd8d59d2dc..6916fd2b3be9 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"ਦੇਖਣ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡਿੰਗ ਨੂੰ ਰੱਖਿਅਤ ਕਰਨ ਵੇਲੇ ਗੜਬੜ ਹੋ ਗਈ"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡਿੰਗ ਨੂੰ ਸ਼ੁਰੂ ਕਰਨ ਵੇਲੇ ਗੜਬੜ ਹੋਈ"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"ਸਮੱਸਿਆ ਰਿਕਾਰਡਰ"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"ਸਮੱਸਿਆ ਰਿਕਾਰਡਿੰਗ ਪ੍ਰਕਿਰਿਆ-ਅਧੀਨ ਹੈ"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"ਸਮੱਸਿਆ ਬਾਰੇ ਜਾਣਕਾਰੀ ਇਕੱਤਰ ਕਰਨ ਵਾਲੇ ਸੈਸ਼ਨ ਸੰਬੰਧੀ ਨਿਰੰਤਰ ਸੂਚਨਾ"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"ਸਮੱਸਿਆ ਨੂੰ ਰਿਕਾਰਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"ਸਾਂਝਾ ਕਰੋ"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"ਸਮੱਸਿਆ ਰਿਕਾਰਡਿੰਗ ਨੂੰ ਰੱਖਿਅਤ ਕੀਤਾ ਗਿਆ"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"ਦੇਖਣ ਲਈ ਟੈਪ ਕਰੋ"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"ਸਮੱਸਿਆ ਰਿਕਾਰਡਿੰਗ ਰੱਖਿਅਤ ਕਰਨ ਵੇਲੇ ਗੜਬੜ ਹੋ ਗਈ"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"ਸਮੱਸਿਆ ਰਿਕਾਰਡਿੰਗ ਨੂੰ ਸ਼ੁਰੂ ਕਰਨ ਵੇਲੇ ਗੜਬੜ ਹੋ ਗਈ"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"ਪੂਰੀ ਸਕ੍ਰੀਨ \'ਤੇ ਦੇਖਿਆ ਜਾ ਰਿਹਾ ਹੈ"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"ਬਾਹਰ ਜਾਣ ਲਈ, ਉਪਰੋਂ ਹੇਠਾਂ ਵੱਲ ਸਵਾਈਪ ਕਰੋ।"</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"ਸਮਝ ਲਿਆ"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ਰੱਖਿਅਤ ਕੀਤਾ ਗਿਆ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ਡਿਸਕਨੈਕਟ ਕਰੋ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ਕਿਰਿਆਸ਼ੀਲ ਕਰੋ"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ਕੱਲ੍ਹ ਨੂੰ ਆਪਣੇ ਆਪ ਚਾਲੂ ਹੋ ਜਾਵੇਗਾ"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"ਕਵਿੱਕ ਸ਼ੇਅਰ, Find My Device ਅਤੇ ਡੀਵਾਈਸ ਦਾ ਟਿਕਾਣਾ ਵਰਗੀਆਂ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਬਲੂਟੁੱਥ ਦੀ ਵਰਤੋਂ ਕਰਦੀਆਂ ਹਨ"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ਬੈਟਰੀ"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ਆਡੀਓ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ਹੈੱਡਸੈੱਟ"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"ਇਸ ਡੀਵਾਈਸ ਦਾ ਪ੍ਰਬੰਧਨ ਤੁਹਾਡੇ ਮਾਂ-ਪਿਓ ਵੱਲੋਂ ਕੀਤਾ ਜਾਂਦਾ ਹੈ। ਤੁਹਾਡੇ ਮਾਂ-ਪਿਓ ਤੁਹਾਡੀਆਂ ਐਪਾਂ ਦੀ ਵਰਤੋਂ, ਤੁਹਾਡੇ ਟਿਕਾਣੇ ਅਤੇ ਤੁਹਾਡੇ ਸਕ੍ਰੀਨ ਸਮੇਂ ਵਰਗੀ ਜਾਣਕਾਰੀ ਨੂੰ ਦੇਖ ਅਤੇ ਉਸਦਾ ਪ੍ਰਬੰਧਨ ਕਰ ਸਕਦੇ ਹਨ।"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"ਟਰੱਸਟ-ਏਜੰਟ ਵੱਲੋਂ ਅਣਲਾਕ ਰੱਖਿਆ ਗਿਆ"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"ਚੋਰੀ ਤੋਂ ਸੁਰੱਖਿਆ\nਡੀਵਾਈਸ ਲਾਕ ਹੋ ਗਿਆ, ਅਣਲਾਕ ਕਰਨ ਦੀਆਂ ਕਈ ਕੋਸ਼ਿਸ਼ਾਂ ਕੀਤੀਆਂ ਗਈਆਂ"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"ਧੁਨੀ ਸੈਟਿੰਗਾਂ"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"ਸਵੈਚਲਿਤ ਸੁਰਖੀ ਮੀਡੀਆ"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"ਬੰਦ ਕਰੋ"</string>
<string name="sound_settings" msgid="8874581353127418308">"ਧੁਨੀ ਅਤੇ ਥਰਥਰਾਹਟ"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"ਸੈਟਿੰਗਾਂ"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"ਲਾਈਵ ਸੁਰਖੀਆਂ"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"ਅਵਾਜ਼ ਨੂੰ ਜ਼ਿਆਦਾ ਸੁਰੱਖਿਅਤ ਪੱਧਰ ਤੱਕ ਘੱਟ ਕੀਤਾ ਗਿਆ"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"ਹੈੱਡਫ਼ੋਨ ਦੀ ਅਵਾਜ਼ ਸਿਫ਼ਾਰਸ਼ੀ ਪੱਧਰ ਨਾਲੋਂ ਜ਼ਿਆਦਾ ਲੰਬੇ ਸਮੇਂ ਤੱਕ ਉੱਚੀ ਰਹੀ"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"ਹੈੱਡਫ਼ੋਨ ਦੀ ਅਵਾਜ਼ ਇਸ ਹਫ਼ਤੇ ਦੀ ਸੁਰੱਖਿਅਤ ਸੀਮਾ ਨੂੰ ਪਾਰ ਕਰ ਗਈ"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ਥਰਥਰਾਹਟ"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s ਵੌਲਿਊਮ ਕੰਟਰੋਲ"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"ਕਾਲਾਂ ਆਉਣ ਅਤੇ ਸੂਚਨਾਵਾਂ ਮਿਲਣ \'ਤੇ ਘੰਟੀ ਵਜੇਗੀ (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> ਚਲਾਇਆ ਜਾ ਰਿਹਾ ਹੈ"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"ਆਡੀਓ ਇਸ \'ਤੇ ਚੱਲੇਗੀ"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI ਟਿਊਨਰ"</string>
<string name="status_bar" msgid="4357390266055077437">"ਸਥਿਤੀ ਪੱਟੀ"</string>
<string name="demo_mode" msgid="263484519766901593">"ਸਿਸਟਮ UI ਡੈਮੋ ਮੋਡ"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> ਵਜੇ"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> ਵਜੇ"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"ਹੌਟਸਪੌਟ"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"ਸੈਟੇਲਾਈਟ, ਕਨੈਕਸ਼ਨ ਨਹੀਂ ਹੈ"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"ਸੈਟੇਲਾਈਟ, ਕਨੈਕਸ਼ਨ ਖਰਾਬ ਹੈ"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ਸੈਟੇਲਾਈਟ, ਕਨੈਕਸ਼ਨ ਵਧੀਆ ਹੈ"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ਸੈਟੇਲਾਈਟ, ਕਨੈਕਸ਼ਨ ਉਪਲਬਧ ਹੈ"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"ਕੁਝ ਵਾਸਤੇ ਤਾਂ ਮਜ਼ੇਦਾਰ ਹੈ ਲੇਕਿਨ ਸਾਰਿਆਂ ਵਾਸਤੇ ਨਹੀਂ"</string>
<string name="tuner_warning" msgid="1861736288458481650">"ਸਿਸਟਮ UI ਟਿਊਨਰ ਤੁਹਾਨੂੰ Android ਵਰਤੋਂਕਾਰ ਇੰਟਰਫ਼ੇਸ ਤਬਦੀਲ ਕਰਨ ਅਤੇ ਵਿਉਂਤਬੱਧ ਕਰਨ ਲਈ ਵਾਧੂ ਤਰੀਕੇ ਦਿੰਦਾ ਹੈ। ਇਹ ਪ੍ਰਯੋਗਾਤਮਿਕ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਭਵਿੱਖ ਦੀ ਰੀਲੀਜ਼ ਵਿੱਚ ਬਦਲ ਸਕਦੀਆਂ ਹਨ, ਟੁੱਟ ਸਕਦੀਆਂ ਹਨ, ਜਾਂ ਅਲੋਪ ਹੋ ਸਕਦੀਆਂ ਹਨ। ਸਾਵਧਾਨੀ ਨਾਲ ਅੱਗੇ ਵੱਧੋ।"</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹੋ"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Assistant ਖੋਲ੍ਹੋ"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"ਲਾਕ ਸਕ੍ਰੀਨ"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"ਨੋਟਸ ਖੋਲ੍ਹੋ"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"ਨੋਟ ਲਿਖੋ"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"ਸਿਸਟਮ ਮਲਟੀਟਾਸਕਿੰਗ"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"RHS ਲਈ ਮੌਜੂਦਾ ਐਪ ਨਾਲ ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਵਿੱਚ ਦਾਖਲ ਹੋਵੋ"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"LHS ਲਈ ਮੌਜੂਦਾ ਐਪ ਨਾਲ ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਵਿੱਚ ਦਾਖਲ ਹੋਵੋ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 019127dd0634..c04d6dae6e0d 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Kliknij, aby wyświetlić"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Podczas zapisywania nagrania ekranu wystąpił błąd"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Błąd podczas rozpoczynania rejestracji zawartości ekranu"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Rejestrator problemów"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Przetwarzam nagranie problemu"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Powiadomienie o trwającej aktywności sesji nagrywania ekranu"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Nagrywanie problemu"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Udostępnij"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Zapisano nagranie problemu"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Kliknij, aby wyświetlić"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Błąd podczas zapisywania nagrania problemu"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Błąd podczas rozpoczynania nagrania problemu"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Włączony pełny ekran"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Aby wyjść, przesuń palcem z góry na dół."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"OK"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Zapisane"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"rozłącz"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktywuj"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatycznie włącz ponownie jutro"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Funkcje takie jak Szybkie udostępnianie, Znajdź moje urządzenie i dotyczące lokalizacji urządzenia używają Bluetootha"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> naładowania baterii"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Dźwięk"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Zestaw słuchawkowy"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Tym urządzeniem zarządza Twój rodzic. Rodzic może zobaczyć różne informacje, np. o aplikacjach, których używasz, lokalizacji i czasie korzystania z urządzenia, a także zarządzać tymi danymi."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Blokada anulowana przez agenta zaufania"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Ochrona przed kradzieżą\nZablokowano – za dużo prób logowania."</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Ustawienia dźwięku"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Autom. napisy do multimediów"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"wyłącz"</string>
<string name="sound_settings" msgid="8874581353127418308">"Dźwięk i wibracje"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Ustawienia"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Napisy na żywo"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Głośność obniżona do bezpieczniejszego poziomu"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Głośność na słuchawkach jest zbyt duża przez czas dłuższy niż zalecany"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Głośność na słuchawkach przekroczyła limit bezpieczeństwa na ten tydzień"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"włącz wibracje"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Sterowanie głośnością: %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Połączenia i powiadomienia będą uruchamiały dzwonek (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Odtwarzam <xliff:g id="LABEL">%s</xliff:g> na"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Wyjścia dźwięku:"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Kalibrator System UI"</string>
<string name="status_bar" msgid="4357390266055077437">"Pasek stanu"</string>
<string name="demo_mode" msgid="263484519766901593">"Tryb demonstracyjny interfejsu"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"o <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"w: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Hotspot"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Satelita – brak połączenia"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Satelita – połączenie słabe"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelita – połączenie dobre"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelita – połączenie dostępne"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Profil służbowy"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Dobra zabawa, ale nie dla każdego"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Kalibrator System UI udostępnia dodatkowe sposoby dostrajania i dostosowywania interfejsu Androida. Te eksperymentalne funkcje mogą się zmienić, popsuć lub zniknąć w przyszłych wersjach. Zachowaj ostrożność."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Otwieranie ustawień"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Otwieranie asystenta"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Blokada ekranu"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Otwieranie notatek"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Zanotuj"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Wielozadaniowość w systemie"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Uruchamianie trybu podzielonego ekranu z bieżącą aplikacją po prawej"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Uruchamianie trybu podzielonego ekranu z bieżącą aplikacją po lewej"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index a6341cc3f628..e82a1032dbc6 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Toque para ver"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Erro ao salvar a gravação da tela"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Erro ao iniciar a gravação de tela"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Gravador de problemas"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Processando a gravação do problema"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Notificação em andamento para uma sessão de coleta de problemas"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Gravando o problema"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Compartilhar"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"A gravação do problema foi salva"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Toque para ver"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Erro ao salvar a gravação do problema"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Erro ao iniciar a gravação do problema"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Visualização em tela cheia"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Para sair, deslize de cima para baixo."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Entendi"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvo"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ativar"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ativar automaticamente de novo amanhã"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Recursos como o Quick Share, o Encontre Meu Dispositivo e a localização do dispositivo usam o Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Fone de ouvido"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Este dispositivo é gerenciado pelo seu familiar responsável, que pode ver e gerenciar informações como os apps que você usa, sua localização e seu tempo de uso."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Desbloqueado pelo TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Proteção contra roubo\nDispositivo bloqueado por tentativas de desbloqueio em excesso"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Configurações de som"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Transcrição automática"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"desativar"</string>
<string name="sound_settings" msgid="8874581353127418308">"Som e vibração"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Configurações"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Legenda instantânea"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Volume diminuído para um nível mais seguro"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"O volume dos fones de ouvido está alto há mais tempo que o recomendado"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"O volume dos fones de ouvido excedeu o limite de segurança para esta semana"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Controles de volume %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Chamadas e notificações farão o smartphone tocar (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Tocando <xliff:g id="LABEL">%s</xliff:g> em"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"O áudio vai tocar em"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Sintonizador System UI"</string>
<string name="status_bar" msgid="4357390266055077437">"Barra de status"</string>
<string name="demo_mode" msgid="263484519766901593">"Modo de demonstração da interface do sistema"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"às <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Ponto de acesso"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Satélite, sem conexão"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Satélite, conexão ruim"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satélite, conexão boa"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satélite, conexão disponível"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Perfil de trabalho"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Diversão para alguns, mas não para todos"</string>
<string name="tuner_warning" msgid="1861736288458481650">"O sintonizador System UI fornece maneiras adicionais de ajustar e personalizar a interface do usuário do Android. Esses recursos experimentais podem mudar, falhar ou desaparecer nas versões futuras. Prossiga com cuidado."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Abrir configurações"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Abrir o Google Assistente"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Tela de bloqueio"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Abrir observações"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Crie uma nota"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Multitarefa do sistema"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Usar a tela dividida com o app atual à direita"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Usar a tela dividida com o app atual à esquerda"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 72cc01e52476..8ba907b4ee41 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Toque para ver"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Erro ao guardar a gravação de ecrã"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Ocorreu um erro ao iniciar a gravação do ecrã."</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Registador de problemas"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"A proc. registo de problemas"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Notificação em curso de uma sessão de recolha de problemas"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Problema na gravação"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Partilhar"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Registo de problemas guardado"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Toque para ver"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Erro ao guardar o registo de problemas"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Erro ao iniciar o registo de problemas"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Visualização de ecrã inteiro"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Para sair, deslize rapidamente para baixo a partir da parte superior."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"OK"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Guardado"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desassociar"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ativar"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Reativar amanhã automaticamente"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"As funcionalidades como Partilha rápida, Localizar o meu dispositivo e localização do dispositivo usam o Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de bateria"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ausc. c/ mic. integ."</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Este dispositivo é gerido pelos teus pais, que podem ver e gerir informações como as apps que utilizas, a tua localização e o tempo de utilização."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Mantido desbloqueado pelo TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Proteção contra roubo\nDisp. bloq., muitas tentativas de desb."</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Definições de som"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Multim. legendas automáticas"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"desativar"</string>
<string name="sound_settings" msgid="8874581353127418308">"Som e vibração"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Definições"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Legendas instantâneas"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Volume reduzido para um nível mais seguro"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"O volume dos auscultadores está elevado há mais tempo do que o recomendado"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"O volume dos auscultadores excedeu o limite seguro para esta semana"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Controlos de volume de %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"As chamadas e as notificações tocam (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"A ouvir <xliff:g id="LABEL">%s</xliff:g> em"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"O áudio será ouv. em"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Sintonizador da interface do sistema"</string>
<string name="status_bar" msgid="4357390266055077437">"Barra de estado"</string>
<string name="demo_mode" msgid="263484519766901593">"Modo de demonstração da IU do sistema"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"às <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"em <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Zona Wi-Fi"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Satélite, sem ligação"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Satélite, ligação fraca"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satélite, boa ligação"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satélite, ligação disponível"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Perfil de trabalho"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Diversão para alguns, mas não para todos"</string>
<string name="tuner_warning" msgid="1861736288458481650">"O Sintonizador da interface do sistema disponibiliza-lhe formas adicionais ajustar e personalizar a interface do utilizador do Android. Estas funcionalidades experimentais podem ser alteradas, deixar de funcionar ou desaparecer em versões futuras. Prossiga com cuidado."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Abrir definições"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Abrir Assistente"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Ecrã de bloqueio"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Abrir notas"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Tire uma nota"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Execução de várias tarefas em simultâneo no sistema"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Aceder ao ecrã dividido com a app atual para RHS"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Aceder ao ecrã dividido com a app atual para LHS"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index a6341cc3f628..e82a1032dbc6 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Toque para ver"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Erro ao salvar a gravação da tela"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Erro ao iniciar a gravação de tela"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Gravador de problemas"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Processando a gravação do problema"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Notificação em andamento para uma sessão de coleta de problemas"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Gravando o problema"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Compartilhar"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"A gravação do problema foi salva"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Toque para ver"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Erro ao salvar a gravação do problema"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Erro ao iniciar a gravação do problema"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Visualização em tela cheia"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Para sair, deslize de cima para baixo."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Entendi"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvo"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ativar"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ativar automaticamente de novo amanhã"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Recursos como o Quick Share, o Encontre Meu Dispositivo e a localização do dispositivo usam o Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Fone de ouvido"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Este dispositivo é gerenciado pelo seu familiar responsável, que pode ver e gerenciar informações como os apps que você usa, sua localização e seu tempo de uso."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Desbloqueado pelo TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Proteção contra roubo\nDispositivo bloqueado por tentativas de desbloqueio em excesso"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Configurações de som"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Transcrição automática"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"desativar"</string>
<string name="sound_settings" msgid="8874581353127418308">"Som e vibração"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Configurações"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Legenda instantânea"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Volume diminuído para um nível mais seguro"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"O volume dos fones de ouvido está alto há mais tempo que o recomendado"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"O volume dos fones de ouvido excedeu o limite de segurança para esta semana"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Controles de volume %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Chamadas e notificações farão o smartphone tocar (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Tocando <xliff:g id="LABEL">%s</xliff:g> em"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"O áudio vai tocar em"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Sintonizador System UI"</string>
<string name="status_bar" msgid="4357390266055077437">"Barra de status"</string>
<string name="demo_mode" msgid="263484519766901593">"Modo de demonstração da interface do sistema"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"às <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Ponto de acesso"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Satélite, sem conexão"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Satélite, conexão ruim"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satélite, conexão boa"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satélite, conexão disponível"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Perfil de trabalho"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Diversão para alguns, mas não para todos"</string>
<string name="tuner_warning" msgid="1861736288458481650">"O sintonizador System UI fornece maneiras adicionais de ajustar e personalizar a interface do usuário do Android. Esses recursos experimentais podem mudar, falhar ou desaparecer nas versões futuras. Prossiga com cuidado."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Abrir configurações"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Abrir o Google Assistente"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Tela de bloqueio"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Abrir observações"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Crie uma nota"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Multitarefa do sistema"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Usar a tela dividida com o app atual à direita"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Usar a tela dividida com o app atual à esquerda"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index fc420a02aa2e..8cd2d77f9efb 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Atinge pentru a afișa"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Eroare la salvarea înregistrării ecranului"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Eroare la începerea înregistrării ecranului"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Instrument de înregistrare a problemelor"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Se procesează înregistrarea problemei"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Notificare în curs pentru o sesiune de înregistrare a problemei"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Se înregistrează problema"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Trimite"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Înregistrarea problemei a fost salvată"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Atinge pentru a afișa"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Eroare la salvarea înregistrării problemei"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Eroare la începerea înregistrării problemei"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Vizualizare pe ecran complet"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Pentru a ieși, glisează de sus în jos."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"OK"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvat"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"deconectează"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activează"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Activează din nou automat mâine"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Funcții precum Quick Share, Găsește-mi dispozitivul și locația dispozitivului folosesc Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Nivelul bateriei: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Căști"</string>
@@ -307,7 +300,7 @@
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Luminozitate"</string>
<string name="quick_settings_inversion_label" msgid="3501527749494755688">"Inversarea culorilor"</string>
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Corecția culorii"</string>
- <string name="quick_settings_font_scaling_label" msgid="5289001009876936768">"Dimensiunea fontului"</string>
+ <string name="quick_settings_font_scaling_label" msgid="5289001009876936768">"Mărimea fontului"</string>
<string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Gestionează"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Terminat"</string>
<string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Închide"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Dispozitivul este gestionat de unul dintre părinți. Părintele poate să vadă și să gestioneze informații cum ar fi aplicațiile pe care le folosești, locația ta și durata de folosire a dispozitivului."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Deblocat de TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Protecție anti-furt\nDispozitiv blocat, prea multe încercări de deblocare"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Setări de sunet"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Adaugă subtitrări automate la fișierele media"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"dezactivează"</string>
<string name="sound_settings" msgid="8874581353127418308">"Sunete și vibrații"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Setări"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Subtitrări live"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Volumul a fost redus la un nivel mai sigur"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Volumul căștilor a fost ridicat mai mult timp decât este recomandat"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Volumul căștilor a depășit limita de siguranță pentru săptămâna aceasta"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrații"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Comenzi de volum pentru %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Apelurile și notificările vor suna (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Se redă <xliff:g id="LABEL">%s</xliff:g> pe"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Conținutul audio se va reda pe"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Bară de stare"</string>
<string name="demo_mode" msgid="263484519766901593">"Mod demonstrativ pentru IU sistem"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"la <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Hotspot"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Satelit, fără conexiune"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Satelit, conexiune slabă"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, conexiune bună"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, conexiune disponibilă"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Profil de serviciu"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Distractiv pentru unii, dar nu pentru toată lumea"</string>
<string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner oferă modalități suplimentare de a ajusta și a personaliza interfața de utilizare Android. Aceste funcții experimentale pot să se schimbe, să se blocheze sau să dispară din versiunile viitoare. Continuă cu prudență."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Deschide setările"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Deschide Asistentul"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Ecranul de blocare"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Deschide notele"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Creează o notă"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Multitasking pe sistem"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Accesează ecranul împărțit cu aplicația actuală în dreapta"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Accesează ecranul împărțit cu aplicația actuală în stânga"</string>
@@ -918,7 +911,7 @@
<string name="privacy_type_media_projection" msgid="8136723828804251547">"înregistrare de ecran"</string>
<string name="music_controls_no_title" msgid="4166497066552290938">"Fără titlu"</string>
<string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"Standby"</string>
- <string name="font_scaling_dialog_title" msgid="6273107303850248375">"Dimensiunea fontului"</string>
+ <string name="font_scaling_dialog_title" msgid="6273107303850248375">"Mărimea fontului"</string>
<string name="font_scaling_smaller" msgid="1012032217622008232">"Micșorează"</string>
<string name="font_scaling_larger" msgid="5476242157436806760">"Mărește"</string>
<string name="magnification_window_title" msgid="4863914360847258333">"Fereastra de mărire"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index a24039bf9c5a..614eb0617fb4 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Нажмите, чтобы посмотреть."</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Не удалось сохранить запись видео с экрана."</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Не удалось начать запись видео с экрана."</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Запись проблем на видео"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Обрабатываем запись"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Текущее уведомление о записи проблемы на видео"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Записываем проблему на видео"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Поделиться"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Запись сохранена."</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Нажмите, чтобы посмотреть."</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Не удалось сохранить запись."</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Не удалось начать запись."</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Полноэкранный режим"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Чтобы выйти, проведите по экрану сверху вниз."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"ОК"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Сохранено"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"отключить"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"активировать"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Включить завтра автоматически"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Bluetooth используется в сервисе \"Найти устройство\", таких функциях, как Быстрая отправка, и при определении местоположения устройства"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Заряд: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудиоустройство"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Этим устройством управляет один из твоих родителей. Он может видеть, например, какими приложениями ты пользуешься и где находишься, а также задавать определенные настройки (например, ограничивать время использования устройства)."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"Сеть VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Разблокировано агентом доверия"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Защита от кражи\nУстр-во заблокировано. Слишком много попыток."</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>."</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Настройки звука"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Автоматически добавлять субтитры"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"отключить"</string>
<string name="sound_settings" msgid="8874581353127418308">"Звук и вибрация"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Открыть настройки"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Автоматические субтитры"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Громкость уменьшена до безопасного уровня"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Вы используете наушники при высоком уровне громкости дольше, чем рекомендуется."</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Превышен безопасный лимит громкости наушников на этой неделе."</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"включить вибрацию"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s: регулировка громкости"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Для звонков и уведомлений включен звук (уровень громкости: <xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> – запущено здесь:"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Проигрывание аудио:"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Строка состояния"</string>
<string name="demo_mode" msgid="263484519766901593">"Интерфейс системы: деморежим"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Точка доступа"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Спутниковая связь, нет соединения"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Спутниковая связь, плохое качество соединения"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Спутниковая связь, хорошее качество соединения"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Доступно соединение по спутниковой связи"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Рабочий профиль"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Внимание!"</string>
<string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner позволяет настраивать интерфейс устройства Android по вашему вкусу. В будущем эта экспериментальная функция может измениться, перестать работать или исчезнуть."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Открыть настройки"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Открыть Ассистента"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Заблокировать экран"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Открыть заметки"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Создать заметку"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Режим многозадачности"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Включить разделение экрана с текущим приложением справа"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Включить разделение экрана с текущим приложением слева"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index de347ad273e7..d1bcee422bd4 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"බැලීමට තට්ටු කරන්න"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"තිර පටිගත කිරීම සුරැකීමේ දෝෂයකි"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"තිර පටිගත කිරීම ආරම්භ කිරීමේ දෝෂයකි"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"ගැටලු රෙකෝඩරය"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"ගැටලු වාර්තාව සැකසුම් කිරීම"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"ගැටලු එකතු කිරීමේ සැසියක් සඳහා දැනට පවතින දැනුම්දීම"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"ගැටලුව සටහන් කිරීම"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"බෙදා ගන්න"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"ගැටලු පටිගත කිරීම සුරැකිණි"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"බැලීමට තට්ටු කරන්න"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"ගැටලුව සටහන් කිරීම සුරැකීමේ දෝෂය"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"ගැටලුව වාර්තා කිරීම ආරම්භ කිරීමේ දෝෂය"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"මුළු තිරය බලමින්"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"ඉවත් වීමට, ඉහළ සිට පහළට ස්වයිප් කරන්න"</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"තේරුණා"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"සුරැකිණි"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"විසන්ධි කරන්න"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"සක්‍රිය කරන්න"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"හෙට ස්වයංක්‍රීයව නැවත ක්‍රියාත්මක කරන්න"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"ඉක්මන් බෙදා ගැනීම, මගේ උපාංගය සෙවීම, සහ උපාංග ස්ථානය වැනි විශේෂාංග බ්ලූටූත් භාවිත කරයි"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"බැටරිය <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ශ්‍රව්‍ය"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"හෙඩ්සෙටය"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"මෙම උපාංගය ඔබගේ මාපියන් විසින් කළමනාකරණය කෙරේ. ඔබ භාවිත කරන යෙදුම්, ඔබගේ ස්ථානය සහ ඔබගේ තිර කාලය වැනි තොරතුරු ඔබගේ මාපියන්ට බැලීමට සහ කළමනාකරණය කිරීමට හැකිය."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent මඟින් අඟුලු දමා තබා ගන්න"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"සොරකම් ආරක්ෂණය\nඋපාංගය අගුළු දමා ඇත, අගුළු හැරීමේ උත්සාහයන් වැඩියි"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"ශබ්ද සැකසීම්"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"මාධ්‍ය ස්වයංක්‍රීයව සිරස්තල"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"අබල කරන්න"</string>
<string name="sound_settings" msgid="8874581353127418308">"ශබ්ද සහ කම්පනය"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"සැකසීම්"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"සජීවී සිරස්තල"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"හඬ පරිමාව සුරක්ෂිත මට්ටමට අඩු කරන ලදි"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"නිර්දේශිත ප්‍රමාණයට වඩා වැඩි කාලයක් හෙඩ්ෆෝන් හඬ පරිමාව ඉහළ මට්ටමක පවතී"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"හෙඩ්ෆෝන් හඬ පරිමාව මෙම සතිය සඳහා සුරක්ෂිත සීමාව ඉක්මවා ඇත"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"කම්පනය"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"හඬ පරිමා පාලන %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"ඇමතුම් සහ දැනුම්දීම් (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>) නාද කරනු ඇත"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> වාදනය කරන්නේ"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"ශ්‍රව්‍ය වාදනය වනු ඇත්තේ"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"පද්ධති UI සුසරකය"</string>
<string name="status_bar" msgid="4357390266055077437">"තත්ත්ව තීරුව"</string>
<string name="demo_mode" msgid="263484519766901593">"පද්ධති UI ආදර්ශන ප්‍රකාරය"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> ට"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> දී"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"හොට්ස්පොට්"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"චන්ද්‍රිකාව, සම්බන්ධයක් නැත"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"චන්ද්‍රිකාව, දුර්වල සම්බන්ධතාවය"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"චන්ද්‍රිකාව, හොඳ සම්බන්ධතාවයක්"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"චන්ද්‍රිකාව, සම්බන්ධතාවය තිබේ"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"කාර්යාල පැතිකඩ"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"සමහරක් දේවල් වලට විනෝදයි, නමුත් සියල්ලටම නොවේ"</string>
<string name="tuner_warning" msgid="1861736288458481650">"පද්ධති UI සුසරකය ඔබට Android පරිශීලක අතුරු මුහුණත වෙනස් කිරීමට හෝ අභිරුචිකරණය කිරීමට අමතර ක්‍රම ලබා දේ. මෙම පර්යේෂණාත්මක අංග ඉදිරි නිකුත් වීම් වල වෙනස් වීමට, වැඩ නොකිරීමට, හෝ නැතිවීමට හැක. ප්‍රවේශමෙන් ඉදිරියට යන්න."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"සැකසීම් විවෘත කරන්න"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"සහායක විවෘත කරන්න"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"තිරය අගුළු දමන්න"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"සටහන් විවෘත කරන්න"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"සටහනක් ගන්න"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"පද්ධති බහු කාර්ය"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"RHS වෙත වත්මන් යෙදුම සමග බෙදුම් තිරයට ඇතුළු වන්න"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"LHS වෙත වත්මන් යෙදුම සමග බෙදුම් තිරයට ඇතුළු වන්න"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 60d3200e497e..3e2b554f18b2 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Zobrazte klepnutím"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Pri ukladaní nahrávky obrazovky sa vyskytla chyba"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Pri spustení nahrávania obrazovky sa vyskytla chyba"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Nástroj na zaznamenávanie problémov"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Záznam problému sa spracúva"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Upozornenie na prebiehajúcu aktivitu týkajúcu sa relácie zhromažďovania problémov"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Problém sa zaznamenáva"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Zdieľať"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Záznam problému bol uložený"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Zobrazíte ho klepnutím"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Pri ukladaní záznamu problému sa vyskytla chyba"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Pri spúšťaní záznamu problému sa vyskytla chyba"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Zobrazenie na celú obrazovku"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Ukončíte potiahnutím zhora nadol."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Dobre"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Uložené"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"odpojiť"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivovať"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automaticky zajtra znova zapnúť"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Funkcie, ako sú Quick Share, Nájdi moje zariadenie a poloha zariadenia, používajú Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batéria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Náhlavná súprava"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Toto zariadenie spravuje tvoj rodič. Vidí a môže spravovať informácie, napríklad aplikácie, ktoré používaš, tvoju polohu a čas používania."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Odomknutie udržiava TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Ochrana pred krádež.\nZar. uzamknuté, priveľa pokusov o odomk."</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Nastavenia zvuku"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Automatické titulkovanie médií"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"zakázať"</string>
<string name="sound_settings" msgid="8874581353127418308">"Zvuk a vibrácie"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Nastavenia"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Živý prepis"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Hlasitosť bola znížená na bezpečnejšiu úroveň"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Hlasitosť slúchadiel bola vysoká dlhšie, ako sa odporúča"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Hlasitosť slúchadiel prekročila bezpečný limit pre tento týždeň"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"zapnite vibrovanie"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Ovládacie prvky hlasitosti %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Hovory a upozornenia spustia zvonenie (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> sa prehráva v zar."</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Zvuk prehrá zariad.:"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Tuner používateľského rozhrania systému"</string>
<string name="status_bar" msgid="4357390266055077437">"Stavový riadok"</string>
<string name="demo_mode" msgid="263484519766901593">"Ukážka používateľského rozhrania systému"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"o <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Hotspot"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Satelit, bez pripojenia"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Satelit, slabá kvalita pripojenia"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, dobrá kvalita pripojenia"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, pripojenie je k dispozícii"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Pracovný profil"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Pri používaní tuneru postupujte opatrne"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Tuner používateľského rozhrania systému poskytujte ďalšie spôsoby ladenia a prispôsobenia používateľského rozhrania Android. Tieto experimentálne funkcie sa môžu v budúcich verziách zmeniť, ich poskytovanie môže byť prerušené alebo môžu byť odstránené. Pokračujte opatrne."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Otvorenie nastavení"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Otvorenie Asistenta"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Zamknúť obrazovku"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Otvorenie poznámok"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Napísanie poznámky"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Multitasking systému"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Rozdelenie obrazovky s aktuálnou aplikáciou vpravo"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Rozdelenie obrazovky s aktuálnou aplikáciou vľavo"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 29f023f4844c..1ea97a1f4b43 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Dotaknite se za ogled."</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Napaka pri shranjevanju posnetka zaslona"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Napaka pri začenjanju snemanja zaslona"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Snemalnik težav"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Obdelovanje posnetka težave"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Obvestilo o aktivni dejavnosti za sejo zbiranja težav"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Snemanje težave"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Deli"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Posnetek težave je shranjen"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Dotaknite se za ogled"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Napaka pri shranjevanju posnetka težave"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Napaka pri zagonu snemanja težave"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Vklopljen je celozaslonski način."</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Zaprete ga tako, da z vrha s prstom povlečete navzdol."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Razumem"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Shranjeno"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"prekinitev povezave"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiviranje"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Samodejno znova vklopi jutri"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Funkcije, kot sta Hitro deljenje in Poišči mojo napravo, ter lokacija naprave, uporabljajo Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterija na <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvok"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalke z mikrofonom"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"To napravo upravlja tvoj starš. Lahko si ogleda in upravlja podatke, na primer katere aplikacije uporabljaš, tvojo lokacijo in koliko časa uporabljaš napravo."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent ohranja odklenjeno"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Zaščita pred krajo\nNaprava je zaklenjena, preveč poskusov odklepanja"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Nastavitve zvoka"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Sam. podnapisi predstavnosti"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"onemogoči"</string>
<string name="sound_settings" msgid="8874581353127418308">"Zvok in vibriranje"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Nastavitve"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Samodejni podnapisi"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Glasnost je bila zmanjšana na varnejšo raven"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Glasnost v slušalkah je bila visoka dalj časa, kot je priporočeno."</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Visoka glasnost v slušalkah je presegla varno omejitev za ta teden"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibriranje"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Kontrolniki glasnosti za %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Klici in obvestila bodo pozvonili (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Predvajanje »<xliff:g id="LABEL">%s</xliff:g>« v"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Zvok bo predvajan v"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Uglaševalnik uporabniškega vmesnika sistema"</string>
<string name="status_bar" msgid="4357390266055077437">"Vrstica stanja"</string>
<string name="demo_mode" msgid="263484519766901593">"Predstavitveni način uporabniškega vmesnika sistema"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"ob <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"ob tem času: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Dostopna točka"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Satelit, ni povezave"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Satelit, slaba povezava"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, dobra povezava"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, povezava je na voljo"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Delovni profil"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Zabavno za nekatere, a ne za vse"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Uglaševalnik uporabniškega vmesnika sistema vam omogoča dodatne načine za spreminjanje in prilagajanje uporabniškega vmesnika Android. Te poskusne funkcije lahko v prihodnjih izdajah kadar koli izginejo, se spremenijo ali pokvarijo. Bodite previdni."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Odpiranje nastavitev"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Odpiranje Pomočnika"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Zaklepanje zaslona"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Odpiranje zapiskov"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Ustvarjanje zapiska"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Sistemska večopravilnost"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Vklop razdeljenega zaslona s trenutno aplikacijo na desni"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Vklop razdeljenega zaslona s trenutno aplikacijo na levi"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index c8f6f0eedd6b..c3ccd81e6f62 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Trokit për të parë"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Gabim gjatë ruajtjes së regjistrimit të ekranit"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Gabim gjatë nisjes së regjistrimit të ekranit"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Regjistruesi i problemeve"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Regjistrimi i problemeve me përpunimin"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Njoftim në vazhdim për një seancë për mbledhjen e problemeve"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Problemi po regjistrohet"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Ndaj"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Regjistrimi i problemit u ruajt"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Trokit për të parë"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Gabim gjatë ruajtjes së regjistrimit të problemit"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Gabim gjatë nisjes së regjistrimit të problemit"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Po shikon ekranin e plotë"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Për të dalë, rrëshqit shpejt poshtë."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"E kuptova"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Ruajtur"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"shkëput"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivizo"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktivizoje automatikisht nesër"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Veçoritë si \"Ndarja e shpejtë\", \"Gjej pajisjen time\" dhe vendndodhja e pajisjes përdorin Bluetooth-in"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> bateri"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Kufje me mikrofon"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Kjo pajisje menaxhohet nga prindi yt. Prindi yt mund të shikojë dhe menaxhojë informacionet, si p.sh. aplikacionet që përdor, vendndodhjen tënde dhe kohën para ekranit."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Mbajtur shkyçur nga TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Mbrojtje nga vjedhja\nPajisja u kyç. Shumë përpjekje shkyçjeje"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Cilësimet e zërit"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Media me titra automatike"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"çaktivizo"</string>
<string name="sound_settings" msgid="8874581353127418308">"Tingulli dhe dridhjet"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Cilësimet"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Titrat në çast"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Volumi është ulur në një nivel më të sigurt"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Volumi i kufjeve ka qenë i lartë për një kohë më të gjatë nga sa rekomandohet"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Volumi i kufjeve ka tejkaluar kufirin e sigurisë për këtë javë"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"lësho dridhje"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Kontrollet e volumit %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Do të bjerë zilja për telefonatat dhe njoftimet (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Po luhet <xliff:g id="LABEL">%s</xliff:g> në"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Do të luhet audio në"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Sintonizuesi i Sistemit të Ndërfaqes së Përdoruesit"</string>
<string name="status_bar" msgid="4357390266055077437">"Shiriti i statusit"</string>
<string name="demo_mode" msgid="263484519766901593">"Modaliteti i demonstrimit i ndërfaqes së përdoruesit të sistemit"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"në <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"në <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Zona e qasjes për internet"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Sateliti. Nuk ka lidhje"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Sateliti. Lidhje e dobët"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Sateliti. Lidhje e mirë"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Sateliti. Ofrohet lidhje"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Profili i punës"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Argëtim për disa, por jo për të gjithë!"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Sintonizuesi i Sistemit të Ndërfaqes së Përdoruesit të jep mënyra shtesë për të tërhequr dhe personalizuar ndërfaqen Android të përdoruesit. Këto funksione eksperimentale mund të ndryshojnë, prishen ose zhduken në versionet e ardhshme. Vazhdo me kujdes."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Hap cilësimet"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Hap \"Asistentin\""</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Ekrani i kyçjes"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Hap \"Shënimet\""</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Mbaj një shënim"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Kryerja e shumë detyrave nga sistemi"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Hyr në ekranin e ndarë me aplikacionin aktual në anën e djathtë"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Hyr në ekranin e ndarë me aplikacionin aktual në anën e majtë"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 26548721881f..2309d7de205c 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Додирните да бисте прегледали"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Грешка при чувању снимка екрана"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Грешка при покретању снимања екрана"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Снимач проблема"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Обрађује се снимак проблема"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"обавештење о активности у току за сесију прикупљања података о проблему"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Снимамо проблем"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Дели"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Снимак проблема је сачуван"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Додирните да бисте прегледали"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Грешка при чувању снимка проблема"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Грешка при покретању снимања проблема"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Приказује се цео екран"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Да бисте изашли, превуците надоле одозго."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Важи"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Сачувано"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"прекините везу"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"активирајте"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Аутоматски поново укључи сутра"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Функције као што су Quick Share, Пронађи мој уређај и локација уређаја користе Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Ниво батерије је <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалице"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Овим уређајем управља родитељ. Родитељ може да види информације, као што су апликације које користиш, твоју локацију и време испред екрана, и да управља њима."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Поуздани агент спречава закључавање"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Заштита од крађе\nЗакљ. уређај, превише покушаја откључавања"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Подешавања звука"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Аутоматски титл за медије"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"онемогућите"</string>
<string name="sound_settings" msgid="8874581353127418308">"Звук и вибрирање"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Подешавања"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Титл уживо"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Звук је смањен на безбедну јачину"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Звук у слушалицама је био гласан дуже него што се препоручује"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Јачина звука у слушалицама је премашила безбедносно ограничење за ову недељу"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"вибрација"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Контроле за јачину звука за %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Мелодија звона за позиве и обавештења је укључена (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> се пушта на"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Звук се пушта на"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Тјунер за кориснички интерфејс система"</string>
<string name="status_bar" msgid="4357390266055077437">"Статусна трака"</string>
<string name="demo_mode" msgid="263484519766901593">"Режим демонстрације за кориснички интерфејс система"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"у <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"у <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Хотспот"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Сателит, нисте повезани"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Сателит, веза је лоша"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Сателит, веза је добра"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Сателит, веза је доступна"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Пословни профил"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Забава за неке, али не за све"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Тјунер за кориснички интерфејс система вам пружа додатне начине за подешавање и прилагођавање Android корисничког интерфејса. Ове експерименталне функције могу да се промене, откажу или нестану у будућим издањима. Будите опрезни."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Отвори подешавања"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Отвори помоћника"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Закључавање екрана"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Отвори белешке"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Направите белешку"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Обављање више задатака система истовремено"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Покрени подељени екран за актуелну апликацију на десној страни"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Покрени подељени екран за актуелну апликацију на левој страни"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 34078dac243d..be80f7aefd2d 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Tryck för att visa"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Det gick inte att spara skärminspelningen"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Det gick inte att starta skärminspelningen"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Probleminspelare"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Behandlar probleminspelning"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Avisering om pågående probleminsamlingssession"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Spelar in problem"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Dela"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Probleminspelning har sparats"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Visa genom att trycka här"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Det gick inte att spara probleminspelning"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Det gick inte att starta probleminspelning"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Visar på fullskärm"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Svep nedåt från skärmens överkant för att avsluta."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"OK"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Sparad"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"koppla från"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivera"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktivera automatiskt igen i morgon"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Funktioner som Snabbdelning, Hitta min enhet och enhetens plats använder Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ljud"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Den här enheten hanteras av din förälder. Föräldern kan se och hantera information som vilka appar du använder, din plats och din skärmtid."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Hålls olåst med TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Stöldskydd\nEnheten har låsts på grund av för många försök"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Ljudinställningar"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Texta media automatiskt"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"inaktivera"</string>
<string name="sound_settings" msgid="8874581353127418308">"Ljud och vibration"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Inställningar"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Live Caption"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Volymen har sänkts till en säkrare nivå"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Volymen i hörlurarna har varit hög längre än vad som rekommenderas"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Volymen i hörlurarna har överskridit den säkra gränsen för veckan"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibration"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Volymkontroller för %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Ringsignal används för samtal och aviseringar (volym: <xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Spelar upp <xliff:g id="LABEL">%s</xliff:g> på"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Ljud spelas upp på"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Inställningar för systemgränssnitt"</string>
<string name="status_bar" msgid="4357390266055077437">"Statusfält"</string>
<string name="demo_mode" msgid="263484519766901593">"Demoläge för systemgränssnitt"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"kl. <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"kl. <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Surfzon"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Satellit, ingen anslutning"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Satellit, dålig anslutning"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellit, bra anslutning"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellit, anslutning tillgänglig"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Jobbprofil"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Kul för vissa, inte för alla"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Du kan använda inställningarna för systemgränssnitt för att justera användargränssnittet i Android. Dessa experimentfunktioner kan när som helst ändras, sluta fungera eller försvinna. Använd med försiktighet."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Öppna inställningarna"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Öppna assistenten"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Lås skärmen"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Öppna anteckningar"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Anteckna"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Systemets multikörning"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Öppna delad skärm med aktuell app till höger"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Öppna delad skärm med aktuell app till vänster"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 74b6c8842b47..09998200c0f4 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Gusa ili uangalie"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Hitilafu imetokea wakati wa kuhifadhi rekodi ya skrini"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Hitilafu imetokea wakati wa kuanza kurekodi skrini"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Kifaa cha Kurekodi Hitilafu"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Mchakato wa kurekodi hitilafu unaendelea"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Arifa inayoendelea kuhusu kipindi cha ukusanyaji wa data ya hitilafu"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Inarekodi hitilafu"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Tuma"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Imehifadhi rekodi ya hitilafu"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Gusa ili uangalie"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Hitilafu imetokea wakati wa kuhifadhi rekodi ya hitilafu"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Hitilafu imetokea wakati wa kuanza kurekodi hitilafu"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Unatazama kwenye skrini nzima"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Ili uondoke, telezesha kidole kutoka juu hadi chini."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Nimeelewa"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Imehifadhiwa"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ondoa"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"anza kutumia"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Iwashe tena kesho kiotomatiki"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Vipengele kama vile Kutuma Haraka, Tafuta Kifaa Changu na mahali kifaa kilipo hutumia Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Chaji ya betri ni <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Sauti"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Vifaa vya sauti"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Kifaa hiki kinadhibitiwa na mzazi wako. Mzazi wako anaweza kuona na kudhibiti maelezo kama vile programu unazotumia, mahali ulipo na muda unaotumia kwenye kifaa."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Imefunguliwa na kipengele cha kutathmini hali ya kuaminika"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Kifaa cha ulinzi\ndhidi ya wizi kimefungwa, kuna majaribio mengi mno ya kukifungua"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Mipangilio ya sauti"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Wekea maudhui manukuu kiotomatiki"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"zima"</string>
<string name="sound_settings" msgid="8874581353127418308">"Sauti na mtetemo"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Mipangilio"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Manukuu Papo Hapo"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Kiwango cha sauti kimepunguzwa hadi kiwango salama"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Kiwango cha sauti ya vipokea sauti vya kichwani kimekuwa juu kwa muda mrefu kuliko inavyopendekezwa"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Kiwango cha sauti ya vipokea sauti vya kichwani kimezidi kikomo cha kiwango salama kwa wiki hii"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"tetema"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Vidhibiti %s vya sauti"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Itatoa mlio arifa ikitumwa na simu ikipigwa (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Inacheza <xliff:g id="LABEL">%s</xliff:g> kwenye"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Sauti itacheza kwenye"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Kirekebishi cha kiolesura cha mfumo"</string>
<string name="status_bar" msgid="4357390266055077437">"Sehemu ya kuonyesha hali"</string>
<string name="demo_mode" msgid="263484519766901593">"Hali ya onyesho la kirekebishi cha kiolesura cha mfumo"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"saa <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"siku ya <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Mtandaopepe"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Setilaiti, hakuna muunganisho"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Setilaiti, muunganisho hafifu"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Setilaiti, muunganisho thabiti"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Setilaiti, muunganisho unapatikana"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Wasifu wa kazini"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Kinafurahisha kwa baadhi ya watu lakini si wote"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Kirekebishi cha kiolesura cha mfumo kinakupa njia zaidi za kugeuza na kubadilisha kiolesura cha Android ili kikufae. Vipengele hivi vya majaribio vinaweza kubadilika, kuharibika au kupotea katika matoleo ya siku zijazo. Endelea kwa uangalifu."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Fungua mipangilio"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Fungua programu ya Mratibu wa Google"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Funga skrini"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Fungua madokezo"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Andika dokezo"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Majukumu mengi ya mfumo"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Tumia programu kwenye skrini iliyogawanywa upande wa kulia"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Tumia programu kwenye skrini iliyogawanywa upande wa kushoto"</string>
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index f1017d8fe36e..b4383156dc71 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -53,4 +53,7 @@
<item>bottom_start:home</item>
<item>bottom_end:create_note</item>
</string-array>
+
+ <!-- Whether volume panel should use the large screen layout or not -->
+ <bool name="volume_panel_is_large_screen">true</bool>
</resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index cbf7f77f721f..486d7f97f16e 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"பார்க்கத் தட்டவும்"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"ஸ்கிரீன் ரெக்கார்டிங்கைச் சேமிப்பதில் பிழை ஏற்பட்டது"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"ஸ்கிரீன் ரெக்கார்டிங்கைத் தொடங்குவதில் பிழை"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"சிக்கல் ரெக்கார்டர்"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"சிக்கல் ரெக்கார்டிங்கைச் செயலாக்குகிறது"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"சிக்கல் சேகரிப்பு அமர்வுக்கான பின்னணிச் செயல்பாட்டின் அறிவிப்பு"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"சிக்கல் ரெக்கார்டு செய்யப்படுகிறது"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"பகிர்"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"சிக்கல் தொடர்பான ரெக்கார்டிங் சேமிக்கப்பட்டது"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"பார்க்க தட்டவும்"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"சிக்கல் தொடர்பான ரெக்கார்டிங்கைச் சேமிப்பதில் பிழை ஏற்பட்டது"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"சிக்கலை ரெக்கார்டிங் செய்யத் தொடங்குவதில் பிழை ஏற்பட்டது"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"முழுத் திரையில் காட்டுகிறது"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"வெளியேற, மேலிருந்து கீழே ஸ்வைப் செய்யவும்."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"சரி"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"சேமிக்கப்பட்டது"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"இணைப்பு நீக்கும்"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"செயல்படுத்தும்"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"நாளைக்குத் தானாகவே மீண்டும் இயக்கப்படும்"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"விரைவுப் பகிர்தல், Find My Device போன்ற அம்சங்களும் சாதன இருப்பிடமும் புளூடூத்தைப் பயன்படுத்துகின்றன"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> பேட்டரி"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ஆடியோ"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ஹெட்செட்"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"இந்தச் சாதனம் உங்கள் பெற்றோரால் நிர்வகிக்கப்படுகிறது. நீங்கள் பயன்படுத்தும் ஆப்ஸ், இருப்பிடம், பயன்படுத்திய நேரம் ஆகியவற்றைப் பார்க்கவும் நிர்வகிக்கவும் உங்கள் பெற்றோரால் முடியும்."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent இதைத் திறந்தே வைத்துள்ளது"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"திருட்டைத் தடுக்க\nசாதனம் பூட்டப்பட்டது, அதிகமான அன்லாக் முயற்சிகள்"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"ஒலி அமைப்புகள்"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"வசன உரைகளைத் தானாக எழுதும்"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"முடக்கும்"</string>
<string name="sound_settings" msgid="8874581353127418308">"ஒலி &amp; அதிர்வு"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"அமைப்புகள்"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"உடனடி வசனம்"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"பாதுகாப்பான நிலைக்கு ஒலியளவு குறைக்கப்பட்டது"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"ஹெட்ஃபோன் ஒலியளவு பரிந்துரைக்கப்பட்டதைவிட அதிகளவில் நீண்ட நேரமாக உள்ளது"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"இந்த வாரம் ஹெட்ஃபோன் ஒலியளவு பாதுகாப்பு வரம்பைக் கடந்துவிட்டது"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"அதிர்வுறும்"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s ஒலியளவுக் கட்டுப்பாடுகள்"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"அழைப்புகளும் அறிவிப்புகளும் வரும்போது ஒலிக்கச் செய்யும் (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"இதில் <xliff:g id="LABEL">%s</xliff:g> பிளே ஆகிறது"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"இல் ஆடியோ பிளே ஆகும்"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"நிலைப் பட்டி"</string>
<string name="demo_mode" msgid="263484519766901593">"சிஸ்டம் பயனர் இடைமுக டெமோ பயன்முறை"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> மணிக்கு"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> மணிக்கு"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"ஹாட்ஸ்பாட்"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"சாட்டிலைட், இணைப்பு இல்லை"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"சாட்டிலைட், மோசமான இணைப்பு"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"சாட்டிலைட், நிலையான இணைப்பு"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"சாட்டிலைட், இணைப்பு கிடைக்கிறது"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"பணிக் கணக்கு"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"சில வேடிக்கையாக இருந்தாலும் கவனம் தேவை"</string>
<string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner, Android பயனர் இடைமுகத்தை மாற்றவும் தனிப்பயனாக்கவும் கூடுதல் வழிகளை வழங்குகிறது. இந்தப் பரிசோதனைக்குரிய அம்சங்கள் எதிர்கால வெளியீடுகளில் மாற்றப்படலாம், இடைநிறுத்தப்படலாம் அல்லது தோன்றாமல் போகலாம். கவனத்துடன் தொடரவும்."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"அமைப்புகளைத் திறத்தல்"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Assistantடைத் திறத்தல்"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"பூட்டுத் திரை"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"குறிப்புகளைத் திறத்தல்"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"குறிப்பெடுத்தல்"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"சிஸ்டம் பல வேலைகளைச் செய்தல்"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"வலதுபுறத்தில் தற்போதைய ஆப்ஸ் தோன்றுமாறு திரைப் பிரிப்பை அமைத்தல்"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"இடதுபுறத்தில் தற்போதைய ஆப்ஸ் தோன்றுமாறு திரைப் பிரிப்பை அமைத்தல்"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 5234125d2183..227ba5a2ea4a 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"చూడటానికి ట్యాప్ చేయండి"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"స్క్రీన్ రికార్డింగ్‌ను సేవ్ చేయడంలో ఎర్రర్ ఏర్పడింది"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"స్క్రీన్ రికార్డింగ్ ప్రారంభించడంలో ఎర్రర్ ఏర్పడింది"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"సమస్య రికార్డర్"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"సమస్య రికార్డింగ్ ప్రాసెసింగ్"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"సమస్య సేకరణ సెషన్ కోసం కొనసాగుతోన్న నోటిఫికేషన్"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"సమస్యను రికార్డ్ చేస్తోంది"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"షేర్ చేయండి"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"రికార్డింగ్ సంబంధిత సమస్య సేవ్ చేయబడింది"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"చూడటానికి ట్యాప్ చేయండి"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"సమస్య రికార్డింగ్‌ను సేవ్ చేయడంలో ఎర్రర్ ఏర్పడింది"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"సమస్య రికార్డింగ్‌ను ప్రారంభించడంలో ఎర్రర్ ఏర్పడింది"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"ఫుల్ స్క్రీన్‌లో చూస్తున్నారు"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"నిష్క్రమించడానికి, పై నుండి కిందికి స్వైప్ చేయండి."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"సరే"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"సేవ్ చేయబడింది"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"డిస్‌కనెక్ట్ చేయండి"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"యాక్టివేట్ చేయండి"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"రేపు మళ్లీ ఆటోమేటిక్‌గా ఆన్ చేస్తుంది"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"క్విక్ షేర్, Find My Device, పరికర లొకేషన్ వంటి ఫీచర్‌లు బ్లూటూత్‌ను ఉపయోగిస్తాయి"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> బ్యాటరీ"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ఆడియో"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"హెడ్‌సెట్"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"ఈ పరికరాన్ని మీ తల్లి/తండ్రి మేనేజ్ చేస్తున్నారు. మీ తల్లి/తండ్రి, మీరు ఉపయోగించే యాప్‌లు, మీ లొకేషన్, అలాగే మీ పరికర వినియోగ వ్యవధి వంటి సమాచారాన్ని చూడగలరు, మేనేజ్ చేయగలరు."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent ద్వారా అన్‌లాక్ చేయబడింది"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"దొంగతనం చేయడం నుండి రక్షణ\nపరికరం లాక్ చేయబడింది, చాలా ఎక్కువ అన్‌లాక్ ప్రయత్నాలు జరిగాయి"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"ధ్వని సెట్టింగ్‌లు"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"మీడియాకు ఆటోమేటిక్ క్యాప్షన్‌లు"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"నిలిపివేయండి"</string>
<string name="sound_settings" msgid="8874581353127418308">"సౌండ్ &amp; వైబ్రేషన్"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"సెట్టింగ్‌లు"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"లైవ్ క్యాప్షన్"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"వాల్యూమ్‌ను సురక్షిత స్థాయికి తగ్గించడం జరిగింది"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"హెడ్‌ఫోన్ వాల్యూమ్, సిఫార్సు చేసిన సమయం కంటే ఎక్కువసేపు అధిక వాల్యూమ్‌లో ఉంది"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"హెడ్‌ఫోన్ వాల్యూమ్ ఈ వారం సురక్షిత పరిమితిని మించిపోయింది"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"వైబ్రేట్"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s వాల్యూమ్ నియంత్రణలు"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"కాల్స్‌ మరియు నోటిఫికేషన్‌లు రింగ్ అవుతాయి (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g>‌లో ప్లే అవుతోంది"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"ఆడియో ప్లే అవుతుంది"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"సిస్టమ్ UI ట్యూనర్"</string>
<string name="status_bar" msgid="4357390266055077437">"స్టేటస్‌ బార్‌"</string>
<string name="demo_mode" msgid="263484519766901593">"సిస్టమ్ UI డెమో మోడ్"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>కి"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>కి"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"హాట్‌స్పాట్"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"శాటిలైట్, కనెక్షన్ లేదు"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"శాటిలైట్, కనెక్షన్ సరిగా లేదు"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"శాటిలైట్, కనెక్షన్ బాగుంది"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"శాటిలైట్, కనెక్షన్ అందుబాటులో ఉంది"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"ఆఫీస్ ప్రొఫైల్‌"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"కొందరికి సరదాగా ఉంటుంది కానీ అందరికీ అలాగే ఉండదు"</string>
<string name="tuner_warning" msgid="1861736288458481650">"సిస్టమ్ UI ట్యూనర్ Android వినియోగదారు ఇంటర్‌ఫేస్‌ను మెరుగుపరచడానికి మరియు అనుకూలంగా మార్చడానికి మీకు మరిన్ని మార్గాలను అందిస్తుంది. ఈ ప్రయోగాత్మక లక్షణాలు భవిష్యత్తు విడుదలల్లో మార్పుకు లోనవ్వచ్చు, తాత్కాలికంగా లేదా పూర్తిగా నిలిపివేయవచ్చు. జాగ్రత్తగా కొనసాగండి."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"సెట్టింగ్‌లను తెరవండి"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"అసిస్టెంట్‌ను తెరవండి"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"లాక్ స్క్రీన్"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"నోట్స్‌ను తెరవండి"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"గమనికను రాయండి"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"సిస్టమ్ మల్టీ-టాస్కింగ్"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"RHSకు ప్రస్తుత యాప్‌తో స్ప్లిట్ స్క్రీన్‌ను ఎంటర్ చేయండి"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"LHSకు ప్రస్తుత యాప్‌తో స్ప్లిట్ స్క్రీన్‌ను ఎంటర్ చేయండి"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 9316f1687eec..d1e186fa73b4 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"แตะเพื่อดู"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"เกิดข้อผิดพลาดในการบันทึกหน้าจอ"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"เกิดข้อผิดพลาดขณะเริ่มบันทึกหน้าจอ"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"โปรแกรมบันทึกปัญหา"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"กำลังประมวลผลการบันทึกปัญหา"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"การแจ้งเตือนต่อเนื่องสำหรับเซสชันการรวบรวมปัญหา"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"กำลังบันทึกปัญหา"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"แชร์"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"บันทึกไฟล์บันทึกปัญหาแล้ว"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"แตะเพื่อดู"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"เกิดข้อผิดพลาดในการบันทึกไฟล์บันทึกปัญหา"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"เกิดข้อผิดพลาดในการเริ่มบันทึกปัญหา"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"กำลังดูแบบเต็มหน้าจอ"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"หากต้องการออก ให้ปัดลงจากด้านบน"</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"รับทราบ"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"บันทึกแล้ว"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ยกเลิกการเชื่อมต่อ"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"เปิดใช้งาน"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"เปิดอีกครั้งโดยอัตโนมัติในวันพรุ่งนี้"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"ฟีเจอร์ต่างๆ เช่น Quick Share, หาอุปกรณ์ของฉัน และตำแหน่งของอุปกรณ์ ใช้บลูทูธ"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"แบตเตอรี่ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"เสียง"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ชุดหูฟัง"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"อุปกรณ์นี้จัดการโดยผู้ปกครอง ผู้ปกครองจะดูและจัดการข้อมูลต่างๆ ได้ เช่น แอปที่คุณใช้ ตำแหน่งของคุณ และเวลาอยู่หน้าจอ"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"ปลดล็อกไว้โดย TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"การป้องกันการโจรกรรม\nอุปกรณ์ถูกล็อก ลองปลดล็อกหลายครั้งเกินไป"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g> <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"การตั้งค่าเสียง"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"แสดงคำบรรยายสื่อโดยอัตโนมัติ"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"ปิดใช้"</string>
<string name="sound_settings" msgid="8874581353127418308">"เสียงและการสั่น"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"การตั้งค่า"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"คำบรรยายสด"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"ลดเสียงให้อยู่ในระดับที่ปลอดภัยยิ่งขึ้น"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"เสียงของหูฟังอยู่ในระดับที่ดังเป็นระยะเวลานานกว่าที่แนะนำ"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"เสียงของหูฟังอยู่ในระดับที่ดังเกินขีดจำกัดความปลอดภัยสำหรับสัปดาห์นี้"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"สั่น"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"ตัวควบคุมระดับเสียง %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"สายเรียกเข้าและการแจ้งเตือนจะส่งเสียง (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"กำลังเล่น <xliff:g id="LABEL">%s</xliff:g> ใน"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"เสียงจะเล่นต่อไป"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"ตัวรับสัญญาณ UI ระบบ"</string>
<string name="status_bar" msgid="4357390266055077437">"แถบสถานะ"</string>
<string name="demo_mode" msgid="263484519766901593">"โหมดสาธิต UI ของระบบ"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"เวลา <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"ในวันที่ <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"ฮอตสปอต"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"ดาวเทียม, ไม่มีการเชื่อมต่อ"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"ดาวเทียม, การเชื่อมต่อไม่ดี"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ดาวเทียม, การเชื่อมต่อดี"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ดาวเทียม, การเชื่อมต่อที่พร้อมใช้งาน"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"โปรไฟล์งาน"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"เพลิดเพลินกับบางส่วนแต่ไม่ใช่ทั้งหมด"</string>
<string name="tuner_warning" msgid="1861736288458481650">"ตัวรับสัญญาณ UI ระบบช่วยให้คุณมีวิธีพิเศษในการปรับแต่งและกำหนดค่าส่วนติดต่อผู้ใช้ Android ฟีเจอร์รุ่นทดลองเหล่านี้อาจมีการเปลี่ยนแปลง ขัดข้อง หรือหายไปในเวอร์ชันอนาคต โปรดดำเนินการด้วยความระมัดระวัง"</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"เปิดการตั้งค่า"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"เปิด Assistant"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"ล็อกหน้าจอ"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"เปิดโน้ต"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"จดโน้ต"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"การทํางานหลายอย่างพร้อมกันของระบบ"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"เข้าสู่โหมดแยกหน้าจอโดยแอปปัจจุบันอยู่ด้านขวา"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"เข้าสู่โหมดแยกหน้าจอโดยแอปปัจจุบันอยู่ด้านซ้าย"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 9c7b1bfc0f2d..c929e4579df9 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"I-tap para tingnan"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Nagka-error sa pag-save ng recording ng screen"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Nagkaroon ng error sa pagsisimula ng pag-record ng screen"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Recorder ng Isyu"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Pinoproseso: recording ng isyu"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Kasalukuyang notification para sa session ng pangongolekta ng isyu"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Isyu sa pag-record"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Ibahagi"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Na-save ang recording ng isyu"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"I-tap para tingnan"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Nagkaroon ng error sa pag-save ng recording ng isyu"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Nagkaroon ng error sa pagsisimula ng pag-record ng isyu"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Nanonood sa full screen"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Para lumabas, mag-swipe mula sa itaas pababa."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"OK"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Na-save"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"idiskonekta"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"i-activate"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Awtomatikong i-on ulit bukas"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Guamgamit ng Bluetooth ang mga feature tulad ng Quick Share, Hanapin ang Aking Device, at lokasyon ng device"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> na baterya"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Pinapamahalaan ng iyong magulang ang device na ito. Makikita at mapapamahalaan ng iyong magulang ang impormasyon tulad ng mga app na ginagamit mo, iyong lokasyon, at tagal ng paggamit mo sa device."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Pinanatiling naka-unlock ng TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Theft protection\nNa-lock ang device, sobrang pag-unlock"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Mga setting ng tunog"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"I-autocaption ang media"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"i-disable"</string>
<string name="sound_settings" msgid="8874581353127418308">"Tunog at pag-vibrate"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Mga Setting"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Instant Caption"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Ibinaba sa mas ligtas na level ang volume"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Naging malakas ang volume ng headphones nang mas matagal sa inirerekomenda"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Lampas na sa ligtas na limitasyon para sa linggong ito ang volume ng headphone"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"i-vibrate"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Mga kontrol ng volume ng %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Magri-ring kapag may mga tawag at notification (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Nagpe-play ang <xliff:g id="LABEL">%s</xliff:g> sa"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"I-play ang audio sa"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Tuner ng System UI"</string>
<string name="status_bar" msgid="4357390266055077437">"Status bar"</string>
<string name="demo_mode" msgid="263484519766901593">"Demo mode ng System UI"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"ng <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"sa <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Hotspot"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Satellite, walang koneksyon"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Satellite, mahina ang koneksyon"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellite, malakas ang koneksyon"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellite, may koneksyon"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Profile sa trabaho"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Masaya para sa ilan ngunit hindi para sa lahat"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Nagbibigay sa iyo ang Tuner ng System UI ng mga karagdagang paraan upang baguhin at i-customize ang user interface ng Android. Ang mga pang-eksperimentong feature na ito ay maaaring magbago, masira o mawala sa mga pagpapalabas sa hinaharap. Magpatuloy nang may pag-iingat."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Buksan ang mga setting"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Buksan ang assistant"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"I-lock ang screen"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Buksan ang mga tala"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Magtala"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Multitasking ng system"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Lumipat sa split screen nang nasa RHS ang kasalukuyang app"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Lumipat sa split screen nang nasa LHS ang kasalukuyang app"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 6ff03a965a58..9407fbbbf3d9 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Görüntülemek için dokunun"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Ekran kaydı saklanırken hata oluştu"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Ekran kaydı başlatılırken hata oluştu"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Sorun Kaydedici"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Sorun kaydı işleniyor"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Sorun toplama oturumuyla ilgili devam eden görev bildirimi"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Kayıt sorunu"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Paylaş"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Sorun kaydı saklandı"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Görüntülemek için dokunun"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Sorun kaydı saklanırken hata oluştu"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Sorun kaydı başlatılırken hata oluştu"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Tam ekran olarak görüntüleme"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Çıkmak için yukarıdan aşağıya doğru hızlıca kaydırın."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Anladım"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Kaydedildi"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"bağlantıyı kes"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"etkinleştir"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Yarın otomatik olarak tekrar aç"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Quick Share, Cihazımı Bul ve cihaz konumu gibi özellikler Bluetooth\'u kullanır"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Pil düzeyi <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ses"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Mikrofonlu kulaklık"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Bu cihaz ebeveyniniz tarafından yönetiliyor. Kullandığınız uygulamalar, konumunuz ve ekran başında kalma süreniz gibi bilgiler ebeveyniniz tarafından görülüp yönetilebilir."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent tarafından kilit açık tutuldu"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Hırsızlık koruması\nCihaz kilitlendi, çok fazla kilit açma denemesi"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Ses ayarları"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Otomatik medya altyazısı"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"devre dışı bırak"</string>
<string name="sound_settings" msgid="8874581353127418308">"Ses ve titreşim"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Ayarlar"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Canlı Altyazı"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Ses düzeyi daha güvenli bir düzeye indirildi"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Ses, önerilenden daha uzun süredir yüksek düzeydeydi"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Bu hafta kulaklığın ses düzeyi güvenli sınırı aştı"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"titreşim"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s ses denetimleri"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Aramalar ve bildirimler telefonun zilini çaldıracak (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> şurada çalacak:"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Ses şurada çalacak:"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Sistem Arayüzü Ayarlayıcısı"</string>
<string name="status_bar" msgid="4357390266055077437">"Durum çubuğu"</string>
<string name="demo_mode" msgid="263484519766901593">"Sistem kullanıcı arayüzü demo modu"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"saat: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"gün ve saat: <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Hotspot"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Uydu, bağlantı yok"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Uydu, bağlantı zayıf"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Uydu, bağlantı güçlü"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Uydu, bağlantı mevcut"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"İş profili"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Bazıları için eğlenceliyken diğerleri için olmayabilir"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Sistem Kullanıcı Arayüzü Ayarlayıcı, Android kullanıcı arayüzünde değişiklikler yapmanız ve arayüzü özelleştirmeniz için ekstra yollar sağlar. Bu deneysel özellikler değişebilir, bozulabilir veya gelecekteki sürümlerde yer almayabilir. Dikkatli bir şekilde devam edin."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Ayarları aç"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Asistan\'ı aç"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Kilit ekranı"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Notları aç"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Not al"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Sistem çoklu görevi"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Mevcut uygulamayı sağ tarafa alarak bölünmüş ekrana geç"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Mevcut uygulamayı sol tarafa alarak bölünmüş ekrana geç"</string>
@@ -963,7 +956,7 @@
<string name="accessibility_floating_button_undo" msgid="511112888715708241">"Geri al"</string>
<string name="accessibility_floating_button_hidden_notification_title" msgid="4115036997406994799">"Erişilebilirlik düğmesi gizlendi"</string>
<string name="accessibility_floating_button_hidden_notification_text" msgid="1457021647040915658">"Erişilebilirlik düğmesini göstermek için dokunun"</string>
- <string name="accessibility_floating_button_undo_message_label_text" msgid="9017658016426242640">"<xliff:g id="FEATURE_NAME">%s</xliff:g> kısayol kaldırıldı"</string>
+ <string name="accessibility_floating_button_undo_message_label_text" msgid="9017658016426242640">"<xliff:g id="FEATURE_NAME">%s</xliff:g> kısayolu kaldırıldı"</string>
<string name="accessibility_floating_button_undo_message_number_text" msgid="4909270290725226075">"{count,plural, =1{# kısayol kaldırıldı}other{# kısayol kaldırıldı}}"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Sol üste taşı"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Sağ üste taşı"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 1bc422eb1658..b7a4999865de 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Натисніть, щоб переглянути"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Не вдалося зберегти запис відео з екрана"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Не вдалося почати запис екрана"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Засіб запису проблем"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Обробка запису проблеми"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Поточне сповіщення про сеанс збирання даних про проблему"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Триває запис проблеми"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Поділитися"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Запис проблеми збережено"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Натисніть, щоб переглянути"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Не вдалося зберегти запис проблеми"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Не вдалося почати запис проблеми"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Перегляд на весь екран"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Щоб вийти, проведіть пальцем униз від верху екрана."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"OK"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Збережено"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"від’єднати"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"активувати"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Автоматично ввімкнути знову завтра"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Такі функції, як швидкий обмін, \"Знайти пристрій\" і визначення місцезнаходження пристрою, використовують Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> заряду акумулятора"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудіопристрій"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнітура"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Цим пристроєм керують твої батьки. Вони можуть бачити та контролювати, якими додатками ти користуєшся, де перебуваєш і скільки часу проводиш за пристроєм."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Розблоковує довірчий агент"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Захист від крадіжки\nПристрій заблоковано (забагато спроб)"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Налаштування звуку"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Автоматичні субтитри (медіа)"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"вимкнути"</string>
<string name="sound_settings" msgid="8874581353127418308">"Звук і вібрація"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Налаштування"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Живі субтитри"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Гучність знижено до безпечнішого рівня"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Аудіо в навушниках відтворювалося з високою гучністю довше, ніж рекомендується"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Гучність навушників перевищила безпечний рівень, допустимий протягом тижня"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"увімкнути вібросигнал"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Регуляторів гучності: %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Для викликів і сповіщень налаштовано звуковий сигнал (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Відтворюється <xliff:g id="LABEL">%s</xliff:g> на:"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Аудіо гратиме на:"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Рядок стану"</string>
<string name="demo_mode" msgid="263484519766901593">"Демо-режим інтерфейсу системи"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Точка доступу"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Немає з’єднання із супутником"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Погане з’єднання із супутником"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Хороше з’єднання із супутником"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Доступне з’єднання із супутником"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Робочий профіль"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Це цікаво, але будьте обачні"</string>
<string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner пропонує нові способи налаштувати та персоналізувати інтерфейс користувача Android. Ці експериментальні функції можуть змінюватися, не працювати чи зникати в майбутніх версіях. Будьте обачні."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Відкрити налаштування"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Відкрити додаток Асистент"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Заблокувати екран"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Відкрити нотатки"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Створити нотатку"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Багатозадачність системи"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Розділити екран із поточним додатком праворуч"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Розділити екран із поточним додатком ліворуч"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 18a90dd6b90e..f9155a0e06e6 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"دیکھنے کے لیے تھپتھپائیں"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"اسکرین ریکارڈنگ محفوظ کرنے میں خرابی"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"اسکرین ریکارڈنگ شروع کرنے میں خرابی"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"ایشو ریکارڈر"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"ایشو ریکارڈنگ پروسیس ہو رہی ہے"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"ایشو کلیکشن سیشن کے لیے جاری اطلاع"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"ریکارڈنگ ایشو"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"اشتراک کریں"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"ایشو ریکارڈنگ محفوظ ہو گئی"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"دیکھنے کیلئے تھپتھپائیں"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"ایشو ریکارڈنگ محفوظ کرنے میں خرابی"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"ایشو ریکارڈنگ شروع کرنے میں خرابی"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"فُل اسکرین میں دیکھ رہے ہیں"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"باہر نکلنے کیلئے اوپر سے نیچے کی طرف سوائپ کریں۔"</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"سمجھ آ گئی"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"محفوظ ہے"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"غیر منسلک کریں"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"فعال کریں"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"کل دوبارہ خودکار طور پر آن ہوگا"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"فوری اشتراک، میرا آلہ ڈھونڈیں، اور آلہ کے مقام جیسی خصوصیات بلوٹوتھ کا استعمال کرتی ہیں"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> بیٹری"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"آڈیو"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ہیڈ سیٹ"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"یہ آلہ آپ کے والدین کے زیر انتظام ہے۔ آپ کے والدین آپ کی استعمال والی ایپس، آپ کا مقام اور آپ کے اسکرین کے وقت جیسی معلومات کو دیکھ اور اس کا نظم کر سکتے ہیں۔"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"ٹرسٹ ایجنٹ نے غیر مقفل رکھا ہے"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"چوری سے تحفظ\n آلہ مقفل ہے، بہت زیادہ غیر مقفل کرنے کی کوششیں"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>۔ <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"صوتی ترتیبات"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"خودکار طور پر میڈیا پر کیپشن لگائیں"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"غیر فعال کریں"</string>
<string name="sound_settings" msgid="8874581353127418308">"آواز اور وائبریشن"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"ترتیبات"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"لائیو کیپشن"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"والیوم کو محفوظ سطح تک کم کر دیا گیا"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"ہیڈ فون کا والیوم تجویز کردہ وقت سے زیادہ دیر تک بلند رہا ہے"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"ہیڈ فون والیوم اس ہفتے محفوظ حد سے تجاوز کر گیا ہے"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"وائبریٹ"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"‏‎%s والیوم کے کنٹرولز"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"کالز اور اطلاعات موصول ہونے پر گھنٹی بجے گی (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> پر چل رہی ہے"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"آڈیو چلتی رہے گی"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"‏سسٹم UI ٹیونر"</string>
<string name="status_bar" msgid="4357390266055077437">"اسٹیٹس بار"</string>
<string name="demo_mode" msgid="263484519766901593">"‏سسٹم UI ڈیمو موڈ"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g> بجے"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g> بجے"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"ہاٹ اسپاٹ"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"سیٹلائٹ، کوئی کنکشن نہیں ہے"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"سیٹلائٹ، کنکشن خراب ہے"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"سیٹلائٹ، کنکشن اچھا ہے"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"سیٹلائٹ، کنکشن دستیاب ہے"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"دفتری پروفائل"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"کچھ کیلئے دلچسپ لیکن سبھی کیلئے نہیں"</string>
<string name="tuner_warning" msgid="1861736288458481650">"‏سسٹم UI ٹیونر Android صارف انٹر فیس میں ردوبدل کرنے اور اسے حسب ضرورت بنانے کیلئے آپ کو اضافی طریقے دیتا ہے۔ یہ تجرباتی خصوصیات مستقبل کی ریلیزز میں تبدیل ہو سکتی، رک سکتی یا غائب ہو سکتی ہیں۔ احتیاط کے ساتھ آگے بڑھیں۔"</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"ترتیبات کھولیں"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"اسسٹنٹ کھولیں"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"مقفل اسکرین"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"نوٹس کھولیں"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"نوٹ لیں"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"سسٹم ملٹی ٹاسکنگ"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"موجودہ ایپ کے ساتھ دائیں جانب اسپلٹ اسکرین انٹر کریں"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"موجودہ ایپ کے ساتھ بائیں جانب اسپلٹ اسکرین انٹر کریں"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 762ce9302c49..82b6b460ef67 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Koʻrish uchun bosing"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Ekran yozuvi saqlanmadi"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Ekranni yozib olish boshlanmadi"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Muammoni yozib olish vositasi"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Yozuv qayta ishlanmoqda"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Muammo toʻplash seansi uchun faol bildirishnoma"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Muammo yozib olinmoqda"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Ulashish"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Muammo yozuvi saqlandi"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Ochish uchun bosing"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Yozuv saqlanmadi"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Yozib olish boshlanmadi"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Butun ekran rejimi"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Chiqish uchun tepadan pastga torting."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"OK"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saqlangan"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"uzish"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"faollashtirish"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ertaga yana avtomatik yoqilsin"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Tezkor ulashuv, Qurilmamni top va qurilma geolokatsiyasi kabi funksiyalar Bluetooth ishlatadi"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batareya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Garnitura"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Bu – ota-onangiz tomonidan boshqariladigan qurilma. Ota-onangiz siz foydalangan ilovalar, joylashuvingiz va qurilmadan foydalanish vaqti kabi axborotlarni koʻrishi va boshqarishi mumkin."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent tomonidan ochilgan"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Oʻgʻirlanishdan himoya\nQurilma qulflandi, juda koʻp urinildi"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Tovush sozlamalari"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Avtomatik taglavha yaratish"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"faolsizlantirish"</string>
<string name="sound_settings" msgid="8874581353127418308">"Tovush va tebranish"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Sozlamalar"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Jonli izoh"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Tovush xavfsiz darajaga pasaytirildi"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Quloqlik tavsiya etilganidan uzoqroq vaqt baland tovushda ishladi"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Quloqlik tovushi bu hafta xavfsiz balandlik limitidan oshib ketdi"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"tebranish"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s tovush balandligi tugmalari"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Chaqiruvlar va bildirishnomalar jiringlaydi (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g>da ijro etilmoqda"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Audio ijro etiladi"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"SystemUI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Holat qatori"</string>
<string name="demo_mode" msgid="263484519766901593">"Tizim interfeysi demo rejimi"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Hotspot"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Sputnik, ulanmagan"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Sputnik, aloqa sifati past"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Sputnik, aloqa sifati yaxshi"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Sputnik, aloqa mavjud"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Ish profili"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Diqqat!"</string>
<string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner yordamida siz Android foydalanuvchi interfeysini tuzatish va o‘zingizga moslashtirishingiz mumkin. Ushbu tajribaviy funksiyalar o‘zgarishi, buzilishi yoki keyingi versiyalarda olib tashlanishi mumkin. Ehtiyot bo‘lib davom eting."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Sozlamalarni ochish"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Assistentni ochish"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Ekran qulfi"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Qaydlarni ochish"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Qayd yaratish"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Tizimdagi multi-vazifalik"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Oʻng tomondagi ajratilgan ekran rejimiga kirish"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Chap tomondagi ajratilgan ekran rejimiga kirish"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index f2a49eab252e..2a8d4cdce031 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Nhấn để xem"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Có lỗi xảy ra khi lưu video ghi màn hình"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Lỗi khi bắt đầu ghi màn hình"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Trình ghi sự cố"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Đang xử lý bản ghi sự cố"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Thông báo hiển thị liên tục cho một phiên thu thập sự cố"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Ghi sự cố"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Chia sẻ"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Đã lưu bản ghi sự cố"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Nhấn để xem"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Đã xảy ra lỗi khi lưu bản ghi sự cố"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Đã xảy ra lỗi khi bắt đầu ghi sự cố"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Xem toàn màn hình"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Để thoát, hãy vuốt từ trên cùng xuống dưới."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Tôi hiểu"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Đã lưu"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ngắt kết nối"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"kích hoạt"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Tự động bật lại vào ngày mai"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Các tính năng như Chia sẻ nhanh, Tìm thiết bị của tôi và dịch vụ vị trí trên thiết bị có sử dụng Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> pin"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Âm thanh"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Tai nghe"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Thiết bị này do cha mẹ bạn quản lý. Cha mẹ có thể có thể xem và quản lý những thông tin như ứng dụng bạn dùng, vị trí của bạn và thời gian bạn sử dụng thiết bị."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Luôn được TrustAgent mở khóa"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Chống trộm\nĐã khoá thiết bị, quá nhiều lần mở khoá"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Cài đặt âm thanh"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Tự động tạo phụ đề cho nội dung nghe nhìn"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"tắt"</string>
<string name="sound_settings" msgid="8874581353127418308">"Âm thanh và chế độ rung"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Cài đặt"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Phụ đề trực tiếp"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Âm lượng đã giảm xuống mức an toàn hơn"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Bạn đã dùng tai nghe ở mức âm lượng cao lâu hơn khoảng thời gian khuyến nghị, điều này có thể gây tổn hại đến thính giác của bạn"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Âm lượng tai nghe đã vượt quá giới hạn an toàn của tuần này"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"rung"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Điều khiển âm lượng %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Cuộc gọi và thông báo sẽ đổ chuông (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Đang phát <xliff:g id="LABEL">%s</xliff:g> trên"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Âm thanh sẽ phát ra"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Bộ điều hướng giao diện người dùng hệ thống"</string>
<string name="status_bar" msgid="4357390266055077437">"Thanh trạng thái"</string>
<string name="demo_mode" msgid="263484519766901593">"Chế độ thử nghiệm giao diện người dùng hệ thống"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"lúc <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"vào <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Điểm phát sóng"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Không có kết nối vệ tinh"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Kết nối vệ tinh kém"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Kết nối vệ tinh tốt"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Hiện có kết nối vệ tinh"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Hồ sơ công việc"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Thú vị đối với một số người nhưng không phải tất cả"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Bộ điều hướng giao diện người dùng hệ thống cung cấp thêm cho bạn những cách chỉnh sửa và tùy chỉnh giao diện người dùng Android. Những tính năng thử nghiệm này có thể thay đổi, hỏng hoặc biến mất trong các phiên bản tương lai. Hãy thận trọng khi tiếp tục."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Mở phần cài đặt"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Mở Trợ lý"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Màn hình khoá"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Mở ghi chú"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Tạo ghi chú"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Xử lý đa nhiệm trong hệ thống"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Vào chế độ chia đôi màn hình, ứng dụng hiện tại ở màn hình bên phải"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Vào chế độ chia đôi màn hình, ứng dụng hiện tại ở màn hình bên trái"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 54cc8d8ec4fa..ddef35f6d1b8 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"点按即可查看"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"保存屏幕录制内容时出错"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"启动屏幕录制时出错"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"问题录制器"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"正在处理问题录制"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"针对问题收集会话的持续性通知"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"录制问题"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"分享"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"已保存问题录制内容"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"点按即可查看"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"保存问题录制内容时出错"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"启动问题录制时出错"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"目前处于全屏模式"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"要退出,请从顶部向下滑动。"</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"知道了"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"已保存"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"断开连接"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"启用"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明天自动重新开启"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"快速分享、查找我的设备、设备位置信息等功能会使用蓝牙"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> 的电量"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音频"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳机"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"此设备由您的家长管理。您的家长可以查看和管理相关信息,例如您使用的应用、您的位置信息和设备使用时间。"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"由 TrustAgent 保持解锁状态"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"防盗保护\n设备已锁定,因为尝试解锁的次数过多"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>(<xliff:g id="EXIT_CONDITION">%2$s</xliff:g>)"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"声音设置"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"自动生成媒体字幕"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"停用"</string>
<string name="sound_settings" msgid="8874581353127418308">"声音和振动"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"设置"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"实时字幕"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"音量已降至较安全的水平"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"您以高音量使用耳机的时长超过了建议值"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"耳机音量已超出这周的安全上限"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"振动"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s音量控件"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"有来电和通知时会响铃 (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g>播放位置:"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"音频播放位置:"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"系统界面调节工具"</string>
<string name="status_bar" msgid="4357390266055077437">"状态栏"</string>
<string name="demo_mode" msgid="263484519766901593">"系统界面演示模式"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"热点"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"卫星,无连接"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"卫星,连接质量不佳"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"卫星,连接质量良好"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"卫星,可连接"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"工作资料"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"并不适合所有用户"</string>
<string name="tuner_warning" msgid="1861736288458481650">"系统界面调节工具可让您以更多方式调整及定制 Android 界面。在日后推出的版本中,这些实验性功能可能会变更、失效或消失。操作时请务必谨慎。"</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"打开设置"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"打开 Google 助理"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"锁定屏幕"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"打开笔记"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"添加记事"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"系统多任务处理"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"进入分屏模式,当前应用显示于右侧"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"进入分屏模式,当前应用显示于左侧"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index c65307c92842..dbf2cc0a25c8 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"輕按即可查看"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"儲存螢幕錄影時發生錯誤"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"開始錄影畫面時發生錯誤"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"問題記錄工具"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"正在處理問題記錄"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"問題收集工作階段的持續通知"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"錄影問題"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"分享"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"問題記錄已儲存"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"輕按即可查看"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"儲存錄影問題時發生錯誤"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"開始記錄問題時發生錯誤"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"以全螢幕檢視"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"如要退出,請從螢幕頂部向下滑動。"</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"知道了"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"已儲存"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"解除連結"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"啟動"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明天自動重新開啟"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"「快速共享」、「尋找我的裝置」和裝置位置等功能都會使用藍牙"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"電量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音訊"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳機"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"此裝置由你的家長管理。家長可以查看及管理裝置上的資料,例如你使用的應用程式、位置和裝置使用時間。"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"由信任的代理保持解鎖狀態"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"防盜保護\n嘗試解鎖次數過多,裝置已上鎖"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>。<xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"音效設定"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"自動為媒體加入字幕"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"停用"</string>
<string name="sound_settings" msgid="8874581353127418308">"音效和震動"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"設定"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"即時字幕"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"音量已降至較安全的水平"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"以高音量使用耳機的時間已超過建議範圍"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"耳機音量已超過本週安全限制"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"震動"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s音量控制項"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"有來電和通知時會發出鈴聲 (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"正在播放「<xliff:g id="LABEL">%s</xliff:g>」的裝置:"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"音訊播放媒體"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"系統使用者介面調諧器"</string>
<string name="status_bar" msgid="4357390266055077437">"狀態列"</string>
<string name="demo_mode" msgid="263484519766901593">"系統使用者介面示範模式"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"在 <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"在<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"熱點"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"衛星,冇連線"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"衛星,連線質素唔好"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"衛星,連線質素好"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"衛星,可以連線"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"工作設定檔"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"這只是測試版本,並不包含完整功能"</string>
<string name="tuner_warning" msgid="1861736288458481650">"使用者介面調諧器讓你以更多方法修改和自訂 Android 使用者介面。但請小心,這些實驗功能可能會在日後發佈時更改、分拆或消失。"</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"開啟設定"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"開啟「Google 助理」"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"上鎖畫面"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"開啟筆記"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"寫筆記"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"系統多工處理"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"進入分割螢幕模式,並將目前的應用程式顯示在右側"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"進入分割螢幕模式,並將目前的應用程式顯示在左側"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index fcd6a1349040..d92de1cf6af9 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"輕觸即可查看"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"儲存螢幕錄影內容時發生錯誤"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"開始錄製螢幕畫面時發生錯誤"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"問題記錄工具"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"正在處理問題記錄"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"問題收集工作階段的持續性通知"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"正在記錄問題"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"分享"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"問題記錄已儲存"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"輕觸即可查看"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"儲存問題記錄時發生錯誤"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"開始記錄問題時發生錯誤"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"以全螢幕檢視"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"如要退出,請從螢幕頂端向下滑動。"</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"我知道了"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"已儲存"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"取消連結"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"啟用"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明天自動重新開啟"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"快速分享、尋找我的裝置和裝置位置資訊等功能都會使用藍牙"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"電量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音訊"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳機"</string>
@@ -534,9 +527,10 @@
<string name="monitoring_description_personal_profile_named_vpn" msgid="5083909710727365452">"你的個人應用程式已透過「<xliff:g id="VPN_APP">%1$s</xliff:g>」連線到網際網路。請注意,VPN 供應商可以看見你的網路活動,包括電子郵件和瀏覽資料。"</string>
<string name="monitoring_description_vpn_settings_separator" msgid="8292589617720435430">" "</string>
<string name="monitoring_description_vpn_settings" msgid="5264167033247632071">"開啟 VPN 設定"</string>
- <string name="monitoring_description_parental_controls" msgid="8184693528917051626">"這個裝置是由你的家長管理。家長可以查看及管理裝置上的資訊,例如你使用的應用程式、所在位置和裝置使用時間。"</string>
+ <string name="monitoring_description_parental_controls" msgid="8184693528917051626">"這個裝置是由你的家長管理。家長可以查看及管理裝置上的資訊,例如你使用的應用程式、所在位置和螢幕時間。"</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"由 TrustAgent 維持解鎖狀態"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"防盜保護\n解鎖失敗次數過多,裝置已鎖定"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>。<xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"音效設定"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"自動產生媒體字幕"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"停用"</string>
<string name="sound_settings" msgid="8874581353127418308">"音效與震動"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"設定"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"即時字幕"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"音量已調低至安全範圍"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"耳機以高音量播放已超過建議時間"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"耳罩式耳機的音量已超過本週的安全限制"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"震動"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"「%s」音量控制項"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"有來電和通知時會響鈴 (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"正在播放「<xliff:g id="LABEL">%s</xliff:g>」的裝置:"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"將播放音訊的媒體:"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"系統使用者介面調整精靈"</string>
<string name="status_bar" msgid="4357390266055077437">"狀態列"</string>
<string name="demo_mode" msgid="263484519766901593">"系統 UI 展示模式"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"於<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"於<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"無線基地台"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"衛星,沒有連線"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"衛星,連線品質不佳"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"衛星,連線品質良好"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"衛星,可連線"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"工作資料夾"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"有趣與否,見仁見智"</string>
<string name="tuner_warning" msgid="1861736288458481650">"系統使用者介面調整精靈可讓你透過其他方式,調整及自訂 Android 使用者介面。這些實驗性功能隨著版本更新可能會變更、損壞或消失,執行時請務必謹慎。"</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"開啟設定"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"開啟 Google 助理"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"螢幕鎖定"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"開啟記事"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"新增記事"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"系統多工處理"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"進入分割畫面模式,並將目前的應用程式顯示於右側"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"進入分割畫面模式,並將目前的應用程式顯示於左側"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index aafa61a460ca..9cd70829c30c 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -120,24 +120,15 @@
<string name="screenrecord_save_text" msgid="3008973099800840163">"Thepha ukuze ubuke"</string>
<string name="screenrecord_save_error" msgid="5862648532560118815">"Iphutha lokulondoloza okokuqopha iskrini"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Iphutha lokuqala ukurekhoda isikrini"</string>
- <!-- no translation found for issuerecord_title (286627115110121849) -->
- <skip />
- <!-- no translation found for issuerecord_background_processing_label (1666840264959336876) -->
- <skip />
- <!-- no translation found for issuerecord_channel_description (6142326363431474632) -->
- <skip />
- <!-- no translation found for issuerecord_ongoing_screen_only (6248206059935015722) -->
- <skip />
- <!-- no translation found for issuerecord_share_label (3992657993619876199) -->
- <skip />
- <!-- no translation found for issuerecord_save_title (4161043023696751591) -->
- <skip />
- <!-- no translation found for issuerecord_save_text (1205985304551521495) -->
- <skip />
- <!-- no translation found for issuerecord_save_error (6913040083446722726) -->
- <skip />
- <!-- no translation found for issuerecord_start_error (3402782952722871190) -->
- <skip />
+ <string name="issuerecord_title" msgid="286627115110121849">"Ushicilelo Lokurekhoda"</string>
+ <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Icubungula ushicilelo lokurekhoda"</string>
+ <string name="issuerecord_channel_description" msgid="6142326363431474632">"Isaziso esiqhubekayo seqoqo leseshini yoshicilelo"</string>
+ <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Inkinga yokurekhoda"</string>
+ <string name="issuerecord_share_label" msgid="3992657993619876199">"Yabelana"</string>
+ <string name="issuerecord_save_title" msgid="4161043023696751591">"Ushicilelo lokurekhoda lilondoloziwe"</string>
+ <string name="issuerecord_save_text" msgid="1205985304551521495">"Thepha ukuze ubuke"</string>
+ <string name="issuerecord_save_error" msgid="6913040083446722726">"Iphutha ekulondolozeni ushicilelo lokurekhoda"</string>
+ <string name="issuerecord_start_error" msgid="3402782952722871190">"Iphutha lokuqalisa ushicilelo lokurekhoda"</string>
<string name="immersive_cling_title" msgid="8372056499315585941">"Ukubuka isikrini esigcwele"</string>
<string name="immersive_cling_description" msgid="6913958856085185775">"Ukuze uphume, swayiphela phansi kusuka phezulu."</string>
<string name="immersive_cling_positive" msgid="3076681691468978568">"Ngiyezwa"</string>
@@ -277,6 +268,8 @@
<string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Ilondoloziwe"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"nqamula"</string>
<string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"yenza kusebenze"</string>
+ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Vula ngokuzenzekela futhi kusasa"</string>
+ <string name="turn_on_bluetooth_auto_info" msgid="8831410009251539988">"Izakhi ezifana Nokwabelana Ngokushesha, okuthi Thola Idivayisi Yami, kanye nendawo yedivayisi zisebenzisa i-Bluetooth"</string>
<string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ibhethri"</string>
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Umsindo"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ihedisethi"</string>
@@ -537,6 +530,7 @@
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Le divayisi iphethwe ngumzali wakho. Umzali wakho angabona futhi aphathe ulwazi olunjengezinhlelo zokusebenza ozisebenzisayo, indawo yakho, kanye nesikhathi sesikrini."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"I-VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Igcinwa ivuliwe ngo-TrustAgent"</string>
+ <string name="kg_prompt_after_adaptive_auth_lock" msgid="1265107698772588299">"Isivikelo sokweba\nIdivayisi ikhiyiwe, imizamo yokuvula eminingi kakhulu"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Izilungiselelo zomsindo"</string>
<string name="volume_odi_captions_tip" msgid="8825655463280990941">"Yenza amagama-ngcazo ngokuzenzakalela emidiya"</string>
@@ -546,6 +540,7 @@
<string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"khubaza"</string>
<string name="sound_settings" msgid="8874581353127418308">"Umsindo nokudlidliza"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Amasethingi"</string>
+ <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Okushuthwe Bukhoma"</string>
<string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Ivolumu yehliselwe kuleveli ephephile"</string>
<string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Ivolumu yama-headphone beyiphezulu isikhathi eside kunokunconyiwe"</string>
<string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Ivolumu yama-headphone ibe phezulu kunokunconyiwe, okungalimaza ukuzwa kwakho"</string>
@@ -589,6 +584,8 @@
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"dlidliza"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"%s izilawuli zevolomu"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Amakholi nezaziso zizokhala (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+ <string name="media_output_label_title" msgid="872824698593182505">"Idlala ku-<xliff:g id="LABEL">%s</xliff:g>"</string>
+ <string name="media_output_title_without_playing" msgid="3825663683169305013">"Umsindo uzodlala"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"Isishuni se-UI yesistimu"</string>
<string name="status_bar" msgid="4357390266055077437">"Ibha yesimo"</string>
<string name="demo_mode" msgid="263484519766901593">"Imodi yedemo ye-UI yesistimu"</string>
@@ -612,14 +609,10 @@
<string name="alarm_template" msgid="2234991538018805736">"ngo-<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"nge-<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"I-Hotspot"</string>
- <!-- no translation found for accessibility_status_bar_satellite_no_connection (3001571744269917762) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_poor_connection (5231478574952724160) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_good_connection (308079391708578704) -->
- <skip />
- <!-- no translation found for accessibility_status_bar_satellite_available (6514855015496916829) -->
- <skip />
+ <string name="accessibility_status_bar_satellite_no_connection" msgid="3001571744269917762">"Isethelayithi, alukho uxhumano"</string>
+ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"Isathelayithi, uxhumano olungalungile"</string>
+ <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Isethelayithi, uxhumano oluhle"</string>
+ <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Isethelayithi, uxhumano luyatholakala"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Iphrofayela yomsebenzi"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Kuyajabulisa kwabanye kodwa hhayi bonke"</string>
<string name="tuner_warning" msgid="1861736288458481650">"Isishuni se-UI sesistimu sikunika izindlela ezingeziwe zokuhlobisa nokwenza ngezifiso isixhumanisi sokubona se-Android. Lezi zici zesilingo zingashintsha, zephuke, noma zinyamalale ekukhishweni kwangakusasa. Qhubeka ngokuqaphela."</string>
@@ -741,7 +734,7 @@
<string name="group_system_access_system_settings" msgid="8731721963449070017">"Vula amasethingi"</string>
<string name="group_system_access_google_assistant" msgid="7210074957915968110">"Vula umsizi"</string>
<string name="group_system_lock_screen" msgid="7391191300363416543">"Khiya isikrini"</string>
- <string name="group_system_quick_memo" msgid="6257072703041301265">"Vula amanothi"</string>
+ <string name="group_system_quick_memo" msgid="3764560265935722903">"Thatha inothi"</string>
<string name="keyboard_shortcut_group_system_multitasking" msgid="1065232949510862593">"Ukwenza imisebenzi eminingi yesistimu"</string>
<string name="system_multitasking_rhs" msgid="2454557648974553729">"Faka ukuhlukanisa isikrini nge-app yamanje kuya ku-RHS"</string>
<string name="system_multitasking_lhs" msgid="3516599774920979402">"Faka ukuhlukanisa isikrini nge-app yamanje kuya ku-LHS"</string>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 65c69f78e9d0..beaa708b4dcc 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -1010,4 +1010,7 @@
<!-- Whether to use a machine learning model for back gesture falsing. -->
<bool name="config_useBackGestureML">true</bool>
+
+ <!-- Whether volume panel should use the large screen layout or not -->
+ <bool name="volume_panel_is_large_screen">false</bool>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 7537a003275d..4be1deb3de1c 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -608,9 +608,6 @@
<dimen name="volume_panel_slice_horizontal_padding">24dp</dimen>
<dimen name="volume_panel_corner_radius">52dp</dimen>
- <dimen name="volume_panel_content_padding">24dp</dimen>
- <dimen name="volume_panel_bottom_bar_horizontal_padding">24dp</dimen>
- <dimen name="volume_panel_bottom_bar_bottom_padding">20dp</dimen>
<!-- Size of each item in the ringer selector drawer. -->
<dimen name="volume_ringer_drawer_item_size">42dp</dimen>
@@ -1515,6 +1512,18 @@
<!-- The amount of vertical offset for the keyguard during the full shade transition. -->
<dimen name="lockscreen_shade_keyguard_transition_vertical_offset">0dp</dimen>
+ <!-- LOCKSCREEN -> GLANCEABLE_HUB transition: Amount to shift lockscreen content on entering -->
+ <dimen name="lockscreen_to_hub_transition_lockscreen_translation_x">-824dp</dimen>
+
+ <!-- GLANCEABLE_HUB -> LOCKSCREEN transition: Amount to shift lockscreen content on entering -->
+ <dimen name="hub_to_lockscreen_transition_lockscreen_translation_x">824dp</dimen>
+
+ <!-- DREAMING -> GLANCEABLE_HUB transition: Amount to shift dream overlay on entering -->
+ <dimen name="dreaming_to_hub_transition_dream_overlay_translation_x">-824dp</dimen>
+
+ <!-- GLANCEABLE_HUB -> DREAMING transition: Amount to shift dream overlay on entering -->
+ <dimen name="hub_to_dreaming_transition_dream_overlay_translation_x">824dp</dimen>
+
<!-- Distance that the full shade transition takes in order for media to fully transition to
the shade -->
<dimen name="lockscreen_shade_media_transition_distance">120dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index e401c71b3716..4263d9402d66 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1416,6 +1416,9 @@
<!-- Indication on the keyguard that appears when a trust agents unlocks the device. [CHAR LIMIT=40] -->
<string name="keyguard_indication_trust_unlocked">Kept unlocked by TrustAgent</string>
+ <!-- Message asking the user to authenticate with primary authentication methods (PIN/pattern/password) or biometrics after the device is locked by adaptive auth. [CHAR LIMIT=60] -->
+ <string name="kg_prompt_after_adaptive_auth_lock">Theft protection\nDevice locked, too many unlock attempts</string>
+
<!-- Accessibility string for current zen mode and selected exit condition. A template that simply concatenates existing mode string and the current condition description. [CHAR LIMIT=20] -->
<string name="zen_mode_and_condition"><xliff:g id="zen_mode" example="Priority interruptions only">%1$s</xliff:g>. <xliff:g id="exit_condition" example="For one hour">%2$s</xliff:g></string>
@@ -1510,6 +1513,11 @@
<string name="volume_ringer_status_vibrate">Vibrate</string>
<string name="volume_ringer_status_silent">Mute</string>
+ <!-- Media device casting volume slider label [CHAR_LIMIT=20] -->
+ <string name="media_device_cast">Cast</string>
+ <!-- A message shown when the notification volume changing is disabled because of the muted ring stream [CHAR_LIMIT=40]-->
+ <string name="stream_notification_unavailable">Unavailable because ring is muted</string>
+
<!-- Shown in the header of quick settings to indicate to the user that their phone ringer is on vibrate. [CHAR_LIMIT=NONE] -->
<!-- Shown in the header of quick settings to indicate to the user that their phone ringer is on silent (muted). [CHAR_LIMIT=NONE] -->
@@ -1519,6 +1527,9 @@
<string name="volume_stream_content_description_vibrate_a11y">%1$s. Tap to set to vibrate.</string>
<string name="volume_stream_content_description_mute_a11y">%1$s. Tap to mute.</string>
+ <!-- Label for button to enabled/disable live caption [CHAR_LIMIT=30] -->
+ <string name="volume_panel_noise_control_title">Noise Control</string>
+
<string name="volume_ringer_change">Tap to change ringer mode</string>
<!-- Hint for accessibility. For example: double tap to mute [CHAR_LIMIT=NONE] -->
@@ -1532,6 +1543,12 @@
<string name="volume_dialog_ringer_guidance_ring">Calls and notifications will ring (<xliff:g id="volume level" example="56">%1$s</xliff:g>)</string>
+ <!-- Title with application label for media output settings. [CHAR LIMIT=20] -->
+ <string name="media_output_label_title">Playing <xliff:g id="label" example="Music Player">%s</xliff:g> on</string>
+
+ <!-- Title for media output settings without media is playing. [CHAR LIMIT=20] -->
+ <string name="media_output_title_without_playing">Audio will play on</string>
+
<!-- Name of special SystemUI debug settings -->
<string name="system_ui_tuner">System UI Tuner</string>
@@ -3117,6 +3134,8 @@
<!-- [CHAR LIMIT=25] Long label used by Note Task Shortcut -->
<string name="note_task_shortcut_long_label">Note-taking, <xliff:g id="note_taking_app" example="Note-taking App">%1$s</xliff:g></string>
+ <!-- [CHAR LIMIT=NONE] Output switch chip text during broadcasting -->
+ <string name="audio_sharing_description">Sharing audio</string>
<!-- [CHAR LIMIT=NONE] Le audio broadcast dialog, media app is broadcasting -->
<string name="broadcasting_description_is_broadcasting">Broadcasting</string>
<!-- [CHAR LIMIT=NONE] Le audio broadcast dialog, title -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index ab3cacb27191..617eadbd447c 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -953,13 +953,22 @@
<item name="wallpaperTextColor">@*android:color/primary_text_material_dark</item>
</style>
- <style name="Theme.VolumePanelActivity" parent="@style/Theme.SystemUI">
- <item name="android:windowIsTranslucent">true</item>
- <item name="android:windowBackground">@android:color/transparent</item>
+ <style name="Theme.VolumePanelActivity" parent="@android:style/Theme.DeviceDefault.DayNight">
+ <item name="android:windowFullscreen">true</item>
+ <item name="android:windowContentOverlay">@null</item>
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
- <!-- Setting a placeholder will avoid using the SystemUI icon on the splash screen. -->
- <item name="android:windowSplashScreenAnimatedIcon">@drawable/ic_blank</item>
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:backgroundDimEnabled">true</item>
+ <item name="android:windowCloseOnTouchOutside">true</item>
+ <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
+ </style>
+
+ <style name="Theme.VolumePanelActivity.Popup" parent="@style/Theme.SystemUI.Dialog">
+ <item name="android:dialogCornerRadius">44dp</item>
+ <item name="android:colorBackground">?androidprv:attr/materialColorSurfaceContainerHigh
+ </item>
</style>
<style name="Theme.UserSwitcherFullscreenDialog" parent="@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen">
diff --git a/packages/SystemUI/res/xml/media_session_collapsed.xml b/packages/SystemUI/res/xml/media_session_collapsed.xml
index c053b33b4c63..2f2b10f8dc0c 100644
--- a/packages/SystemUI/res/xml/media_session_collapsed.xml
+++ b/packages/SystemUI/res/xml/media_session_collapsed.xml
@@ -55,6 +55,15 @@
app:layout_constraintBottom_toBottomOf="@+id/album_art" />
<Constraint
+ android:id="@+id/loading_effect_view"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/qs_media_session_height_collapsed"
+ app:layout_constraintStart_toStartOf="@+id/album_art"
+ app:layout_constraintEnd_toEndOf="@+id/album_art"
+ app:layout_constraintTop_toTopOf="@+id/album_art"
+ app:layout_constraintBottom_toBottomOf="@+id/album_art" />
+
+ <Constraint
android:id="@+id/header_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res/xml/media_session_expanded.xml b/packages/SystemUI/res/xml/media_session_expanded.xml
index 8bf7560d6ddb..0140d52bd175 100644
--- a/packages/SystemUI/res/xml/media_session_expanded.xml
+++ b/packages/SystemUI/res/xml/media_session_expanded.xml
@@ -48,6 +48,15 @@
app:layout_constraintBottom_toBottomOf="@+id/album_art" />
<Constraint
+ android:id="@+id/loading_effect_view"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/qs_media_session_height_expanded"
+ app:layout_constraintStart_toStartOf="@+id/album_art"
+ app:layout_constraintEnd_toEndOf="@+id/album_art"
+ app:layout_constraintTop_toTopOf="@+id/album_art"
+ app:layout_constraintBottom_toBottomOf="@+id/album_art" />
+
+ <Constraint
android:id="@+id/header_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/packages/SystemUI/shared/biometrics/Android.bp b/packages/SystemUI/shared/biometrics/Android.bp
index 2bd7d9780bf5..63de81d4a680 100644
--- a/packages/SystemUI/shared/biometrics/Android.bp
+++ b/packages/SystemUI/shared/biometrics/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/BiometricUserInfo.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/BiometricUserInfo.kt
index fdac37b7a2c8..b0d66110deb5 100644
--- a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/BiometricUserInfo.kt
+++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/BiometricUserInfo.kt
@@ -21,8 +21,12 @@ package com.android.systemui.biometrics.shared.model
*
* If the user's fallback credential is owned by another profile user the [deviceCredentialOwnerId]
* will differ from the user's [userId].
+ *
+ * If prompt requests to use the user's parent profile for device credential,
+ * [userIdForPasswordEntry] might differ from the user's [userId].
*/
data class BiometricUserInfo(
val userId: Int,
val deviceCredentialOwnerId: Int = userId,
+ val userIdForPasswordEntry: Int = userId,
)
diff --git a/packages/SystemUI/shared/keyguard/Android.bp b/packages/SystemUI/shared/keyguard/Android.bp
index 2181439ae1fc..2b3c77a651e9 100644
--- a/packages/SystemUI/shared/keyguard/Android.bp
+++ b/packages/SystemUI/shared/keyguard/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 91e6b6248d34..05e07a788892 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -43,7 +43,6 @@ import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags.REGION_SAMPLING
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.log.core.Logger
@@ -316,7 +315,7 @@ constructor(
object : KeyguardUpdateMonitorCallback() {
override fun onKeyguardVisibilityChanged(visible: Boolean) {
isKeyguardVisible = visible
- if (!KeyguardShadeMigrationNssl.isEnabled) {
+ if (!migrateClocksToBlueprint()) {
if (!isKeyguardVisible) {
clock?.run {
smallClock.animations.doze(if (isDozing) 1f else 0f)
@@ -378,7 +377,9 @@ constructor(
if (mode == ZenMode.OFF) SysuiR.string::dnd_is_off.name
else SysuiR.string::dnd_is_on.name
).also { data ->
- clock?.run { events.onZenDataChanged(data) }
+ mainExecutor.execute {
+ clock?.run { events.onZenDataChanged(data) }
+ }
}
}
@@ -410,7 +411,7 @@ constructor(
parent.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
listenForDozing(this)
- if (KeyguardShadeMigrationNssl.isEnabled) {
+ if (migrateClocksToBlueprint()) {
listenForDozeAmountTransition(this)
listenForAnyStateToAodTransition(this)
} else {
@@ -421,9 +422,11 @@ constructor(
smallTimeListener?.update(shouldTimeListenerRun)
largeTimeListener?.update(shouldTimeListenerRun)
- // Query ZenMode data
- zenModeCallback.onZenChanged(zenModeController.zen)
- zenModeCallback.onNextAlarmChanged()
+ bgExecutor.execute {
+ // Query ZenMode data
+ zenModeCallback.onZenChanged(zenModeController.zen)
+ zenModeCallback.onNextAlarmChanged()
+ }
}
fun unregisterListeners() {
diff --git a/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
index dec7d7992596..630610d1a85f 100644
--- a/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
@@ -19,13 +19,22 @@ package com.android.keyguard
import android.app.Presentation
import android.content.Context
import android.graphics.Color
+import android.graphics.Rect
import android.os.Bundle
import android.view.Display
+import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
+import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.view.WindowManager
+import android.widget.FrameLayout
+import android.widget.FrameLayout.LayoutParams
import com.android.keyguard.dagger.KeyguardStatusViewComponent
+import com.android.systemui.Flags.migrateClocksToBlueprint
+import com.android.systemui.plugins.clocks.ClockController
+import com.android.systemui.plugins.clocks.ClockFaceController
import com.android.systemui.res.R
+import com.android.systemui.shared.clocks.ClockRegistry
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -37,6 +46,8 @@ constructor(
@Assisted display: Display,
context: Context,
private val keyguardStatusViewComponentFactory: KeyguardStatusViewComponent.Factory,
+ private val clockRegistry: ClockRegistry,
+ private val clockEventController: ClockEventController,
) :
Presentation(
context,
@@ -45,31 +56,74 @@ constructor(
WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG
) {
+ private lateinit var rootView: FrameLayout
+ private var clock: View? = null
private lateinit var keyguardStatusViewController: KeyguardStatusViewController
- private lateinit var clock: KeyguardStatusView
+ private lateinit var faceController: ClockFaceController
+ private lateinit var clockFrame: FrameLayout
+
+ private val clockChangedListener =
+ object : ClockRegistry.ClockChangeListener {
+ override fun onCurrentClockChanged() {
+ setClock(clockRegistry.createCurrentClock())
+ }
+
+ override fun onAvailableClocksChanged() {}
+ }
+
+ private val layoutChangeListener =
+ object : View.OnLayoutChangeListener {
+ override fun onLayoutChange(
+ view: View,
+ left: Int,
+ top: Int,
+ right: Int,
+ bottom: Int,
+ oldLeft: Int,
+ oldTop: Int,
+ oldRight: Int,
+ oldBottom: Int
+ ) {
+ clock?.let {
+ faceController.events.onTargetRegionChanged(
+ Rect(it.left, it.top, it.width, it.height)
+ )
+ }
+ }
+ }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ if (migrateClocksToBlueprint()) {
+ onCreateV2()
+ } else {
+ onCreate()
+ }
+ }
+
+ fun onCreateV2() {
+ rootView = FrameLayout(getContext(), null)
+ rootView.setClipChildren(false)
+ setContentView(rootView)
+
+ setFullscreen()
+
+ setClock(clockRegistry.createCurrentClock())
+ }
+
+ fun onCreate() {
setContentView(
LayoutInflater.from(context)
.inflate(R.layout.keyguard_clock_presentation, /* root= */ null)
)
- val window = window ?: error("no window available.")
- // Logic to make the lock screen fullscreen
- window.decorView.systemUiVisibility =
- (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
- View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
- View.SYSTEM_UI_FLAG_LAYOUT_STABLE)
- window.attributes.fitInsetsTypes = 0
- window.isNavigationBarContrastEnforced = false
- window.navigationBarColor = Color.TRANSPARENT
+ setFullscreen()
clock = requireViewById(R.id.clock)
keyguardStatusViewController =
keyguardStatusViewComponentFactory
- .build(clock, display)
+ .build(clock as KeyguardStatusView, display)
.keyguardStatusViewController
.apply {
setDisplayedOnSecondaryDisplay()
@@ -77,6 +131,61 @@ constructor(
}
}
+ override fun onAttachedToWindow() {
+ if (migrateClocksToBlueprint()) {
+ clockRegistry.registerClockChangeListener(clockChangedListener)
+ clockEventController.registerListeners(clock!!)
+
+ faceController.animations.enter()
+ }
+ }
+
+ override fun onDetachedFromWindow() {
+ if (migrateClocksToBlueprint()) {
+ clockEventController.unregisterListeners()
+ clockRegistry.unregisterClockChangeListener(clockChangedListener)
+ }
+
+ super.onDetachedFromWindow()
+ }
+
+ override fun onDisplayChanged() {
+ val window = window ?: error("no window available.")
+ window.getDecorView().requestLayout()
+ }
+
+ private fun setClock(clockController: ClockController) {
+ clock?.removeOnLayoutChangeListener(layoutChangeListener)
+ rootView.removeAllViews()
+
+ faceController = clockController.largeClock
+ clock = faceController.view.also { it.addOnLayoutChangeListener(layoutChangeListener) }
+ rootView.addView(
+ clock,
+ FrameLayout.LayoutParams(
+ context.resources.getDimensionPixelSize(R.dimen.keyguard_presentation_width),
+ WRAP_CONTENT,
+ Gravity.CENTER,
+ )
+ )
+
+ clockEventController.clock = clockController
+ clockEventController.setLargeClockOnSecondaryDisplay(true)
+ faceController.events.onSecondaryDisplayChanged(true)
+ }
+
+ private fun setFullscreen() {
+ val window = window ?: error("no window available.")
+ // Logic to make the lock screen fullscreen
+ window.decorView.systemUiVisibility =
+ (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE)
+ window.attributes.fitInsetsTypes = 0
+ window.isNavigationBarContrastEnforced = false
+ window.navigationBarColor = Color.TRANSPARENT
+ }
+
/** [ConnectedDisplayKeyguardPresentation] factory. */
@AssistedFactory
interface Factory {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 2db3795cbad7..e621ffe4cbc4 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -47,7 +47,6 @@ import com.android.systemui.flags.FeatureFlagsClassic;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
-import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl;
import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.core.LogLevel;
@@ -349,7 +348,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
}
int getNotificationIconAreaHeight() {
- if (KeyguardShadeMigrationNssl.isEnabled()) {
+ if (migrateClocksToBlueprint()) {
return 0;
} else if (NotificationIconContainerRefactor.isEnabled()) {
return mAodIconContainer != null ? mAodIconContainer.getHeight() : 0;
@@ -597,7 +596,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
}
private void updateAodIcons() {
- if (!KeyguardShadeMigrationNssl.isEnabled()) {
+ if (!migrateClocksToBlueprint()) {
NotificationIconContainer nic = (NotificationIconContainer)
mView.findViewById(
com.android.systemui.res.R.id.left_aligned_notification_icon_container);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 8a6f101b6c6c..0bb5c174444f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -17,13 +17,10 @@ package com.android.keyguard;
import android.app.Presentation;
import android.content.Context;
-import android.graphics.Color;
-import android.graphics.Rect;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.display.DisplayManager;
import android.media.MediaRouter;
import android.media.MediaRouter.RouteInfo;
-import android.os.Bundle;
import android.os.Trace;
import android.text.TextUtils;
import android.util.Log;
@@ -31,20 +28,14 @@ import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayAddress;
import android.view.DisplayInfo;
-import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import androidx.annotation.Nullable;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.keyguard.dagger.KeyguardStatusViewComponent;
-import com.android.systemui.res.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.settings.DisplayTracker;
@@ -66,10 +57,8 @@ public class KeyguardDisplayManager {
private final DisplayManager mDisplayService;
private final DisplayTracker mDisplayTracker;
private final Lazy<NavigationBarController> mNavigationBarControllerLazy;
- private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
private final ConnectedDisplayKeyguardPresentation.Factory
mConnectedDisplayKeyguardPresentationFactory;
- private final FeatureFlags mFeatureFlags;
private final Context mContext;
private boolean mShowing;
@@ -106,18 +95,15 @@ public class KeyguardDisplayManager {
@Inject
public KeyguardDisplayManager(Context context,
Lazy<NavigationBarController> navigationBarControllerLazy,
- KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory,
DisplayTracker displayTracker,
@Main Executor mainExecutor,
@UiBackground Executor uiBgExecutor,
DeviceStateHelper deviceStateHelper,
KeyguardStateController keyguardStateController,
ConnectedDisplayKeyguardPresentation.Factory
- connectedDisplayKeyguardPresentationFactory,
- FeatureFlags featureFlags) {
+ connectedDisplayKeyguardPresentationFactory) {
mContext = context;
mNavigationBarControllerLazy = navigationBarControllerLazy;
- mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
uiBgExecutor.execute(() -> mMediaRouter = mContext.getSystemService(MediaRouter.class));
mDisplayService = mContext.getSystemService(DisplayManager.class);
mDisplayTracker = displayTracker;
@@ -125,7 +111,6 @@ public class KeyguardDisplayManager {
mDeviceStateHelper = deviceStateHelper;
mKeyguardStateController = keyguardStateController;
mConnectedDisplayKeyguardPresentationFactory = connectedDisplayKeyguardPresentationFactory;
- mFeatureFlags = featureFlags;
}
private boolean isKeyguardShowable(Display display) {
@@ -197,11 +182,7 @@ public class KeyguardDisplayManager {
}
Presentation createPresentation(Display display) {
- if (mFeatureFlags.isEnabled(Flags.ENABLE_CLOCK_KEYGUARD_PRESENTATION)) {
- return mConnectedDisplayKeyguardPresentationFactory.create(display);
- } else {
- return new KeyguardPresentation(mContext, display, mKeyguardStatusViewComponentFactory);
- }
+ return mConnectedDisplayKeyguardPresentationFactory.create(display);
}
/**
@@ -347,92 +328,4 @@ public class KeyguardDisplayManager {
&& mRearDisplayPhysicalAddress.equals(display.getAddress());
}
}
-
-
- @VisibleForTesting
- static final class KeyguardPresentation extends Presentation {
- private static final int VIDEO_SAFE_REGION = 80; // Percentage of display width & height
- private static final int MOVE_CLOCK_TIMEOUT = 10000; // 10s
- private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
- private KeyguardClockSwitchController mKeyguardClockSwitchController;
- private View mClock;
- private int mUsableWidth;
- private int mUsableHeight;
- private int mMarginTop;
- private int mMarginLeft;
- Runnable mMoveTextRunnable = new Runnable() {
- @Override
- public void run() {
- int x = mMarginLeft + (int) (Math.random() * (mUsableWidth - mClock.getWidth()));
- int y = mMarginTop + (int) (Math.random() * (mUsableHeight - mClock.getHeight()));
- mClock.setTranslationX(x);
- mClock.setTranslationY(y);
- mClock.postDelayed(mMoveTextRunnable, MOVE_CLOCK_TIMEOUT);
- }
- };
-
- KeyguardPresentation(Context context, Display display,
- KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory) {
- super(context, display, R.style.Theme_SystemUI_KeyguardPresentation,
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
- setCancelable(false);
- }
-
- @Override
- public void cancel() {
- // Do not allow anything to cancel KeyguardPresentation except KeyguardDisplayManager.
- }
-
- @Override
- public void onDetachedFromWindow() {
- mClock.removeCallbacks(mMoveTextRunnable);
- }
-
- @Override
- public void onDisplayChanged() {
- updateBounds();
- getWindow().getDecorView().requestLayout();
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- updateBounds();
-
- setContentView(LayoutInflater.from(getContext())
- .inflate(R.layout.keyguard_presentation, null));
-
- // Logic to make the lock screen fullscreen
- getWindow().getDecorView().setSystemUiVisibility(
- View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
- | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
- | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
- getWindow().getAttributes().setFitInsetsTypes(0 /* types */);
- getWindow().setNavigationBarContrastEnforced(false);
- getWindow().setNavigationBarColor(Color.TRANSPARENT);
-
- mClock = findViewById(R.id.clock);
-
- // Avoid screen burn in
- mClock.post(mMoveTextRunnable);
-
- mKeyguardClockSwitchController = mKeyguardStatusViewComponentFactory
- .build(findViewById(R.id.clock), getDisplay())
- .getKeyguardClockSwitchController();
-
- mKeyguardClockSwitchController.setOnlyClock(true);
- mKeyguardClockSwitchController.init();
- }
-
- private void updateBounds() {
- final Rect bounds = getWindow().getWindowManager().getMaximumWindowMetrics()
- .getBounds();
- mUsableWidth = VIDEO_SAFE_REGION * bounds.width() / 100;
- mUsableHeight = VIDEO_SAFE_REGION * bounds.height() / 100;
- mMarginLeft = (100 - VIDEO_SAFE_REGION) * bounds.width() / 200;
- mMarginTop = (100 - VIDEO_SAFE_REGION) * bounds.height() / 200;
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 3585feb3442d..84c8ea708031 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -19,6 +19,7 @@ package com.android.keyguard;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.view.WindowInsets.Type.ime;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_ADAPTIVE_AUTH_REQUEST;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT;
@@ -126,6 +127,8 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView {
return R.string.kg_prompt_reason_timeout_password;
case PROMPT_REASON_TRUSTAGENT_EXPIRED:
return R.string.kg_prompt_reason_timeout_password;
+ case PROMPT_REASON_ADAPTIVE_AUTH_REQUEST:
+ return R.string.kg_prompt_after_adaptive_auth_lock;
case PROMPT_REASON_NONE:
return 0;
default:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index db7ff888356c..bf8900da887a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -331,6 +331,9 @@ public class KeyguardPatternViewController
case PROMPT_REASON_TRUSTAGENT_EXPIRED:
resId = R.string.kg_prompt_reason_timeout_pattern;
break;
+ case PROMPT_REASON_ADAPTIVE_AUTH_REQUEST:
+ resId = R.string.kg_prompt_after_adaptive_auth_lock;
+ break;
case PROMPT_REASON_NONE:
break;
default:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index fcff0dbc0878..bcab6f054dd6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -16,6 +16,7 @@
package com.android.keyguard;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_ADAPTIVE_AUTH_REQUEST;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT;
@@ -138,6 +139,8 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView
return R.string.kg_prompt_reason_timeout_pin;
case PROMPT_REASON_TRUSTAGENT_EXPIRED:
return R.string.kg_prompt_reason_timeout_pin;
+ case PROMPT_REASON_ADAPTIVE_AUTH_REQUEST:
+ return R.string.kg_prompt_after_adaptive_auth_lock;
case PROMPT_REASON_NONE:
return 0;
default:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
index 476497dea9c4..10d1891c8405 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
@@ -19,6 +19,7 @@ package com.android.keyguard;
import static com.android.systemui.Flags.pinInputFieldStyledFocusState;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
+import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.StateListDrawable;
import android.util.TypedValue;
@@ -150,18 +151,22 @@ public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinB
}
private void setKeyboardBasedFocusOutline(boolean isAnyKeyboardConnected) {
- StateListDrawable background = (StateListDrawable) mPasswordEntry.getBackground();
- GradientDrawable stateDrawable = (GradientDrawable) background.getStateDrawable(0);
+ Drawable background = mPasswordEntry.getBackground();
+ if (!(background instanceof StateListDrawable)) return;
+ Drawable stateDrawable = ((StateListDrawable) background).getStateDrawable(0);
+ if (!(stateDrawable instanceof GradientDrawable gradientDrawable)) return;
+
int color = getResources().getColor(R.color.bouncer_password_focus_color);
if (!isAnyKeyboardConnected) {
- stateDrawable.setStroke(0, color);
+ gradientDrawable.setStroke(0, color);
} else {
int strokeWidthInDP = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3,
getResources().getDisplayMetrics());
- stateDrawable.setStroke(strokeWidthInDP, color);
+ gradientDrawable.setStroke(strokeWidthInDP, color);
}
}
+
@Override
protected void onViewDetached() {
super.onViewDetached();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
index 83b1a2cbbf52..3e87c1b60581 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
@@ -67,6 +67,11 @@ public interface KeyguardSecurityView {
int PROMPT_REASON_TRUSTAGENT_EXPIRED = 8;
/**
+ * Some auth is required because adaptive auth has determined risk
+ */
+ int PROMPT_REASON_ADAPTIVE_AUTH_REQUEST = 9;
+
+ /**
* Strong auth is required because the device has just booted because of an automatic
* mainline update.
*/
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
index a9928d80117b..63088aaf7ff4 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
@@ -20,6 +20,7 @@ import static android.app.slice.Slice.HINT_LIST_ITEM;
import android.app.PendingIntent;
import android.net.Uri;
+import android.os.Handler;
import android.os.Trace;
import android.provider.Settings;
import android.util.Log;
@@ -39,6 +40,9 @@ import androidx.slice.widget.SliceLiveData;
import com.android.keyguard.dagger.KeyguardStatusViewScope;
import com.android.systemui.Dumpable;
+import com.android.systemui.Flags;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardSliceProvider;
import com.android.systemui.plugins.ActivityStarter;
@@ -60,6 +64,8 @@ public class KeyguardSliceViewController extends ViewController<KeyguardSliceVie
Dumpable {
private static final String TAG = "KeyguardSliceViewCtrl";
+ private final Handler mHandler;
+ private final Handler mBgHandler;
private final ActivityStarter mActivityStarter;
private final ConfigurationController mConfigurationController;
private final TunerService mTunerService;
@@ -105,6 +111,8 @@ public class KeyguardSliceViewController extends ViewController<KeyguardSliceVie
@Inject
public KeyguardSliceViewController(
+ @Main Handler handler,
+ @Background Handler bgHandler,
KeyguardSliceView keyguardSliceView,
ActivityStarter activityStarter,
ConfigurationController configurationController,
@@ -112,6 +120,8 @@ public class KeyguardSliceViewController extends ViewController<KeyguardSliceVie
DumpManager dumpManager,
DisplayTracker displayTracker) {
super(keyguardSliceView);
+ mHandler = handler;
+ mBgHandler = bgHandler;
mActivityStarter = activityStarter;
mConfigurationController = configurationController;
mTunerService = tunerService;
@@ -182,24 +192,34 @@ public class KeyguardSliceViewController extends ViewController<KeyguardSliceVie
* Update contents of the view.
*/
public void refresh() {
- Slice slice;
+
Trace.beginSection("KeyguardSliceViewController#refresh");
- // We can optimize performance and avoid binder calls when we know that we're bound
- // to a Slice on the same process.
- if (KeyguardSliceProvider.KEYGUARD_SLICE_URI.equals(mKeyguardSliceUri.toString())) {
- KeyguardSliceProvider instance = KeyguardSliceProvider.getAttachedInstance();
- if (instance != null) {
- slice = instance.onBindSlice(mKeyguardSliceUri);
+ try {
+ Slice slice;
+ if (KeyguardSliceProvider.KEYGUARD_SLICE_URI.equals(mKeyguardSliceUri.toString())) {
+ KeyguardSliceProvider instance = KeyguardSliceProvider.getAttachedInstance();
+ if (instance != null) {
+ if (Flags.sliceManagerBinderCallBackground()) {
+ mBgHandler.post(() -> {
+ Slice _slice = instance.onBindSlice(mKeyguardSliceUri);
+ mHandler.post(() -> mObserver.onChanged(_slice));
+ });
+ return;
+ }
+ slice = instance.onBindSlice(mKeyguardSliceUri);
+ } else {
+ Log.w(TAG, "Keyguard slice not bound yet?");
+ slice = null;
+ }
} else {
- Log.w(TAG, "Keyguard slice not bound yet?");
- slice = null;
+ // TODO: Make SliceViewManager injectable
+ slice = SliceViewManager.getInstance(mView.getContext()).bindSlice(
+ mKeyguardSliceUri);
}
- } else {
- // TODO: Make SliceViewManager injectable
- slice = SliceViewManager.getInstance(mView.getContext()).bindSlice(mKeyguardSliceUri);
+ mObserver.onChanged(slice);
+ } finally {
+ Trace.endSection();
}
- mObserver.onChanged(slice);
- Trace.endSection();
}
void showSlice(Slice slice) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 1758831203d6..9421f150a38a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -54,7 +54,6 @@ import com.android.systemui.animation.ViewHierarchyAnimator;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
-import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl;
import com.android.systemui.keyguard.shared.model.TransitionState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.plugins.clocks.ClockController;
@@ -231,7 +230,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
}
mDumpManager.registerDumpable(getInstanceName(), this);
- if (KeyguardShadeMigrationNssl.isEnabled()) {
+ if (migrateClocksToBlueprint()) {
startCoroutines(EmptyCoroutineContext.INSTANCE);
}
}
@@ -511,7 +510,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone(layout);
int guideline;
- if (KeyguardShadeMigrationNssl.isEnabled()) {
+ if (migrateClocksToBlueprint()) {
guideline = R.id.split_shade_guideline;
} else {
guideline = R.id.qs_edge_guideline;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 536f3afdd575..38c2829e27f6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -35,6 +35,7 @@ import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
import static android.os.BatteryManager.CHARGING_POLICY_DEFAULT;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
@@ -382,6 +383,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private List<SubscriptionInfo> mSubscriptionInfo;
@VisibleForTesting
protected int mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
+ private boolean mFingerprintDetectRunning;
private boolean mIsDreaming;
private boolean mLogoutEnabled;
private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -1003,6 +1005,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
final boolean wasCancellingRestarting = mFingerprintRunningState
== BIOMETRIC_STATE_CANCELLING_RESTARTING;
mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
+ mFingerprintDetectRunning = false;
if (wasCancellingRestarting) {
KeyguardUpdateMonitor.this.updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
} else {
@@ -1111,6 +1114,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
boolean wasRunning = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING;
boolean isRunning = fingerprintRunningState == BIOMETRIC_STATE_RUNNING;
mFingerprintRunningState = fingerprintRunningState;
+ if (mFingerprintRunningState == BIOMETRIC_STATE_STOPPED) {
+ mFingerprintDetectRunning = false;
+ }
mLogger.logFingerprintRunningState(mFingerprintRunningState);
// Clients of KeyguardUpdateMonitor don't care about the internal state about the
// asynchronousness of the cancel cycle. So only notify them if the actually running state
@@ -1570,6 +1576,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
return isEncrypted || isLockDown;
}
+ /**
+ * Whether the device is locked by adaptive auth
+ */
+ public boolean isDeviceLockedByAdaptiveAuth(int userId) {
+ return containsFlag(mStrongAuthTracker.getStrongAuthForUser(userId),
+ SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST);
+ }
+
private boolean containsFlag(int haystack, int needle) {
return (haystack & needle) != 0;
}
@@ -1835,8 +1849,16 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
@Override
public void onFingerprintDetected(int sensorId, int userId,
boolean isStrongBiometric) {
- handleBiometricDetected(userId, FINGERPRINT, isStrongBiometric);
+ // Fingerprint lifecycle ends
+ if (mHandler.hasCallbacks(mFpCancelNotReceived)) {
+ mLogger.d("onFingerprintDetected()"
+ + " triggered while waiting for cancellation, removing watchdog");
+ mHandler.removeCallbacks(mFpCancelNotReceived);
+ }
+ // Don't send cancel if detect succeeds
+ mFingerprintCancelSignal = null;
setFingerprintRunningState(BIOMETRIC_STATE_STOPPED);
+ handleBiometricDetected(userId, FINGERPRINT, isStrongBiometric);
}
};
@@ -2099,6 +2121,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
@VisibleForTesting
void resetBiometricListeningState() {
mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
+ mFingerprintDetectRunning = false;
}
@VisibleForTesting
@@ -2537,8 +2560,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
return;
}
final boolean shouldListenForFingerprint = shouldListenForFingerprint(isUdfpsSupported());
- final boolean runningOrRestarting = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING
+ final boolean running = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING;
+ final boolean runningOrRestarting = running
|| mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING;
+ final boolean runDetect = !isUnlockingWithFingerprintAllowed();
+
if (runningOrRestarting && !shouldListenForFingerprint) {
if (action == BIOMETRIC_ACTION_START) {
mLogger.v("Ignoring stopListeningForFingerprint()");
@@ -2550,7 +2576,18 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mLogger.v("Ignoring startListeningForFingerprint()");
return;
}
- startListeningForFingerprint();
+ startListeningForFingerprint(runDetect);
+ } else if (running && (runDetect != mFingerprintDetectRunning)) {
+ if (action == BIOMETRIC_ACTION_STOP) {
+ if (runDetect) {
+ mLogger.v("Allowing startListeningForFingerprint(detect) despite"
+ + " BIOMETRIC_ACTION_STOP since auth was running before.");
+ } else {
+ mLogger.v("Ignoring startListeningForFingerprint() switch detect -> auth");
+ return;
+ }
+ }
+ startListeningForFingerprint(runDetect);
}
}
@@ -2809,7 +2846,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
&& biometricEnabledForUser
&& !isUserInLockdown(user);
final boolean strongerAuthRequired = !isUnlockingWithFingerprintAllowed();
- final boolean isSideFps = isSfpsSupported() && isSfpsEnrolled();
final boolean shouldListenBouncerState =
!strongerAuthRequired || !mPrimaryBouncerIsOrWillBeShowing;
@@ -2872,7 +2908,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
}
- private void startListeningForFingerprint() {
+ private void startListeningForFingerprint(boolean runDetect) {
final int userId = mSelectedUserInteractor.getSelectedUserId();
final boolean unlockPossible = isUnlockWithFingerprintPossible(userId);
if (mFingerprintCancelSignal != null) {
@@ -2902,18 +2938,20 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mFingerprintInteractiveToAuthProvider.getVendorExtension(userId));
}
- if (!isUnlockingWithFingerprintAllowed()) {
+ if (runDetect) {
mLogger.v("startListeningForFingerprint - detect");
mFpm.detectFingerprint(
mFingerprintCancelSignal,
mFingerprintDetectionCallback,
fingerprintAuthenticateOptions);
+ mFingerprintDetectRunning = true;
} else {
mLogger.v("startListeningForFingerprint");
mFpm.authenticate(null /* crypto */, mFingerprintCancelSignal,
mFingerprintAuthenticationCallback,
null /* handler */,
fingerprintAuthenticateOptions);
+ mFingerprintDetectRunning = false;
}
setFingerprintRunningState(BIOMETRIC_STATE_RUNNING);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
index ce03072e2f0b..c3c42399f1f7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
@@ -24,7 +24,7 @@ import androidx.annotation.Nullable;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.shade.ShadeExpansionStateManager;
-import com.android.systemui.shade.ShadeViewController;
+import com.android.systemui.shade.ShadeLockscreenInteractor;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -186,7 +186,7 @@ public interface KeyguardViewController {
* Registers the CentralSurfaces to which this Keyguard View is mounted.
*/
void registerCentralSurfaces(CentralSurfaces centralSurfaces,
- ShadeViewController shadeViewController,
+ ShadeLockscreenInteractor shadeLockscreenInteractor,
@Nullable ShadeExpansionStateManager shadeExpansionStateManager,
BiometricUnlockController biometricUnlockController,
View notificationContainer,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
index 9ebae9023d44..2000028dff41 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
@@ -16,6 +16,7 @@
package com.android.keyguard;
+import static com.android.systemui.Flags.migrateClocksToBlueprint;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
@@ -23,7 +24,6 @@ import android.util.Property;
import android.view.View;
import com.android.app.animation.Interpolators;
-import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.core.LogLevel;
import com.android.systemui.statusbar.StatusBarState;
@@ -109,7 +109,7 @@ public class KeyguardVisibilityHelper {
animProps.setDelay(0).setDuration(160);
log("goingToFullShade && !keyguardFadingAway");
}
- if (KeyguardShadeMigrationNssl.isEnabled()) {
+ if (migrateClocksToBlueprint()) {
log("Using LockscreenToGoneTransition 1");
} else {
PropertyAnimator.setProperty(
@@ -167,7 +167,7 @@ public class KeyguardVisibilityHelper {
animProps,
true /* animate */);
} else if (mScreenOffAnimationController.shouldAnimateInKeyguard()) {
- if (KeyguardShadeMigrationNssl.isEnabled()) {
+ if (migrateClocksToBlueprint()) {
log("Using GoneToAodTransition");
mKeyguardViewVisibilityAnimating = false;
} else {
@@ -183,7 +183,7 @@ public class KeyguardVisibilityHelper {
mView.setVisibility(View.VISIBLE);
}
} else {
- if (KeyguardShadeMigrationNssl.isEnabled()) {
+ if (migrateClocksToBlueprint()) {
log("Using LockscreenToGoneTransition 2");
} else {
log("Direct set Visibility to GONE");
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
index 878a5d88f164..a0f15efe7025 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
@@ -33,6 +33,7 @@ import com.android.systemui.plugins.clocks.ClockMessageBuffers;
import com.android.systemui.res.R;
import com.android.systemui.shared.clocks.ClockRegistry;
import com.android.systemui.shared.clocks.DefaultClockProvider;
+import com.android.systemui.util.ThreadAssert;
import dagger.Module;
import dagger.Provides;
@@ -74,7 +75,8 @@ public abstract class ClockRegistryModule {
clockBuffers,
/* keepAllLoaded = */ false,
/* subTag = */ "System",
- /* isTransitClockEnabled = */ featureFlags.isEnabled(Flags.TRANSIT_CLOCK));
+ /* isTransitClockEnabled = */ featureFlags.isEnabled(Flags.TRANSIT_CLOCK),
+ new ThreadAssert());
registry.registerListeners();
return registry;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt
index 2d854d934ca2..02ee2c951453 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt
@@ -16,18 +16,8 @@
package com.android.keyguard.logging
-import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel.DEBUG
-import com.android.systemui.log.dagger.BiometricLog
-import javax.inject.Inject
-
-/** Helper class for logging for [com.android.systemui.biometrics.FaceHelpMessageDeferral] */
-@SysUISingleton
-class FaceMessageDeferralLogger
-@Inject
-constructor(@BiometricLog private val logBuffer: LogBuffer) :
- BiometricMessageDeferralLogger(logBuffer, "FaceMessageDeferralLogger")
open class BiometricMessageDeferralLogger(
private val logBuffer: LogBuffer,
diff --git a/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt b/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt
index e8499d3ca9a9..ca83724aaf07 100644
--- a/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt
@@ -143,7 +143,7 @@ class CameraAvailabilityListener(
private fun notifyCameraActive(info: CameraProtectionInfo) {
listeners.forEach {
- it.onApplyCameraProtection(info.cutoutProtectionPath, info.cutoutBounds)
+ it.onApplyCameraProtection(info.cutoutProtectionPath, info.bounds)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/CameraProtectionInfo.kt b/packages/SystemUI/src/com/android/systemui/CameraProtectionInfo.kt
index 6314bd9a5615..9357056a0850 100644
--- a/packages/SystemUI/src/com/android/systemui/CameraProtectionInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/CameraProtectionInfo.kt
@@ -23,6 +23,6 @@ data class CameraProtectionInfo(
val logicalCameraId: String,
val physicalCameraId: String?,
val cutoutProtectionPath: Path,
- val cutoutBounds: Rect,
+ val bounds: Rect,
val displayUniqueId: String?,
)
diff --git a/packages/SystemUI/src/com/android/systemui/SysUICutoutProvider.kt b/packages/SystemUI/src/com/android/systemui/SysUICutoutProvider.kt
index aad934124dfb..7309599d9c04 100644
--- a/packages/SystemUI/src/com/android/systemui/SysUICutoutProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/SysUICutoutProvider.kt
@@ -17,8 +17,12 @@
package com.android.systemui
import android.content.Context
+import android.graphics.Rect
+import android.util.RotationUtils
+import android.view.Display
import android.view.DisplayCutout
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.display.naturalBounds
import javax.inject.Inject
@SysUISingleton
@@ -33,15 +37,43 @@ constructor(
cameraProtectionLoader.loadCameraProtectionInfoList()
}
- fun cutoutInfoForCurrentDisplay(): SysUICutoutInformation? {
+ /**
+ * Returns the [SysUICutoutInformation] for the current display and the current rotation.
+ *
+ * This means that the bounds of the display cutout and the camera protection will be
+ * adjusted/rotated for the current rotation.
+ */
+ fun cutoutInfoForCurrentDisplayAndRotation(): SysUICutoutInformation? {
val display = context.display
val displayCutout: DisplayCutout = display.cutout ?: return null
+ return SysUICutoutInformation(displayCutout, getCameraProtectionForDisplay(display))
+ }
+
+ private fun getCameraProtectionForDisplay(display: Display): CameraProtectionInfo? {
val displayUniqueId: String? = display.uniqueId
if (displayUniqueId.isNullOrEmpty()) {
- return SysUICutoutInformation(displayCutout, cameraProtection = null)
+ return null
}
- val cameraProtection: CameraProtectionInfo? =
+ val cameraProtection: CameraProtectionInfo =
cameraProtectionList.firstOrNull { it.displayUniqueId == displayUniqueId }
- return SysUICutoutInformation(displayCutout, cameraProtection)
+ ?: return null
+ val adjustedBoundsForRotation =
+ calculateCameraProtectionBoundsForRotation(display, cameraProtection.bounds)
+ return cameraProtection.copy(bounds = adjustedBoundsForRotation)
+ }
+
+ private fun calculateCameraProtectionBoundsForRotation(
+ display: Display,
+ originalProtectionBounds: Rect,
+ ): Rect {
+ val displayNaturalBounds = display.naturalBounds
+ val rotatedBoundsOut = Rect(originalProtectionBounds)
+ RotationUtils.rotateBounds(
+ /* inOutBounds = */ rotatedBoundsOut,
+ /* parentWidth = */ displayNaturalBounds.width(),
+ /* parentHeight = */ displayNaturalBounds.height(),
+ /* rotation = */ display.rotation
+ )
+ return rotatedBoundsOut
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
index 27f9106fde7c..6299739c4461 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
@@ -464,9 +464,6 @@ class MenuView extends FrameLayout implements
Bundle fragmentArgs = new Bundle();
fragmentArgs.putStringArray("targets", targets.toArray(new String[0]));
args.putBundle(":settings:show_fragment_args", fragmentArgs);
- // TODO: b/318748373 - The fragment should set its own title using the targets
- args.putString(
- ":settings:show_fragment_title", "Accessibility Shortcut");
intent.replaceExtras(args);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
return intent;
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 9fd060255e9b..e0f73a63113a 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -229,6 +229,9 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
private void fetchCurrentActiveOps() {
List<AppOpsManager.PackageOps> packageOps = mAppOps.getPackagesForOps(OPS);
+ if (packageOps == null) {
+ return;
+ }
for (AppOpsManager.PackageOps op : packageOps) {
for (AppOpsManager.OpEntry entry : op.getOps()) {
for (Map.Entry<String, AppOpsManager.AttributedOpEntry> attributedOpEntry :
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 3397906aa6ea..0bd44f0f3901 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -17,6 +17,7 @@
package com.android.systemui.biometrics;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
+import static android.hardware.biometrics.Flags.customBiometricPrompt;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_BIOMETRIC_PROMPT_TRANSITION;
@@ -401,37 +402,8 @@ public class AuthContainerView extends LinearLayout
@Nullable FaceSensorPropertiesInternal faceProps,
@NonNull VibratorHelper vibratorHelper
) {
- if (Utils.isBiometricAllowed(config.mPromptInfo)) {
- mPromptSelectorInteractorProvider.get().useBiometricsForAuthentication(
- config.mPromptInfo,
- config.mUserId,
- config.mOperationId,
- new BiometricModalities(fpProps, faceProps),
- config.mOpPackageName);
-
- if (constraintBp()) {
- mBiometricView = BiometricViewBinder.bind(mLayout, viewModel, null,
- // TODO(b/201510778): This uses the wrong timeout in some cases
- getJankListener(mLayout, TRANSIT,
- BiometricViewSizeBinder.ANIMATE_MEDIUM_TO_LARGE_DURATION_MS),
- mBackgroundView, mBiometricCallback, mApplicationCoroutineScope,
- vibratorHelper);
- } else {
- final BiometricPromptLayout view = (BiometricPromptLayout) layoutInflater.inflate(
- R.layout.biometric_prompt_layout, null, false);
- mBiometricView = BiometricViewBinder.bind(view, viewModel, mPanelController,
- // TODO(b/201510778): This uses the wrong timeout in some cases
- getJankListener(view, TRANSIT,
- BiometricViewSizeBinder.ANIMATE_MEDIUM_TO_LARGE_DURATION_MS),
- mBackgroundView, mBiometricCallback, mApplicationCoroutineScope,
- vibratorHelper);
-
- // TODO(b/251476085): migrate these dependencies
- if (fpProps != null && fpProps.isAnyUdfpsType()) {
- view.setUdfpsAdapter(new UdfpsDialogMeasureAdapter(view, fpProps),
- config.mScaleProvider);
- }
- }
+ if (Utils.isBiometricAllowed(config.mPromptInfo) || customBiometricPrompt()) {
+ addBiometricView(config, layoutInflater, viewModel, fpProps, faceProps, vibratorHelper);
} else if (constraintBp() && Utils.isDeviceCredentialAllowed(mConfig.mPromptInfo)) {
addCredentialView(true, false);
} else {
@@ -439,6 +411,44 @@ public class AuthContainerView extends LinearLayout
}
}
+
+ private void addBiometricView(@NonNull Config config, @NonNull LayoutInflater layoutInflater,
+ @NonNull PromptViewModel viewModel,
+ @Nullable FingerprintSensorPropertiesInternal fpProps,
+ @Nullable FaceSensorPropertiesInternal faceProps,
+ @NonNull VibratorHelper vibratorHelper) {
+ mPromptSelectorInteractorProvider.get().useBiometricsForAuthentication(
+ config.mPromptInfo,
+ config.mUserId,
+ config.mOperationId,
+ new BiometricModalities(fpProps, faceProps),
+ config.mOpPackageName);
+
+ if (constraintBp()) {
+ mBiometricView = BiometricViewBinder.bind(mLayout, viewModel, null,
+ // TODO(b/201510778): This uses the wrong timeout in some cases
+ getJankListener(mLayout, TRANSIT,
+ BiometricViewSizeBinder.ANIMATE_MEDIUM_TO_LARGE_DURATION_MS),
+ mBackgroundView, mBiometricCallback, mApplicationCoroutineScope,
+ vibratorHelper);
+ } else {
+ final BiometricPromptLayout view = (BiometricPromptLayout) layoutInflater.inflate(
+ R.layout.biometric_prompt_layout, null, false);
+ mBiometricView = BiometricViewBinder.bind(view, viewModel, mPanelController,
+ // TODO(b/201510778): This uses the wrong timeout in some cases
+ getJankListener(view, TRANSIT,
+ BiometricViewSizeBinder.ANIMATE_MEDIUM_TO_LARGE_DURATION_MS),
+ mBackgroundView, mBiometricCallback, mApplicationCoroutineScope,
+ vibratorHelper);
+
+ // TODO(b/251476085): migrate these dependencies
+ if (fpProps != null && fpProps.isAnyUdfpsType()) {
+ view.setUdfpsAdapter(new UdfpsDialogMeasureAdapter(view, fpProps),
+ config.mScaleProvider);
+ }
+ }
+ }
+
private void onBackInvoked() {
sendEarlyUserCanceled();
animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED);
@@ -524,7 +534,7 @@ public class AuthContainerView extends LinearLayout
() -> animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED));
if (constraintBp()) {
// Do nothing on attachment with constraintLayout
- } else if (Utils.isBiometricAllowed(mConfig.mPromptInfo)) {
+ } else if (Utils.isBiometricAllowed(mConfig.mPromptInfo) || customBiometricPrompt()) {
mBiometricScrollView.addView(mBiometricView.asView());
} else if (Utils.isDeviceCredentialAllowed(mConfig.mPromptInfo)) {
addCredentialView(true /* animatePanel */, false /* animateContents */);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt b/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt
index 8c68eac84963..90d06fb0bec1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt
@@ -18,14 +18,16 @@ package com.android.systemui.biometrics
import android.content.res.Resources
import com.android.keyguard.logging.BiometricMessageDeferralLogger
-import com.android.keyguard.logging.FaceMessageDeferralLogger
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.log.LogBuffer
+import com.android.systemui.log.dagger.BiometricLog
import com.android.systemui.res.R
import java.io.PrintWriter
import java.util.Objects
+import java.util.UUID
import javax.inject.Inject
@SysUISingleton
@@ -33,14 +35,16 @@ class FaceHelpMessageDeferralFactory
@Inject
constructor(
@Main private val resources: Resources,
- private val logBuffer: FaceMessageDeferralLogger,
+ @BiometricLog private val logBuffer: LogBuffer,
private val dumpManager: DumpManager
) {
fun create(): FaceHelpMessageDeferral {
+ val id = UUID.randomUUID().toString()
return FaceHelpMessageDeferral(
resources = resources,
- logBuffer = logBuffer,
+ logBuffer = BiometricMessageDeferralLogger(logBuffer, "FaceHelpMessageDeferral[$id]"),
dumpManager = dumpManager,
+ id = id,
)
}
}
@@ -51,15 +55,17 @@ constructor(
*/
class FaceHelpMessageDeferral(
resources: Resources,
- logBuffer: FaceMessageDeferralLogger,
- dumpManager: DumpManager
+ logBuffer: BiometricMessageDeferralLogger,
+ dumpManager: DumpManager,
+ val id: String,
) :
BiometricMessageDeferral(
resources.getIntArray(R.array.config_face_help_msgs_defer_until_timeout).toHashSet(),
resources.getIntArray(R.array.config_face_help_msgs_ignore).toHashSet(),
resources.getFloat(R.dimen.config_face_help_msgs_defer_until_timeout_threshold),
logBuffer,
- dumpManager
+ dumpManager,
+ id,
)
/**
@@ -72,7 +78,8 @@ open class BiometricMessageDeferral(
private val acquiredInfoToIgnore: Set<Int>,
private val threshold: Float,
private val logBuffer: BiometricMessageDeferralLogger,
- dumpManager: DumpManager
+ dumpManager: DumpManager,
+ id: String,
) : Dumpable {
private val acquiredInfoToFrequency: MutableMap<Int, Int> = HashMap()
private val acquiredInfoToHelpString: MutableMap<Int, String> = HashMap()
@@ -80,7 +87,10 @@ open class BiometricMessageDeferral(
private var totalFrames = 0
init {
- dumpManager.registerDumpable(this.javaClass.name, this)
+ dumpManager.registerNormalDumpable(
+ "${this.javaClass.name}[$id]",
+ this,
+ )
}
override fun dump(pw: PrintWriter, args: Array<out String>) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
index f5603ed732a5..c3e781800f9f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
@@ -132,13 +132,13 @@ abstract class UdfpsAnimationViewController<T : UdfpsAnimationView>(
override fun onViewAttached() {
dialogManager.registerListener(dialogListener)
dumpManager.registerDumpable(dumpTag, this)
- udfpsOverlayInteractor.setHandleTouches(shouldHandle = true)
+ udfpsOverlayInteractor.setHandleTouches(shouldHandle = !shouldPauseAuth())
}
override fun onViewDetached() {
dialogManager.unregisterListener(dialogListener)
dumpManager.unregisterDumpable(dumpTag)
- udfpsOverlayInteractor.setHandleTouches(shouldHandle = true)
+ udfpsOverlayInteractor.setHandleTouches(shouldHandle = !shouldPauseAuth())
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 716209d18a01..2c3ebe901850 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -80,6 +80,7 @@ import com.android.systemui.biometrics.ui.viewmodel.DeviceEntryUdfpsTouchOverlay
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
@@ -90,6 +91,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
@@ -116,6 +118,7 @@ import java.util.concurrent.Executor;
import javax.inject.Inject;
+import kotlinx.coroutines.CoroutineScope;
import kotlinx.coroutines.ExperimentalCoroutinesApi;
/**
@@ -173,6 +176,8 @@ public class UdfpsController implements DozeReceiver, Dumpable {
mDefaultUdfpsTouchOverlayViewModel;
@NonNull private final AlternateBouncerInteractor mAlternateBouncerInteractor;
@NonNull private final UdfpsOverlayInteractor mUdfpsOverlayInteractor;
+ @NonNull private final PowerInteractor mPowerInteractor;
+ @NonNull private final CoroutineScope mScope;
@NonNull private final InputManager mInputManager;
@NonNull private final UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate;
@NonNull private final SelectedUserInteractor mSelectedUserInteractor;
@@ -296,7 +301,9 @@ public class UdfpsController implements DozeReceiver, Dumpable {
mDeviceEntryUdfpsTouchOverlayViewModel,
mDefaultUdfpsTouchOverlayViewModel,
mShadeInteractor,
- mUdfpsOverlayInteractor
+ mUdfpsOverlayInteractor,
+ mPowerInteractor,
+ mScope
)));
}
@@ -678,7 +685,9 @@ public class UdfpsController implements DozeReceiver, Dumpable {
@NonNull KeyguardTransitionInteractor keyguardTransitionInteractor,
Lazy<DeviceEntryUdfpsTouchOverlayViewModel> deviceEntryUdfpsTouchOverlayViewModel,
Lazy<DefaultUdfpsTouchOverlayViewModel> defaultUdfpsTouchOverlayViewModel,
- @NonNull UdfpsOverlayInteractor udfpsOverlayInteractor) {
+ @NonNull UdfpsOverlayInteractor udfpsOverlayInteractor,
+ @NonNull PowerInteractor powerInteractor,
+ @Application CoroutineScope scope) {
mContext = context;
mExecution = execution;
mVibrator = vibrator;
@@ -720,6 +729,8 @@ public class UdfpsController implements DozeReceiver, Dumpable {
mShadeInteractor = shadeInteractor;
mAlternateBouncerInteractor = alternateBouncerInteractor;
mUdfpsOverlayInteractor = udfpsOverlayInteractor;
+ mPowerInteractor = powerInteractor;
+ mScope = scope;
mInputManager = inputManager;
mUdfpsKeyguardAccessibilityDelegate = udfpsKeyguardAccessibilityDelegate;
mSelectedUserInteractor = selectedUserInteractor;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index a209eae5673b..921e39532f58 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -44,6 +44,7 @@ import android.view.accessibility.AccessibilityManager.TouchExplorationStateChan
import androidx.annotation.LayoutRes
import androidx.annotation.VisibleForTesting
import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.Flags.udfpsViewPerformance
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
@@ -53,10 +54,13 @@ import com.android.systemui.biometrics.ui.viewmodel.DefaultUdfpsTouchOverlayView
import com.android.systemui.biometrics.ui.viewmodel.DeviceEntryUdfpsTouchOverlayViewModel
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.res.R
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.LockscreenShadeTransitionController
@@ -67,7 +71,13 @@ import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import dagger.Lazy
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
private const val TAG = "UdfpsControllerOverlay"
@@ -82,36 +92,45 @@ const val SETTING_REMOVE_ENROLLMENT_UI = "udfps_overlay_remove_enrollment_ui"
@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,
- @RequestReason val requestReason: Int,
- private val controllerCallback: IUdfpsOverlayControllerCallback,
- private val onTouch: (View, MotionEvent, Boolean) -> Boolean,
- private val activityTransitionAnimator: ActivityTransitionAnimator,
- private val primaryBouncerInteractor: PrimaryBouncerInteractor,
- private val alternateBouncerInteractor: AlternateBouncerInteractor,
- private val isDebuggable: Boolean = Build.IS_DEBUGGABLE,
- private val udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate,
- private val transitionInteractor: KeyguardTransitionInteractor,
- private val selectedUserInteractor: SelectedUserInteractor,
- private val deviceEntryUdfpsTouchOverlayViewModel: Lazy<DeviceEntryUdfpsTouchOverlayViewModel>,
- private val defaultUdfpsTouchOverlayViewModel: Lazy<DefaultUdfpsTouchOverlayViewModel>,
- private val shadeInteractor: ShadeInteractor,
- private val udfpsOverlayInteractor: UdfpsOverlayInteractor,
+ 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,
+ @RequestReason val requestReason: Int,
+ private val controllerCallback: IUdfpsOverlayControllerCallback,
+ private val onTouch: (View, MotionEvent, Boolean) -> Boolean,
+ private val activityTransitionAnimator: ActivityTransitionAnimator,
+ private val primaryBouncerInteractor: PrimaryBouncerInteractor,
+ private val alternateBouncerInteractor: AlternateBouncerInteractor,
+ private val isDebuggable: Boolean = Build.IS_DEBUGGABLE,
+ private val udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate,
+ private val transitionInteractor: KeyguardTransitionInteractor,
+ private val selectedUserInteractor: SelectedUserInteractor,
+ private val deviceEntryUdfpsTouchOverlayViewModel:
+ Lazy<DeviceEntryUdfpsTouchOverlayViewModel>,
+ private val defaultUdfpsTouchOverlayViewModel: Lazy<DefaultUdfpsTouchOverlayViewModel>,
+ private val shadeInteractor: ShadeInteractor,
+ private val udfpsOverlayInteractor: UdfpsOverlayInteractor,
+ private val powerInteractor: PowerInteractor,
+ @Application private val scope: CoroutineScope,
) {
+ private val isFinishedGoingToSleep: Flow<Unit> =
+ powerInteractor.detailedWakefulness
+ .filter { it.internalWakefulnessState == WakefulnessState.ASLEEP }
+ .map { } // map to Unit
+ private var listenForAsleepJob: Job? = null
+ private var addViewRunnable: Runnable? = null
private var overlayViewLegacy: UdfpsView? = null
private set
private var overlayTouchView: UdfpsTouchOverlay? = null
@@ -192,7 +211,8 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
if (requestReason.isImportantForAccessibility()) {
importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
}
- windowManager.addView(this, coreLayoutParams.updateDimensions(null))
+
+ addViewNowOrLater(this, null)
when (requestReason) {
REASON_AUTH_KEYGUARD ->
UdfpsTouchOverlayBinder.bind(
@@ -225,7 +245,7 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
}
- windowManager.addView(this, coreLayoutParams.updateDimensions(animation))
+ addViewNowOrLater(this, animation)
sensorRect = sensorBounds
}
}
@@ -257,6 +277,41 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
return false
}
+ private fun addViewNowOrLater(view: View, animation: UdfpsAnimationViewController<*>?) {
+ if (udfpsViewPerformance()) {
+ addViewRunnable = kotlinx.coroutines.Runnable {
+ windowManager.addView(
+ view,
+ coreLayoutParams.updateDimensions(animation)
+ )
+ }
+ if (powerInteractor.detailedWakefulness.value.internalWakefulnessState
+ != WakefulnessState.STARTING_TO_SLEEP) {
+ addViewIfPending()
+ } else {
+ listenForAsleepJob?.cancel()
+ listenForAsleepJob = scope.launch {
+ isFinishedGoingToSleep.collect {
+ addViewIfPending()
+ }
+ }
+ }
+ } else {
+ windowManager.addView(
+ view,
+ coreLayoutParams.updateDimensions(animation)
+ )
+ }
+ }
+
+ private fun addViewIfPending() {
+ addViewRunnable?.let {
+ listenForAsleepJob?.cancel()
+ it.run()
+ }
+ addViewRunnable = null
+ }
+
fun inflateUdfpsAnimation(
view: UdfpsView,
controller: UdfpsController
@@ -368,6 +423,7 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
overlayViewLegacy = null
overlayTouchView = null
overlayTouchListener = null
+ listenForAsleepJob?.cancel()
return wasShowing
}
@@ -412,7 +468,8 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
if (!shouldRotate(animation)) {
Log.v(
- TAG, "Skip rotating UDFPS bounds " + Surface.rotationToString(rot) +
+ TAG,
+ "Skip rotating UDFPS bounds " + Surface.rotationToString(rot) +
" animation=$animation" +
" isGoingToSleep=${keyguardUpdateMonitor.isGoingToSleep}" +
" isOccluded=${keyguardStateController.isOccluded}"
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
index 018d92e4e932..ec54e4ce5e86 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
@@ -378,7 +378,7 @@ open class UdfpsKeyguardViewControllerLegacy(
}
}
- override fun onViewDetached() {
+ public override fun onViewDetached() {
super.onViewDetached()
alternateBouncerInteractor.setAlternateBouncerUIAvailable(false, uniqueIdentifier)
faceDetectRunning = false
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractor.kt
index 191533c8f377..49973708487b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractor.kt
@@ -7,9 +7,9 @@ import android.os.UserManager
import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockscreenCredential
import com.android.internal.widget.VerifyCredentialResponse
-import com.android.systemui.res.R
import com.android.systemui.biometrics.domain.model.BiometricPromptRequest
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.res.R
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
import kotlinx.coroutines.delay
@@ -29,6 +29,9 @@ interface CredentialInteractor {
/** Get the effective user id (profile owner, if one exists) */
fun getCredentialOwnerOrSelfId(userId: Int): Int
+ /** Get parent user profile (if exists) */
+ fun getParentProfileIdOrSelfId(userId: Int): Int
+
/**
* Verifies a credential and returns a stream of results.
*
@@ -58,6 +61,9 @@ constructor(
override fun getCredentialOwnerOrSelfId(userId: Int): Int =
userManager.getCredentialOwnerProfile(userId)
+ override fun getParentProfileIdOrSelfId(userId: Int): Int =
+ userManager.getProfileParent(userId)?.id ?: userManager.getCredentialOwnerProfile(userId)
+
override fun verifyCredential(
request: BiometricPromptRequest.Credential,
credential: LockscreenCredential,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt
index 359e2e703ee6..e3facff9af12 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt
@@ -75,20 +75,32 @@ constructor(
PromptKind.Pin ->
BiometricPromptRequest.Credential.Pin(
info = promptInfo,
- userInfo = userInfo(userId),
+ userInfo =
+ userInfo(
+ userId,
+ promptInfo.shouldUseParentProfileForDeviceCredential()
+ ),
operationInfo = operationInfo(challenge)
)
PromptKind.Pattern ->
BiometricPromptRequest.Credential.Pattern(
info = promptInfo,
- userInfo = userInfo(userId),
+ userInfo =
+ userInfo(
+ userId,
+ promptInfo.shouldUseParentProfileForDeviceCredential()
+ ),
operationInfo = operationInfo(challenge),
stealthMode = credentialInteractor.isStealthModeActive(userId)
)
PromptKind.Password ->
BiometricPromptRequest.Credential.Password(
info = promptInfo,
- userInfo = userInfo(userId),
+ userInfo =
+ userInfo(
+ userId,
+ promptInfo.shouldUseParentProfileForDeviceCredential()
+ ),
operationInfo = operationInfo(challenge)
)
else -> null
@@ -96,10 +108,17 @@ constructor(
}
.distinctUntilChanged()
- private fun userInfo(userId: Int): BiometricUserInfo =
+ private fun userInfo(
+ userId: Int,
+ useParentProfileForDeviceCredential: Boolean
+ ): BiometricUserInfo =
BiometricUserInfo(
userId = userId,
- deviceCredentialOwnerId = credentialInteractor.getCredentialOwnerOrSelfId(userId)
+ deviceCredentialOwnerId = credentialInteractor.getCredentialOwnerOrSelfId(userId),
+ userIdForPasswordEntry =
+ if (useParentProfileForDeviceCredential)
+ credentialInteractor.getParentProfileIdOrSelfId(userId)
+ else credentialInteractor.getCredentialOwnerOrSelfId(userId),
)
private fun operationInfo(challenge: Long): BiometricOperationInfo =
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 31aadf51c4f2..cd5b12482d83 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
@@ -22,6 +22,7 @@ import android.content.Context
import android.hardware.biometrics.BiometricAuthenticator
import android.hardware.biometrics.BiometricConstants
import android.hardware.biometrics.BiometricPrompt
+import android.hardware.biometrics.Flags.customBiometricPrompt
import android.hardware.face.FaceManager
import android.text.method.ScrollingMovementMethod
import android.util.Log
@@ -123,13 +124,6 @@ object BiometricViewBinder {
(view as BiometricPromptLayout).updatedFingerprintAffordanceSize
}
- PromptIconViewBinder.bind(
- iconView,
- iconOverlayView,
- iconSizeOverride,
- viewModel,
- )
-
val indicatorMessageView = view.requireViewById<TextView>(R.id.indicator)
// Negative-side (left) buttons
@@ -156,6 +150,18 @@ object BiometricViewBinder {
view.repeatWhenAttached {
// these do not change and need to be set before any size transitions
val modalities = viewModel.modalities.first()
+
+ // If there is no biometrics available, biometric prompt is showing just for displaying
+ // content, no authentication needed.
+ if (!(customBiometricPrompt() && modalities.isEmpty)) {
+ PromptIconViewBinder.bind(
+ iconView,
+ iconOverlayView,
+ iconSizeOverride,
+ viewModel,
+ )
+ }
+
if (modalities.hasFingerprint) {
/**
* Load the given [rawResources] immediately so they are cached for use in the
@@ -432,9 +438,20 @@ object BiometricViewBinder {
// Play haptics
launch {
- viewModel.hapticsToPlay.collect { hapticFeedbackConstant ->
- if (hapticFeedbackConstant != HapticFeedbackConstants.NO_HAPTICS) {
- vibratorHelper.performHapticFeedback(view, hapticFeedbackConstant)
+ viewModel.hapticsToPlay.collect { haptics ->
+ if (haptics.hapticFeedbackConstant != HapticFeedbackConstants.NO_HAPTICS) {
+ if (haptics.flag != null) {
+ vibratorHelper.performHapticFeedback(
+ view,
+ haptics.hapticFeedbackConstant,
+ haptics.flag,
+ )
+ } else {
+ vibratorHelper.performHapticFeedback(
+ view,
+ haptics.hapticFeedbackConstant,
+ )
+ }
viewModel.clearHaptics()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
index 2417fe9cd333..a37d9168dfd3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
@@ -21,6 +21,7 @@ import android.animation.AnimatorSet
import android.animation.ValueAnimator
import android.graphics.Outline
import android.graphics.Rect
+import android.hardware.biometrics.Flags
import android.transition.AutoTransition
import android.transition.TransitionManager
import android.view.Surface
@@ -59,6 +60,7 @@ import com.android.systemui.res.R
import kotlin.math.abs
import kotlin.math.min
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
/** Helper for [BiometricViewBinder] to handle resize transitions. */
@@ -219,6 +221,18 @@ object BiometricViewSizeBinder {
view.repeatWhenAttached {
var currentSize: PromptSize? = null
+ val modalities = viewModel.modalities.first()
+ // TODO(b/288175072): Move all visibility settings together.
+ // If there is no biometrics available, biometric prompt is showing just for
+ // displaying content, no authentication needed.
+ if (Flags.customBiometricPrompt() && modalities.isEmpty) {
+ smallConstraintSet.setVisibility(iconHolderView.id, View.GONE)
+ smallConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
+ smallConstraintSet.setVisibility(R.id.indicator, View.GONE)
+ mediumConstraintSet.setVisibility(iconHolderView.id, View.GONE)
+ mediumConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
+ mediumConstraintSet.setVisibility(R.id.indicator, View.GONE)
+ }
lifecycleScope.launch {
combine(viewModel.position, viewModel.size, ::Pair).collect {
(position, size) ->
@@ -299,6 +313,7 @@ object BiometricViewSizeBinder {
// TODO(b/251476085): migrate the legacy panel controller and simplify this
view.repeatWhenAttached {
var currentSize: PromptSize? = null
+ val modalities = viewModel.modalities.first()
lifecycleScope.launch {
/**
* View is only set visible in BiometricViewSizeBinder once PromptSize is
@@ -318,6 +333,9 @@ object BiometricViewSizeBinder {
for (v in viewsToHideWhenSmall) {
v.showContentOrHide(forceHide = size.isSmall)
}
+ if (Flags.customBiometricPrompt() && modalities.isEmpty) {
+ iconHolderView.visibility = View.GONE
+ }
if (currentSize == null && size.isSmall) {
iconHolderView.alpha = 0f
}
@@ -328,8 +346,7 @@ object BiometricViewSizeBinder {
// TODO(b/302735104): Fix wrong height due to the delay of
// PromptContentView. addOnLayoutChangeListener() will cause crash when
// showing credential view, since |PromptIconViewModel| won't release
- // the
- // flow.
+ // the flow.
// propagate size changes to legacy panel controller and animate
// transitions
view.doOnLayout {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPasswordViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPasswordViewBinder.kt
index 0ad07ba924a0..4ed786be99c9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPasswordViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPasswordViewBinder.kt
@@ -12,12 +12,12 @@ import android.window.OnBackInvokedDispatcher
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.res.R
import com.android.systemui.biometrics.ui.CredentialPasswordView
import com.android.systemui.biometrics.ui.CredentialView
import com.android.systemui.biometrics.ui.IPinPad
import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.res.R
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.firstOrNull
@@ -42,7 +42,7 @@ object CredentialPasswordViewBinder {
view.repeatWhenAttached {
// the header info never changes - do it early
val header = viewModel.header.first()
- passwordField.setTextOperationUser(UserHandle.of(header.user.deviceCredentialOwnerId))
+ passwordField.setTextOperationUser(UserHandle.of(header.user.userIdForPasswordEntry))
viewModel.inputFlags.firstOrNull()?.let { flags -> passwordField.inputType = flags }
if (requestFocusForInput) {
passwordField.requestFocus()
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
index 7b4be0220ff2..9c28f1c16546 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
@@ -115,17 +115,11 @@ constructor(
}
private var overlayView: View? = null
- private var lottie: LottieAnimationView? = null
/** Show the side fingerprint sensor indicator */
private fun show() {
- overlayView?.let {
- if (it.isAttachedToWindow) {
- lottie = it.requireViewById<LottieAnimationView>(R.id.sidefps_animation)
- lottie?.pauseAnimation()
- lottie?.removeAllLottieOnCompositionLoadedListener()
- windowManager.get().removeView(it)
- }
+ if (overlayView?.isAttachedToWindow == true) {
+ return
}
overlayView = layoutInflater.get().inflate(R.layout.sidefps_view, null, false)
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 788991d2e45b..c933e0e31d40 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
@@ -53,6 +53,7 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
/** ViewModel for BiometricPrompt. */
@@ -144,9 +145,10 @@ constructor(
private val _forceLargeSize = MutableStateFlow(false)
private val _forceMediumSize = MutableStateFlow(false)
- private val _hapticsToPlay = MutableStateFlow(HapticFeedbackConstants.NO_HAPTICS)
+ private val _hapticsToPlay =
+ MutableStateFlow(HapticsToPlay(HapticFeedbackConstants.NO_HAPTICS, /* flag= */ null))
- /** Event fired to the view indicating a [HapticFeedbackConstants] to be played */
+ /** Event fired to the view indicating a [HapticsToPlay] */
val hapticsToPlay = _hapticsToPlay.asStateFlow()
/** The current position of the prompt */
@@ -686,16 +688,26 @@ constructor(
}
private fun vibrateOnSuccess() {
- _hapticsToPlay.value = HapticFeedbackConstants.CONFIRM
+ _hapticsToPlay.value =
+ HapticsToPlay(
+ HapticFeedbackConstants.CONFIRM,
+ HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING,
+ )
}
private fun vibrateOnError() {
- _hapticsToPlay.value = HapticFeedbackConstants.REJECT
+ _hapticsToPlay.value =
+ HapticsToPlay(
+ HapticFeedbackConstants.REJECT,
+ HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING,
+ )
}
- /** Clears the [hapticsToPlay] variable by setting it to the NO_HAPTICS default. */
+ /** Clears the [hapticsToPlay] variable by setting its constant to the NO_HAPTICS default. */
fun clearHaptics() {
- _hapticsToPlay.value = HapticFeedbackConstants.NO_HAPTICS
+ _hapticsToPlay.update { previous ->
+ HapticsToPlay(HapticFeedbackConstants.NO_HAPTICS, previous.flag)
+ }
}
companion object {
@@ -724,3 +736,9 @@ enum class FingerprintStartMode {
val isStarted: Boolean
get() = this == Normal || this == Delayed
}
+
+/**
+ * The state of haptic feedback to play. It is composed by a [HapticFeedbackConstants] and a
+ * [HapticFeedbackConstants] flag.
+ */
+data class HapticsToPlay(val hapticFeedbackConstant: Int, val flag: Int?)
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialogDelegate.java
index 00bbb20ed4f9..6af0fa069dbc 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialogDelegate.java
@@ -40,6 +40,7 @@ import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.media.MediaOutputConstants;
import com.android.systemui.broadcast.BroadcastSender;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.media.controls.util.MediaDataUtils;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.res.R;
@@ -74,7 +75,7 @@ public class BroadcastDialogDelegate implements SystemUIDialog.Delegate {
private final SystemUIDialog.Factory mSystemUIDialogFactory;
private final String mCurrentBroadcastApp;
private final String mOutputPackageName;
- private final Executor mExecutor;
+ private final Executor mBgExecutor;
private boolean mShouldLaunchLeBroadcastDialog;
private Button mSwitchBroadcast;
@@ -159,7 +160,7 @@ public class BroadcastDialogDelegate implements SystemUIDialog.Delegate {
MediaOutputDialogFactory mediaOutputDialogFactory,
@Nullable LocalBluetoothManager localBluetoothManager,
UiEventLogger uiEventLogger,
- Executor executor,
+ @Background Executor bgExecutor,
BroadcastSender broadcastSender,
SystemUIDialog.Factory systemUIDialogFactory,
@Assisted(CURRENT_BROADCAST_APP) String currentBroadcastApp,
@@ -171,7 +172,7 @@ public class BroadcastDialogDelegate implements SystemUIDialog.Delegate {
mCurrentBroadcastApp = currentBroadcastApp;
mOutputPackageName = outputPkgName;
mUiEventLogger = uiEventLogger;
- mExecutor = executor;
+ mBgExecutor = bgExecutor;
mBroadcastSender = broadcastSender;
if (DEBUG) {
@@ -187,7 +188,7 @@ public class BroadcastDialogDelegate implements SystemUIDialog.Delegate {
@Override
public void onStart(SystemUIDialog dialog) {
mDialogs.add(dialog);
- registerBroadcastCallBack(mExecutor, mBroadcastCallback);
+ registerBroadcastCallBack(mBgExecutor, mBroadcastCallback);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
index 8197145f9646..c25e748f8668 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
@@ -50,6 +50,7 @@ import com.android.systemui.res.R.string.kg_fp_not_recognized
import com.android.systemui.res.R.string.kg_primary_auth_locked_out_password
import com.android.systemui.res.R.string.kg_primary_auth_locked_out_pattern
import com.android.systemui.res.R.string.kg_primary_auth_locked_out_pin
+import com.android.systemui.res.R.string.kg_prompt_after_adaptive_auth_lock
import com.android.systemui.res.R.string.kg_prompt_after_dpm_lock
import com.android.systemui.res.R.string.kg_prompt_after_update_password
import com.android.systemui.res.R.string.kg_prompt_after_update_pattern
@@ -208,6 +209,11 @@ constructor(
} else {
faceLockedOut(currentSecurityMode, isFingerprintAuthCurrentlyAllowed.value)
}
+ } else if (flags.isSomeAuthRequiredAfterAdaptiveAuthRequest) {
+ authRequiredAfterAdaptiveAuthRequest(
+ currentSecurityMode,
+ isFingerprintAuthCurrentlyAllowed.value
+ )
} else if (
trustOrBiometricsAvailable &&
flags.strongerAuthRequiredAfterNonStrongBiometricsTimeout
@@ -464,6 +470,34 @@ private fun authRequiredAfterAdminLockdown(securityMode: SecurityMode): BouncerM
}.toMessage()
}
+private fun authRequiredAfterAdaptiveAuthRequest(
+ securityMode: SecurityMode,
+ fpAuthIsAllowed: Boolean
+): BouncerMessageModel {
+ return if (fpAuthIsAllowed) authRequiredAfterAdaptiveAuthRequestFingerprintAllowed(securityMode)
+ else
+ return when (securityMode) {
+ SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_after_adaptive_auth_lock)
+ SecurityMode.Password ->
+ Pair(keyguard_enter_password, kg_prompt_after_adaptive_auth_lock)
+ SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_after_adaptive_auth_lock)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
+private fun authRequiredAfterAdaptiveAuthRequestFingerprintAllowed(
+ securityMode: SecurityMode
+): BouncerMessageModel {
+ return when (securityMode) {
+ SecurityMode.Pattern ->
+ Pair(kg_unlock_with_pattern_or_fp, kg_prompt_after_adaptive_auth_lock)
+ SecurityMode.Password ->
+ Pair(kg_unlock_with_password_or_fp, kg_prompt_after_adaptive_auth_lock)
+ SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, kg_prompt_after_adaptive_auth_lock)
+ else -> Pair(0, 0)
+ }.toMessage()
+}
+
private fun authRequiredAfterUserLockdown(securityMode: SecurityMode): BouncerMessageModel {
return when (securityMode) {
SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_after_user_lockdown_pattern)
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
index 25ccc1658744..357eca37ba37 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
@@ -93,11 +93,13 @@ public class BrightLineFalsingManager implements FalsingManager {
@Override
public void onSessionEnded() {
mLastProximityEvent = null;
+ mHistoryTracker.removeBeliefListener(mBeliefListener);
mClassifiers.forEach(FalsingClassifier::onSessionEnded);
}
@Override
public void onSessionStarted() {
+ mHistoryTracker.addBeliefListener(mBeliefListener);
mClassifiers.forEach(FalsingClassifier::onSessionStarted);
}
};
@@ -200,7 +202,6 @@ public class BrightLineFalsingManager implements FalsingManager {
mDataProvider.addSessionListener(mSessionListener);
mDataProvider.addGestureCompleteListener(mGestureFinalizedListener);
- mHistoryTracker.addBeliefListener(mBeliefListener);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
index 5b1082acd59f..3819e614aca0 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
@@ -27,6 +27,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.communal.domain.interactor.CommunalInteractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dock.DockManager;
@@ -39,6 +40,7 @@ import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChang
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.kotlin.BooleanFlowOperators;
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.sensors.ThresholdSensor;
@@ -67,6 +69,7 @@ class FalsingCollectorImpl implements FalsingCollector {
private final StatusBarStateController mStatusBarStateController;
private final KeyguardStateController mKeyguardStateController;
private final Lazy<ShadeInteractor> mShadeInteractorLazy;
+ private final Lazy<CommunalInteractor> mCommunalInteractorLazy;
private final BatteryController mBatteryController;
private final DockManager mDockManager;
private final DelayableExecutor mMainExecutor;
@@ -76,6 +79,7 @@ class FalsingCollectorImpl implements FalsingCollector {
private int mState;
private boolean mShowingAod;
+ private boolean mShowingCommunalHub;
private boolean mScreenOn;
private boolean mSessionStarted;
private MotionEvent mPendingDownEvent;
@@ -145,7 +149,8 @@ class FalsingCollectorImpl implements FalsingCollector {
@Main DelayableExecutor mainExecutor,
JavaAdapter javaAdapter,
SystemClock systemClock,
- Lazy<SelectedUserInteractor> userInteractor) {
+ Lazy<SelectedUserInteractor> userInteractor,
+ Lazy<CommunalInteractor> communalInteractorLazy) {
mFalsingDataProvider = falsingDataProvider;
mFalsingManager = falsingManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -160,6 +165,7 @@ class FalsingCollectorImpl implements FalsingCollector {
mJavaAdapter = javaAdapter;
mSystemClock = systemClock;
mUserInteractor = userInteractor;
+ mCommunalInteractorLazy = communalInteractorLazy;
}
@Override
@@ -176,6 +182,13 @@ class FalsingCollectorImpl implements FalsingCollector {
mShadeInteractorLazy.get().isQsExpanded(),
this::onQsExpansionChanged
);
+ final CommunalInteractor communalInteractor = mCommunalInteractorLazy.get();
+ mJavaAdapter.alwaysCollectFlow(
+ BooleanFlowOperators.INSTANCE.and(
+ communalInteractor.isCommunalEnabled(),
+ communalInteractor.isCommunalShowing()),
+ this::onShowingCommunalHubChanged
+ );
mBatteryController.addCallback(mBatteryListener);
mDockManager.addListener(mDockEventListener);
@@ -205,6 +218,12 @@ class FalsingCollectorImpl implements FalsingCollector {
}
}
+ private void onShowingCommunalHubChanged(boolean isShowing) {
+ logDebug("REAL: onShowingCommunalHubChanged(" + isShowing + ")");
+ mShowingCommunalHub = isShowing;
+ updateSessionActive();
+ }
+
@Override
public boolean shouldEnforceBouncer() {
return false;
@@ -333,7 +352,10 @@ class FalsingCollectorImpl implements FalsingCollector {
}
private boolean shouldSessionBeActive() {
- return mScreenOn && (mState == StatusBarState.KEYGUARD) && !mShowingAod;
+ return mScreenOn
+ && (mState == StatusBarState.KEYGUARD)
+ && !mShowingAod
+ && !mShowingCommunalHub;
}
private void updateSessionActive() {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/domain/interactor/FalsingInteractor.kt b/packages/SystemUI/src/com/android/systemui/classifier/domain/interactor/FalsingInteractor.kt
index 2e861c399ee9..57e9ade30bb5 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/domain/interactor/FalsingInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/classifier/domain/interactor/FalsingInteractor.kt
@@ -17,23 +17,27 @@
package com.android.systemui.classifier.domain.interactor
import android.view.MotionEvent
+import com.android.systemui.classifier.Classifier
import com.android.systemui.classifier.FalsingClassifier
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorActual
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.FalsingManager
import javax.inject.Inject
/**
- * Exposes the subset of the [FalsingCollector] API that's required by external callers.
+ * Exposes the subset of the [FalsingCollector] and [FalsingManager] APIs that's required by
+ * external callers.
*
- * E.g. methods of [FalsingCollector] that are not exposed by this class don't need to be invoked by
- * external callers as they're already called by the scene framework.
+ * E.g. methods of the above APIs that are not exposed by this class either don't need to be invoked
+ * by external callers (as they're already called by the scene framework) or haven't been added yet.
*/
@SysUISingleton
class FalsingInteractor
@Inject
constructor(
@FalsingCollectorActual private val collector: FalsingCollector,
+ private val manager: FalsingManager,
) {
/**
* Notifies of a [MotionEvent] that passed through the UI.
@@ -62,4 +66,9 @@ constructor(
fun updateFalseConfidence(
result: FalsingClassifier.Result,
) = collector.updateFalseConfidence(result)
+
+ /** Returns `true` if the gesture should be rejected. */
+ fun isFalseTouch(
+ @Classifier.InteractionType interactionType: Int,
+ ): Boolean = manager.isFalseTouch(interactionType)
}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
index e0ce3db39403..c7a47b18f467 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
@@ -18,12 +18,14 @@ package com.android.systemui.clipboardoverlay;
import static android.content.ClipDescription.CLASSIFICATION_COMPLETE;
+import static com.android.systemui.Flags.clipboardNoninteractiveOnLockscreen;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ENTERED;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_UPDATED;
import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_TOAST_SHOWN;
import static com.google.android.setupcompat.util.WizardManagerHelper.SETTINGS_SECURE_USER_SETUP_COMPLETE;
+import android.app.KeyguardManager;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
@@ -57,6 +59,7 @@ public class ClipboardListener implements
private final Provider<ClipboardOverlayController> mOverlayProvider;
private final ClipboardToast mClipboardToast;
private final ClipboardManager mClipboardManager;
+ private final KeyguardManager mKeyguardManager;
private final UiEventLogger mUiEventLogger;
private ClipboardOverlay mClipboardOverlay;
@@ -65,11 +68,13 @@ public class ClipboardListener implements
Provider<ClipboardOverlayController> clipboardOverlayControllerProvider,
ClipboardToast clipboardToast,
ClipboardManager clipboardManager,
+ KeyguardManager keyguardManager,
UiEventLogger uiEventLogger) {
mContext = context;
mOverlayProvider = clipboardOverlayControllerProvider;
mClipboardToast = clipboardToast;
mClipboardManager = clipboardManager;
+ mKeyguardManager = keyguardManager;
mUiEventLogger = uiEventLogger;
}
@@ -92,7 +97,9 @@ public class ClipboardListener implements
return;
}
- if (!isUserSetupComplete() // user should not access intents from this state
+ // user should not access intents before setup or while device is locked
+ if ((clipboardNoninteractiveOnLockscreen() && mKeyguardManager.isDeviceLocked())
+ || !isUserSetupComplete()
|| clipData == null // shouldn't happen, but just in case
|| clipData.getItemCount() == 0) {
if (shouldShowToast(clipData)) {
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
index 2af49cfbf1b1..b2699673f7ea 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
@@ -143,7 +143,7 @@ public class ClipboardOverlayView extends DraggableConstraintLayout {
mTextPreview.getViewTreeObserver().addOnPreDrawListener(() -> {
int availableHeight = mTextPreview.getHeight()
- (mTextPreview.getPaddingTop() + mTextPreview.getPaddingBottom());
- mTextPreview.setMaxLines(availableHeight / mTextPreview.getLineHeight());
+ mTextPreview.setMaxLines(Math.max(availableHeight / mTextPreview.getLineHeight(), 1));
return true;
});
super.onFinishInflate();
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
index f7ba5a44f4c9..8397372e0735 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
@@ -88,7 +88,6 @@ constructor(
val docked = dockManager.isDocked
return when {
- to == KeyguardState.DREAMING -> CommunalSceneKey.Blank
docked && to == KeyguardState.LOCKSCREEN && from != KeyguardState.GLANCEABLE_HUB -> {
CommunalSceneKey.Communal
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
index 0bad33b8f920..82d943796e2a 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
@@ -23,6 +23,7 @@ import com.android.systemui.communal.data.repository.CommunalRepositoryModule
import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryModule
import com.android.systemui.communal.data.repository.CommunalTutorialRepositoryModule
import com.android.systemui.communal.data.repository.CommunalWidgetRepositoryModule
+import com.android.systemui.communal.widgets.CommunalWidgetModule
import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
import com.android.systemui.communal.widgets.EditWidgetsActivityStarterImpl
import dagger.Binds
@@ -36,6 +37,7 @@ import dagger.Module
CommunalTutorialRepositoryModule::class,
CommunalWidgetRepositoryModule::class,
CommunalDatabaseModule::class,
+ CommunalWidgetModule::class,
CommunalPrefsRepositoryModule::class,
CommunalSettingsRepositoryModule::class,
]
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt
index 3b727d2a7ade..9cd77c4713fa 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt
@@ -23,8 +23,8 @@ import androidx.room.Query
import androidx.room.RoomDatabase
import androidx.room.Transaction
import androidx.sqlite.db.SupportSQLiteDatabase
-import com.android.systemui.communal.data.repository.CommunalWidgetRepositoryModule.Companion.DEFAULT_WIDGETS
-import com.android.systemui.communal.shared.CommunalWidgetHost
+import com.android.systemui.communal.widgets.CommunalWidgetHost
+import com.android.systemui.communal.widgets.CommunalWidgetModule.Companion.DEFAULT_WIDGETS
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.log.LogBuffer
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetCategories.kt b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetCategories.kt
new file mode 100644
index 000000000000..03f54c8b25d7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetCategories.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 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.communal.data.model
+
+import android.appwidget.AppWidgetProviderInfo
+
+/**
+ * The widget categories to display on communal hub (where categories is a bitfield with values that
+ * match those in {@link AppWidgetProviderInfo}).
+ */
+@JvmInline
+value class CommunalWidgetCategories(
+ // The default is keyguard widgets.
+ val categories: Int = AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD
+) {
+ fun contains(category: Int) = (categories and category) == category
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
index 201be51b873c..e2fed6d0ea20 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
@@ -21,8 +21,8 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.dagger.CommunalTableLog
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.logDiffsForTable
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import com.android.systemui.media.controls.shared.model.MediaData
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
index 9e68ff88622c..f4a3bcb7a0fa 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
@@ -22,7 +22,6 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.scene.data.repository.SceneContainerRepository
import com.android.systemui.scene.shared.flag.SceneContainerFlags
-import com.android.systemui.scene.shared.model.SceneKey
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -33,14 +32,10 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
/** Encapsulates the state of communal mode. */
interface CommunalRepository {
- /** Whether the communal hub is showing. */
- val isCommunalHubShowing: Flow<Boolean>
-
/**
* Target scene as requested by the underlying [SceneTransitionLayout] or through
* [setDesiredScene].
@@ -99,11 +94,4 @@ constructor(
override fun setTransitionState(transitionState: Flow<ObservableCommunalTransitionState>?) {
_transitionState.value = transitionState
}
-
- override val isCommunalHubShowing: Flow<Boolean> =
- if (sceneContainerFlags.isEnabled()) {
- sceneContainerRepository.currentScene.map { sceneKey -> sceneKey == SceneKey.Communal }
- } else {
- desiredScene.map { sceneKey -> sceneKey == CommunalSceneKey.Communal }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
index 201b04927ea3..7c65d21e2814 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
@@ -18,11 +18,13 @@ package com.android.systemui.communal.data.repository
import android.app.admin.DevicePolicyManager
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL
+import android.appwidget.AppWidgetProviderInfo
import android.content.IntentFilter
import android.content.pm.UserInfo
import com.android.systemui.Flags.communalHub
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.communal.data.model.CommunalEnabledState
+import com.android.systemui.communal.data.model.CommunalWidgetCategories
import com.android.systemui.communal.data.model.DisabledReason
import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_DEVICE_POLICY
import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_FLAG
@@ -48,6 +50,12 @@ import kotlinx.coroutines.flow.onStart
interface CommunalSettingsRepository {
/** A [CommunalEnabledState] for the specified user. */
fun getEnabledState(user: UserInfo): Flow<CommunalEnabledState>
+
+ /**
+ * A flow that reports the widget categories to show on the hub as selected by the user in
+ * Settings.
+ */
+ fun getWidgetCategories(user: UserInfo): Flow<CommunalWidgetCategories>
}
@SysUISingleton
@@ -89,6 +97,23 @@ constructor(
.flowOn(bgDispatcher)
}
+ override fun getWidgetCategories(user: UserInfo): Flow<CommunalWidgetCategories> =
+ secureSettings
+ .observerFlow(userId = user.id, names = arrayOf(GLANCEABLE_HUB_CONTENT_SETTING))
+ // Force an update
+ .onStart { emit(Unit) }
+ .map {
+ CommunalWidgetCategories(
+ // The default is to show only keyguard widgets.
+ secureSettings.getIntForUser(
+ GLANCEABLE_HUB_CONTENT_SETTING,
+ AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD,
+ user.id
+ )
+ )
+ }
+ .flowOn(bgDispatcher)
+
private fun getEnabledByUser(user: UserInfo): Flow<Boolean> =
secureSettings
.observerFlow(userId = user.id, names = arrayOf(GLANCEABLE_HUB_ENABLED))
@@ -114,6 +139,7 @@ constructor(
companion object {
const val GLANCEABLE_HUB_ENABLED = "glanceable_hub_enabled"
+ const val GLANCEABLE_HUB_CONTENT_SETTING = "glanceable_hub_content_setting"
private const val ENABLED_SETTING_DEFAULT = 1
}
}
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 e4c91957ee56..e395ca9bf314 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
@@ -18,13 +18,14 @@ package com.android.systemui.communal.data.repository
import android.appwidget.AppWidgetManager
import android.content.ComponentName
+import android.os.UserHandle
import androidx.annotation.WorkerThread
import com.android.systemui.communal.data.db.CommunalItemRank
import com.android.systemui.communal.data.db.CommunalWidgetDao
import com.android.systemui.communal.data.db.CommunalWidgetItem
-import com.android.systemui.communal.shared.CommunalWidgetHost
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.communal.widgets.CommunalAppWidgetHost
+import com.android.systemui.communal.widgets.CommunalWidgetHost
import com.android.systemui.communal.widgets.WidgetConfigurator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
@@ -50,6 +51,7 @@ interface CommunalWidgetRepository {
/** Add a widget at the specified position in the app widget service and the database. */
fun addWidget(
provider: ComponentName,
+ user: UserHandle,
priority: Int,
configurator: WidgetConfigurator? = null
) {}
@@ -99,11 +101,12 @@ constructor(
override fun addWidget(
provider: ComponentName,
+ user: UserHandle,
priority: Int,
configurator: WidgetConfigurator?
) {
bgScope.launch {
- val id = communalWidgetHost.allocateIdAndBindWidget(provider)
+ val id = communalWidgetHost.allocateIdAndBindWidget(provider, user)
if (id == null) {
logger.e("Failed to allocate widget id to ${provider.flattenToString()}")
return@launch
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt
index d7f163bad2e9..b502fb11d7ac 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt
@@ -17,75 +17,11 @@
package com.android.systemui.communal.data.repository
-import android.appwidget.AppWidgetManager
-import android.content.Context
-import android.content.res.Resources
-import android.os.Looper
-import com.android.systemui.communal.shared.CommunalWidgetHost
-import com.android.systemui.communal.widgets.CommunalAppWidgetHost
-import com.android.systemui.communal.widgets.WidgetInteractionHandler
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.dagger.CommunalLog
-import com.android.systemui.res.R
import dagger.Binds
import dagger.Module
-import dagger.Provides
-import java.util.Optional
-import javax.inject.Named
-import kotlinx.coroutines.CoroutineScope
@Module
interface CommunalWidgetRepositoryModule {
- companion object {
- private const val APP_WIDGET_HOST_ID = 116
- const val DEFAULT_WIDGETS = "default_widgets"
-
- @SysUISingleton
- @Provides
- fun provideAppWidgetManager(@Application context: Context): Optional<AppWidgetManager> {
- return Optional.ofNullable(AppWidgetManager.getInstance(context))
- }
-
- @SysUISingleton
- @Provides
- fun provideCommunalAppWidgetHost(
- @Application context: Context,
- @Background backgroundScope: CoroutineScope,
- interactionHandler: WidgetInteractionHandler,
- @Main looper: Looper,
- @CommunalLog logBuffer: LogBuffer,
- ): CommunalAppWidgetHost {
- return CommunalAppWidgetHost(
- context,
- backgroundScope,
- APP_WIDGET_HOST_ID,
- interactionHandler,
- looper,
- logBuffer,
- )
- }
-
- @SysUISingleton
- @Provides
- fun provideCommunalWidgetHost(
- appWidgetManager: Optional<AppWidgetManager>,
- appWidgetHost: CommunalAppWidgetHost,
- @CommunalLog logBuffer: LogBuffer,
- ): CommunalWidgetHost {
- return CommunalWidgetHost(appWidgetManager, appWidgetHost, logBuffer)
- }
-
- @Provides
- @Named(DEFAULT_WIDGETS)
- fun provideDefaultWidgets(@Main resources: Resources): Array<String> {
- return resources.getStringArray(R.array.config_communalWidgetAllowlist)
- }
- }
-
@Binds
fun communalWidgetRepository(impl: CommunalWidgetRepositoryImpl): CommunalWidgetRepository
}
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 b4f4099af124..5d525413a919 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,6 +18,7 @@ package com.android.systemui.communal.domain.interactor
import android.app.smartspace.SmartspaceTarget
import android.content.ComponentName
+import android.os.UserHandle
import com.android.systemui.communal.data.repository.CommunalMediaRepository
import com.android.systemui.communal.data.repository.CommunalPrefsRepository
import com.android.systemui.communal.data.repository.CommunalRepository
@@ -28,6 +29,7 @@ import com.android.systemui.communal.shared.model.CommunalContentSize.FULL
import com.android.systemui.communal.shared.model.CommunalContentSize.HALF
import com.android.systemui.communal.shared.model.CommunalContentSize.THIRD
import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
import com.android.systemui.communal.widgets.CommunalAppWidgetHost
import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
@@ -41,6 +43,10 @@ import com.android.systemui.log.dagger.CommunalLog
import com.android.systemui.log.dagger.CommunalTableLog
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.logDiffsForTable
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.settings.UserTracker
import com.android.systemui.smartspace.data.repository.SmartspaceRepository
import com.android.systemui.util.kotlin.BooleanFlowOperators.and
import com.android.systemui.util.kotlin.BooleanFlowOperators.not
@@ -55,7 +61,9 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
@@ -74,9 +82,12 @@ constructor(
mediaRepository: CommunalMediaRepository,
smartspaceRepository: SmartspaceRepository,
keyguardInteractor: KeyguardInteractor,
- private val communalSettingsInteractor: CommunalSettingsInteractor,
+ communalSettingsInteractor: CommunalSettingsInteractor,
private val appWidgetHost: CommunalAppWidgetHost,
private val editWidgetsActivityStarter: EditWidgetsActivityStarter,
+ private val userTracker: UserTracker,
+ sceneInteractor: SceneInteractor,
+ sceneContainerFlags: SceneContainerFlags,
@CommunalLog logBuffer: LogBuffer,
@CommunalTableLog tableLogBuffer: TableLogBuffer,
) {
@@ -88,8 +99,7 @@ constructor(
val editModeOpen: StateFlow<Boolean> = _editModeOpen.asStateFlow()
/** Whether communal features are enabled. */
- val isCommunalEnabled: Boolean
- get() = communalSettingsInteractor.isCommunalEnabled.value
+ val isCommunalEnabled: StateFlow<Boolean> = communalSettingsInteractor.isCommunalEnabled
/** Whether communal features are enabled and available. */
val isCommunalAvailable: Flow<Boolean> =
@@ -119,8 +129,13 @@ constructor(
/**
* Target scene as requested by the underlying [SceneTransitionLayout] or through
* [onSceneChanged].
+ *
+ * If [isCommunalAvailable] is false, will return [CommunalSceneKey.Blank]
*/
- val desiredScene: StateFlow<CommunalSceneKey> = communalRepository.desiredScene
+ val desiredScene: Flow<CommunalSceneKey> =
+ communalRepository.desiredScene.combine(isCommunalAvailable) { scene, available ->
+ if (available) scene else CommunalSceneKey.Blank
+ }
/** Transition state of the hub mode. */
val transitionState: StateFlow<ObservableCommunalTransitionState> =
@@ -172,8 +187,14 @@ constructor(
*/
// TODO(b/323215860): rename to something more appropriate after cleaning up usages
val isCommunalShowing: Flow<Boolean> =
- communalRepository.desiredScene
- .map { it == CommunalSceneKey.Communal }
+ flow { emit(sceneContainerFlags.isEnabled()) }
+ .flatMapLatest { sceneContainerEnabled ->
+ if (sceneContainerEnabled) {
+ sceneInteractor.currentScene.map { it == SceneKey.Communal }
+ } else {
+ desiredScene.map { it == CommunalSceneKey.Communal }
+ }
+ }
.distinctUntilChanged()
.onEach { showing ->
logger.i({ "Communal is ${if (bool1) "showing" else "gone"}" }) { bool1 = showing }
@@ -231,9 +252,10 @@ constructor(
/** Add a widget at the specified position. */
fun addWidget(
componentName: ComponentName,
+ user: UserHandle,
priority: Int,
configurator: WidgetConfigurator?,
- ) = widgetRepository.addWidget(componentName, priority, configurator)
+ ) = widgetRepository.addWidget(componentName, user, priority, configurator)
/**
* Delete a widget by id. Called when user deletes a widget from the hub or a widget is
@@ -249,10 +271,16 @@ constructor(
fun updateWidgetOrder(widgetIdToPriorityMap: Map<Int, Int>) =
widgetRepository.updateWidgetOrder(widgetIdToPriorityMap)
+ /** All widgets present in db. */
+ val communalWidgets: Flow<List<CommunalWidgetContentModel>> =
+ isCommunalAvailable.flatMapLatest { available ->
+ if (!available) emptyFlow() else widgetRepository.communalWidgets
+ }
+
/** A list of widget content to be displayed in the communal hub. */
val widgetContent: Flow<List<CommunalContentModel.Widget>> =
widgetRepository.communalWidgets.map { widgets ->
- widgets.map Widget@{ widget ->
+ filterWidgetsByExistingUsers(widgets).map Widget@{ widget ->
return@Widget CommunalContentModel.Widget(
appWidgetId = widget.appWidgetId,
providerInfo = widget.providerInfo,
@@ -332,6 +360,19 @@ constructor(
return@combine ongoingContent
}
+ /**
+ * Filter and retain widgets associated with an existing user, safeguarding against displaying
+ * stale data following user deletion.
+ */
+ private fun filterWidgetsByExistingUsers(
+ list: List<CommunalWidgetContentModel>,
+ ): List<CommunalWidgetContentModel> {
+ val currentUserIds = userTracker.userProfiles.map { it.id }.toSet()
+ return list.filter { widget ->
+ currentUserIds.contains(widget.providerInfo.profile?.identifier)
+ }
+ }
+
companion object {
/**
* The user activity timeout which should be used when the communal hub is opened. A value
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
index 0b096ce67fc5..20f60b79c784 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
@@ -17,6 +17,7 @@
package com.android.systemui.communal.domain.interactor
import com.android.systemui.communal.data.model.CommunalEnabledState
+import com.android.systemui.communal.data.model.CommunalWidgetCategories
import com.android.systemui.communal.data.repository.CommunalSettingsRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
@@ -55,4 +56,16 @@ constructor(
.map { model -> model.enabled }
// Start this eagerly since the value is accessed synchronously in many places.
.stateIn(scope = bgScope, started = SharingStarted.Eagerly, initialValue = false)
+
+ /** What widget categories to show on the hub. */
+ val communalWidgetCategories: StateFlow<Int> =
+ userInteractor.selectedUserInfo
+ .flatMapLatest { user -> repository.getWidgetCategories(user) }
+ .map { categories -> categories.categories }
+ .stateIn(
+ scope = bgScope,
+ // Start this eagerly since the value can be accessed synchronously.
+ started = SharingStarted.Eagerly,
+ initialValue = CommunalWidgetCategories().categories
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
index 25dfc02a5d98..2b7db148582d 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
@@ -17,7 +17,6 @@
package com.android.systemui.communal.domain.interactor
import android.provider.Settings
-import com.android.systemui.communal.data.repository.CommunalRepository
import com.android.systemui.communal.data.repository.CommunalTutorialRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -51,7 +50,6 @@ constructor(
@Application private val scope: CoroutineScope,
private val communalTutorialRepository: CommunalTutorialRepository,
keyguardInteractor: KeyguardInteractor,
- private val communalRepository: CommunalRepository,
private val communalSettingsInteractor: CommunalSettingsInteractor,
communalInteractor: CommunalInteractor,
@CommunalTableLog tableLogBuffer: TableLogBuffer,
@@ -92,7 +90,7 @@ constructor(
if (tutorialSettingState == Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED) {
return@flatMapLatest flowOf(null)
}
- communalRepository.isCommunalHubShowing.map { isCommunalShowing ->
+ communalInteractor.isCommunalShowing.map { isCommunalShowing ->
nextStateAfterTransition(
tutorialSettingState,
isCommunalShowing,
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalTutorialIndicatorSection.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalTutorialIndicatorSection.kt
index 2d9dd50b6d11..1a323a75f8e7 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalTutorialIndicatorSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalTutorialIndicatorSection.kt
@@ -47,7 +47,7 @@ constructor(
private var communalTutorialIndicatorHandle: DisposableHandle? = null
override fun addViews(constraintLayout: ConstraintLayout) {
- if (!communalInteractor.isCommunalEnabled) {
+ if (!communalInteractor.isCommunalEnabled.value) {
return
}
val padding =
@@ -79,7 +79,7 @@ constructor(
}
override fun bindData(constraintLayout: ConstraintLayout) {
- if (!communalInteractor.isCommunalEnabled) {
+ if (!communalInteractor.isCommunalEnabled.value) {
return
}
communalTutorialIndicatorHandle =
@@ -90,7 +90,7 @@ constructor(
}
override fun applyConstraints(constraintSet: ConstraintSet) {
- if (!communalInteractor.isCommunalEnabled) {
+ if (!communalInteractor.isCommunalEnabled.value) {
return
}
val tutorialIndicatorId = R.id.communal_tutorial_indicator
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index a87ebd7e8e9f..3ec9a268f80c 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -17,12 +17,13 @@
package com.android.systemui.communal.ui.viewmodel
import android.content.ComponentName
+import android.os.UserHandle
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
import com.android.systemui.communal.widgets.WidgetConfigurator
-import com.android.systemui.media.controls.ui.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaHost
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -33,7 +34,7 @@ abstract class BaseCommunalViewModel(
private val communalInteractor: CommunalInteractor,
val mediaHost: MediaHost,
) {
- val currentScene: StateFlow<CommunalSceneKey> = communalInteractor.desiredScene
+ val currentScene: Flow<CommunalSceneKey> = communalInteractor.desiredScene
/** Whether widgets are currently being re-ordered. */
open val reorderingWidgets: StateFlow<Boolean> = MutableStateFlow(false)
@@ -62,10 +63,11 @@ abstract class BaseCommunalViewModel(
*/
open fun onAddWidget(
componentName: ComponentName,
+ user: UserHandle,
priority: Int,
configurator: WidgetConfigurator? = null
) {
- communalInteractor.addWidget(componentName, priority, configurator)
+ communalInteractor.addWidget(componentName, user, priority, configurator)
}
/** A list of all the communal content to be displayed in the communal hub. */
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
index 0b355cc10db5..bfe751af7154 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
@@ -18,13 +18,14 @@ package com.android.systemui.communal.ui.viewmodel
import com.android.internal.logging.UiEventLogger
import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.log.CommunalUiEvent
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
import com.android.systemui.log.dagger.CommunalLog
-import com.android.systemui.media.controls.ui.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.media.dagger.MediaModule
import javax.inject.Inject
import javax.inject.Named
@@ -40,6 +41,7 @@ class CommunalEditModeViewModel
@Inject
constructor(
private val communalInteractor: CommunalInteractor,
+ private val communalSettingsInteractor: CommunalSettingsInteractor,
@Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost,
private val uiEventLogger: UiEventLogger,
@CommunalLog logBuffer: LogBuffer,
@@ -84,6 +86,10 @@ constructor(
uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_CANCEL)
}
+ /** Returns the widget categories to show on communal hub. */
+ val getCommunalWidgetCategories: Int
+ get() = communalSettingsInteractor.communalWidgetCategories.value
+
/** Sets whether edit mode is currently open */
fun setEditModeOpen(isOpen: Boolean) = communalInteractor.setEditModeOpen(isOpen)
}
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 febfd4c95db1..fc9a7df4744d 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
@@ -24,9 +24,9 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
import com.android.systemui.log.dagger.CommunalLog
-import com.android.systemui.media.controls.ui.MediaHierarchyManager
-import com.android.systemui.media.controls.ui.MediaHost
-import com.android.systemui.media.controls.ui.MediaHostState
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.media.dagger.MediaModule
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.util.kotlin.BooleanFlowOperators.not
diff --git a/packages/SystemUI/src/com/android/systemui/communal/util/WidgetPickerIntentUtils.kt b/packages/SystemUI/src/com/android/systemui/communal/util/WidgetPickerIntentUtils.kt
new file mode 100644
index 000000000000..94ec8f300da7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/util/WidgetPickerIntentUtils.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 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.communal.util
+
+import android.content.ComponentName
+import android.content.Intent
+import android.os.UserHandle
+
+/** Provides util functions for the intent of adding widgets from the Communal widget picker. */
+object WidgetPickerIntentUtils {
+ fun getWidgetExtraFromIntent(intent: Intent) =
+ WidgetExtra(
+ intent.getParcelableExtra(Intent.EXTRA_COMPONENT_NAME, ComponentName::class.java),
+ intent.getParcelableExtra(Intent.EXTRA_USER, UserHandle::class.java)
+ )
+ data class WidgetExtra(val componentName: ComponentName?, val user: UserHandle?)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt
index 4ddd7681dd98..8390d62b23db 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostStartable.kt
@@ -18,11 +18,14 @@ package com.android.systemui.communal.widgets
import com.android.systemui.CoreStartable
import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.settings.UserTracker
import com.android.systemui.util.kotlin.BooleanFlowOperators.or
import com.android.systemui.util.kotlin.pairwise
+import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
@@ -37,6 +40,7 @@ class CommunalAppWidgetHostStartable
constructor(
private val appWidgetHost: CommunalAppWidgetHost,
private val communalInteractor: CommunalInteractor,
+ private val userTracker: UserTracker,
@Background private val bgScope: CoroutineScope,
@Main private val uiDispatcher: CoroutineDispatcher
) : CoreStartable {
@@ -47,6 +51,14 @@ constructor(
.pairwise(false)
.filter { (previous, new) -> previous != new }
.onEach { (_, shouldListen) -> updateAppWidgetHostActive(shouldListen) }
+ .sample(communalInteractor.communalWidgets, ::Pair)
+ .onEach { (withPrev, widgets) ->
+ val (_, isActive) = withPrev
+ // The validation is performed once the hub becomes active.
+ if (isActive) {
+ validateWidgetsAndDeleteOrphaned(widgets)
+ }
+ }
.launchIn(bgScope)
appWidgetHost.appWidgetIdToRemove
@@ -63,4 +75,15 @@ constructor(
appWidgetHost.stopListening()
}
}
+
+ /**
+ * Ensure the existence of all associated users for widgets, and remove widgets belonging to
+ * users who have been deleted.
+ */
+ private fun validateWidgetsAndDeleteOrphaned(widgets: List<CommunalWidgetContentModel>) {
+ val currentUserIds = userTracker.userProfiles.map { it.id }.toSet()
+ widgets
+ .filter { widget -> !currentUserIds.contains(widget.providerInfo.profile?.identifier) }
+ .onEach { widget -> communalInteractor.deleteWidget(id = widget.appWidgetId) }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalWidgetHost.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalWidgetHost.kt
index 965c1e873279..080dbedcb350 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalWidgetHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalWidgetHost.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,23 +14,24 @@
* limitations under the License.
*/
-package com.android.systemui.communal.shared
+package com.android.systemui.communal.widgets
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProviderInfo
import android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_CONFIGURATION_OPTIONAL
import android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE
import android.content.ComponentName
-import com.android.systemui.communal.widgets.CommunalAppWidgetHost
+import android.os.UserHandle
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
import com.android.systemui.log.dagger.CommunalLog
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.kotlin.getOrNull
import java.util.Optional
import javax.inject.Inject
/**
- * Widget host that interacts with AppWidget service and host to manage and provide info for widgets
+ * Widget host that interacts with AppWidget service and host to bind and provide info for widgets
* shown in the glanceable hub.
*/
class CommunalWidgetHost
@@ -38,6 +39,7 @@ class CommunalWidgetHost
constructor(
private val appWidgetManager: Optional<AppWidgetManager>,
private val appWidgetHost: CommunalAppWidgetHost,
+ private val selectedUserInteractor: SelectedUserInteractor,
@CommunalLog logBuffer: LogBuffer,
) {
companion object {
@@ -57,13 +59,21 @@ constructor(
private val logger = Logger(logBuffer, TAG)
/**
- * Allocate an app widget id and binds the widget.
+ * Allocate an app widget id and binds the widget with the provider and associated user.
*
+ * @param provider The component name of the provider.
+ * @param user User handle in which the provider resides. Default value is the current user.
* @return widgetId if binding is successful; otherwise return null
*/
- fun allocateIdAndBindWidget(provider: ComponentName): Int? {
+ fun allocateIdAndBindWidget(provider: ComponentName, user: UserHandle? = null): Int? {
val id = appWidgetHost.allocateAppWidgetId()
- if (bindWidget(id, provider)) {
+ if (
+ bindWidget(
+ widgetId = id,
+ user = user ?: UserHandle(selectedUserInteractor.getSelectedUserId()),
+ provider = provider
+ )
+ ) {
logger.d("Successfully bound the widget $provider")
return id
}
@@ -72,9 +82,11 @@ constructor(
return null
}
- private fun bindWidget(widgetId: Int, provider: ComponentName): Boolean {
+ private fun bindWidget(widgetId: Int, user: UserHandle, provider: ComponentName): Boolean {
if (appWidgetManager.isPresent) {
- return appWidgetManager.get().bindAppWidgetIdIfAllowed(widgetId, provider)
+ return appWidgetManager
+ .get()
+ .bindAppWidgetIdIfAllowed(widgetId, user, provider, /* options */ null)
}
return false
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalWidgetModule.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalWidgetModule.kt
new file mode 100644
index 000000000000..60fb8d4840cb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalWidgetModule.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2024 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.communal.widgets
+
+import android.appwidget.AppWidgetManager
+import android.content.Context
+import android.content.res.Resources
+import android.os.Looper
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.dagger.CommunalLog
+import com.android.systemui.res.R
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import dagger.Module
+import dagger.Provides
+import java.util.Optional
+import javax.inject.Named
+import kotlinx.coroutines.CoroutineScope
+
+@Module
+interface CommunalWidgetModule {
+ companion object {
+ private const val APP_WIDGET_HOST_ID = 116
+ const val DEFAULT_WIDGETS = "default_widgets"
+
+ @SysUISingleton
+ @Provides
+ fun provideAppWidgetManager(@Application context: Context): Optional<AppWidgetManager> {
+ return Optional.ofNullable(AppWidgetManager.getInstance(context))
+ }
+
+ @SysUISingleton
+ @Provides
+ fun provideCommunalAppWidgetHost(
+ @Application context: Context,
+ @Background backgroundScope: CoroutineScope,
+ interactionHandler: WidgetInteractionHandler,
+ @Main looper: Looper,
+ @CommunalLog logBuffer: LogBuffer,
+ ): CommunalAppWidgetHost {
+ return CommunalAppWidgetHost(
+ context,
+ backgroundScope,
+ APP_WIDGET_HOST_ID,
+ interactionHandler,
+ looper,
+ logBuffer,
+ )
+ }
+
+ @SysUISingleton
+ @Provides
+ fun provideCommunalWidgetHost(
+ appWidgetManager: Optional<AppWidgetManager>,
+ appWidgetHost: CommunalAppWidgetHost,
+ selectedUserInteractor: SelectedUserInteractor,
+ @CommunalLog logBuffer: LogBuffer,
+ ): CommunalWidgetHost {
+ return CommunalWidgetHost(
+ appWidgetManager,
+ appWidgetHost,
+ selectedUserInteractor,
+ logBuffer
+ )
+ }
+
+ @Provides
+ @Named(DEFAULT_WIDGETS)
+ fun provideDefaultWidgets(@Main resources: Resources): Array<String> {
+ return resources.getStringArray(R.array.config_communalWidgetAllowlist)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
index 92e8153840eb..a5a390d7683b 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
@@ -16,7 +16,7 @@
package com.android.systemui.communal.widgets
-import android.content.ComponentName
+import android.appwidget.AppWidgetManager
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
@@ -31,6 +31,7 @@ import com.android.internal.logging.UiEventLogger
import com.android.systemui.communal.shared.log.CommunalUiEvent
import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
+import com.android.systemui.communal.util.WidgetPickerIntentUtils.getWidgetExtraFromIntent
import com.android.systemui.compose.ComposeFacade.setCommunalEditWidgetActivityContent
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
@@ -51,7 +52,6 @@ constructor(
companion object {
private const val EXTRA_IS_PENDING_WIDGET_DRAG = "is_pending_widget_drag"
private const val EXTRA_DESIRED_WIDGET_WIDTH = "desired_widget_width"
-
private const val EXTRA_DESIRED_WIDGET_HEIGHT = "desired_widget_height"
private const val TAG = "EditWidgetsActivity"
@@ -75,13 +75,17 @@ constructor(
// target in the communal grid will receive the widget to be added (if
// the user drops it over).
if (!isPendingWidgetDrag) {
- intent
- .getParcelableExtra(
- Intent.EXTRA_COMPONENT_NAME,
- ComponentName::class.java
+ val (componentName, user) = getWidgetExtraFromIntent(intent)
+ if (componentName != null && user != null) {
+ communalViewModel.onAddWidget(
+ componentName,
+ user,
+ 0,
+ widgetConfigurator
)
- ?.let { communalViewModel.onAddWidget(it, 0, widgetConfigurator) }
- ?: run { Log.w(TAG, "No AppWidgetProviderInfo found in result.") }
+ } else {
+ run { Log.w(TAG, "No AppWidgetProviderInfo found in result.") }
+ }
}
}
?: run { Log.w(TAG, "No data in result.") }
@@ -134,6 +138,10 @@ constructor(
R.dimen.communal_widget_picker_desired_height
)
)
+ putExtra(
+ AppWidgetManager.EXTRA_CATEGORY_FILTER,
+ communalViewModel.getCommunalWidgetCategories
+ )
}
)
} catch (e: Exception) {
diff --git a/packages/SystemUI/src/com/android/systemui/complication/DreamMediaEntryComplication.java b/packages/SystemUI/src/com/android/systemui/complication/DreamMediaEntryComplication.java
index 6a7278529a54..bdefc4d2cbba 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/DreamMediaEntryComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/DreamMediaEntryComplication.java
@@ -28,7 +28,7 @@ import com.android.systemui.ActivityIntentHelper;
import com.android.systemui.complication.dagger.DreamMediaEntryComplicationComponent;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.media.controls.ui.MediaCarouselController;
+import com.android.systemui.media.controls.ui.controller.MediaCarouselController;
import com.android.systemui.media.dream.MediaDreamComplication;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
diff --git a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt b/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt
index 8178adef49b2..a0aaa906802a 100644
--- a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt
+++ b/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt
@@ -79,7 +79,7 @@ interface BaseComposeFacade {
fun setVolumePanelActivityContent(
activity: ComponentActivity,
viewModel: VolumePanelViewModel,
- onDismissAnimationFinished: () -> Unit,
+ onDismiss: () -> Unit,
)
/** Create a [View] to represent [viewModel] on screen. */
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
index 0f038e10dd4e..bc07b95c5d7f 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
@@ -36,6 +36,7 @@ import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.controls.ControlsMetricsLogger
import com.android.systemui.controls.settings.ControlsSettingsRepository
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.VibratorHelper
@@ -47,16 +48,16 @@ import javax.inject.Inject
@SysUISingleton
class ControlActionCoordinatorImpl @Inject constructor(
- private val context: Context,
- private val bgExecutor: DelayableExecutor,
- @Main private val uiExecutor: DelayableExecutor,
- private val activityStarter: ActivityStarter,
- private val broadcastSender: BroadcastSender,
- private val keyguardStateController: KeyguardStateController,
- private val taskViewFactory: Optional<TaskViewFactory>,
- private val controlsMetricsLogger: ControlsMetricsLogger,
- private val vibrator: VibratorHelper,
- private val controlsSettingsRepository: ControlsSettingsRepository,
+ private val context: Context,
+ @Background private val bgExecutor: DelayableExecutor,
+ @Main private val uiExecutor: DelayableExecutor,
+ private val activityStarter: ActivityStarter,
+ private val broadcastSender: BroadcastSender,
+ private val keyguardStateController: KeyguardStateController,
+ private val taskViewFactory: Optional<TaskViewFactory>,
+ private val controlsMetricsLogger: ControlsMetricsLogger,
+ private val vibrator: VibratorHelper,
+ private val controlsSettingsRepository: ControlsSettingsRepository,
) : ControlActionCoordinator {
private var dialog: Dialog? = null
private var pendingAction: Action? = null
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index e8931770b15e..1157d97f2f2e 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -126,6 +126,7 @@ import com.android.systemui.statusbar.pipeline.dagger.StatusBarPipelineModule;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.PolicyModule;
+import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.statusbar.policy.dagger.SmartRepliesInflationModule;
import com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule;
@@ -358,6 +359,7 @@ public abstract class SystemUIModule {
VisualInterruptionDecisionProvider visualInterruptionDecisionProvider,
ZenModeController zenModeController,
NotificationLockscreenUserManager notifUserManager,
+ SensitiveNotificationProtectionController sensitiveNotificationProtectionController,
CommonNotifCollection notifCollection,
NotifPipeline notifPipeline,
SysUiState sysUiState,
@@ -376,6 +378,7 @@ public abstract class SystemUIModule {
visualInterruptionDecisionProvider,
zenModeController,
notifUserManager,
+ sensitiveNotificationProtectionController,
notifCollection,
notifPipeline,
sysUiState,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/NotifInflation.kt b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/NotifInflation.kt
new file mode 100644
index 000000000000..231fb2dd16c8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/NotifInflation.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 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.dagger.qualifiers
+
+import javax.inject.Qualifier
+
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class NotifInflation
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractor.kt
index 55d2bfcc8911..6bfe8d91b5fc 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractor.kt
@@ -29,6 +29,7 @@ import com.android.systemui.deviceentry.shared.model.FingerprintMessage
import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus
import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
import com.android.systemui.res.R
+import com.android.systemui.util.kotlin.Utils.Companion.toTriple
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -41,9 +42,8 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
/**
- * BiometricMessage business logic. Filters biometric error/acquired/fail/success events for
- * authentication events that should never surface a message to the user at the current device
- * state.
+ * BiometricMessage business logic. Filters biometric error/fail/success events for authentication
+ * events that should never surface a message to the user at the current device state.
*/
@ExperimentalCoroutinesApi
@SysUISingleton
@@ -54,7 +54,8 @@ constructor(
fingerprintAuthInteractor: DeviceEntryFingerprintAuthInteractor,
fingerprintPropertyInteractor: FingerprintPropertyInteractor,
faceAuthInteractor: DeviceEntryFaceAuthInteractor,
- biometricSettingsInteractor: DeviceEntryBiometricSettingsInteractor,
+ private val biometricSettingsInteractor: DeviceEntryBiometricSettingsInteractor,
+ faceHelpMessageDeferralInteractor: FaceHelpMessageDeferralInteractor,
) {
private val faceHelp: Flow<HelpFaceAuthenticationStatus> =
faceAuthInteractor.authenticationStatus.filterIsInstance<HelpFaceAuthenticationStatus>()
@@ -130,19 +131,24 @@ constructor(
)
private val faceHelpMessage: Flow<FaceMessage> =
- biometricSettingsInteractor.fingerprintAndFaceEnrolledAndEnabled
- .flatMapLatest { fingerprintAndFaceEnrolledAndEnabled ->
+ faceHelp
+ .filterNot {
+ // Message deferred to potentially show at face timeout error instead
+ faceHelpMessageDeferralInteractor.shouldDefer(it.msgId)
+ }
+ .sample(biometricSettingsInteractor.fingerprintAndFaceEnrolledAndEnabled, ::Pair)
+ .filter { (faceAuthHelpStatus, fingerprintAndFaceEnrolledAndEnabled) ->
if (fingerprintAndFaceEnrolledAndEnabled) {
- faceHelp.filter { faceAuthHelpStatus ->
- coExFaceAcquisitionMsgIdsToShow.contains(faceAuthHelpStatus.msgId)
- }
+ // Show only some face help messages if fingerprint is also enrolled
+ coExFaceAcquisitionMsgIdsToShow.contains(faceAuthHelpStatus.msgId)
} else {
- faceHelp
+ // Show all face help messages if only face is enrolled
+ true
}
}
- .sample(biometricSettingsInteractor.faceAuthCurrentlyAllowed, ::Pair)
- .filter { (_, faceAuthCurrentlyAllowed) -> faceAuthCurrentlyAllowed }
- .map { (status, _) -> FaceMessage(status.msg) }
+ .sample(biometricSettingsInteractor.faceAuthCurrentlyAllowed, ::toTriple)
+ .filter { (_, _, faceAuthCurrentlyAllowed) -> faceAuthCurrentlyAllowed }
+ .map { (status, _, _) -> FaceMessage(status.msg) }
private val faceFailureMessage: Flow<FaceMessage> =
faceFailure
@@ -159,12 +165,18 @@ constructor(
}
.map { (status, _) ->
when {
- status.isTimeoutError() -> FaceTimeoutMessage(status.msg)
+ status.isTimeoutError() -> {
+ val deferredMessage = faceHelpMessageDeferralInteractor.getDeferredMessage()
+ if (deferredMessage != null) {
+ FaceMessage(deferredMessage.toString())
+ } else {
+ FaceTimeoutMessage(status.msg)
+ }
+ }
else -> FaceMessage(status.msg)
}
}
- // TODO(b/317215391): support showing face acquired messages on timeout + face lockout errors
val faceMessage: Flow<FaceMessage> =
merge(
faceHelpMessage,
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricSettingsInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricSettingsInteractor.kt
index 4515fcb545b3..96171aa6566e 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricSettingsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricSettingsInteractor.kt
@@ -33,6 +33,7 @@ constructor(
) {
val fingerprintAuthCurrentlyAllowed: Flow<Boolean> =
repository.isFingerprintAuthCurrentlyAllowed
+ val faceAuthEnrolledAndEnabled: Flow<Boolean> = repository.isFaceAuthEnrolledAndEnabled
val faceAuthCurrentlyAllowed: Flow<Boolean> = repository.isFaceAuthCurrentlyAllowed
/** Whether both fingerprint and face are enrolled and enabled for device entry. */
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt
new file mode 100644
index 000000000000..fd6fbc9610f4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2024 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.deviceentry.domain.interactor
+
+import android.hardware.face.FaceManager
+import com.android.systemui.biometrics.FaceHelpMessageDeferralFactory
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
+import com.android.systemui.deviceentry.shared.model.AcquiredFaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.filterIsInstance
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.launch
+
+/**
+ * FaceHelpMessageDeferral business logic. Processes face acquired and face help authentication
+ * events to determine whether a face auth event should be displayed to the user immediately or when
+ * a [FaceManager.FACE_ERROR_TIMEOUT] is received.
+ */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class FaceHelpMessageDeferralInteractor
+@Inject
+constructor(
+ @Application private val scope: CoroutineScope,
+ faceAuthInteractor: DeviceEntryFaceAuthInteractor,
+ private val biometricSettingsInteractor: DeviceEntryBiometricSettingsInteractor,
+ faceHelpMessageDeferralFactory: FaceHelpMessageDeferralFactory,
+) {
+ private val faceHelpMessageDeferral = faceHelpMessageDeferralFactory.create()
+ private val faceAcquired: Flow<AcquiredFaceAuthenticationStatus> =
+ faceAuthInteractor.authenticationStatus.filterIsInstance<AcquiredFaceAuthenticationStatus>()
+ private val faceHelp: Flow<HelpFaceAuthenticationStatus> =
+ faceAuthInteractor.authenticationStatus.filterIsInstance<HelpFaceAuthenticationStatus>()
+
+ init {
+ if (DeviceEntryUdfpsRefactor.isEnabled) {
+ startUpdatingFaceHelpMessageDeferral()
+ }
+ }
+
+ /**
+ * If the given [HelpFaceAuthenticationStatus] msgId should be deferred to
+ * [FaceManager.FACE_ERROR_TIMEOUT].
+ */
+ fun shouldDefer(msgId: Int): Boolean {
+ return faceHelpMessageDeferral.shouldDefer(msgId)
+ }
+
+ /**
+ * Message that was deferred to show at [FaceManager.FACE_ERROR_TIMEOUT], if any. Returns null
+ * if there are currently no valid deferred messages.
+ */
+ fun getDeferredMessage(): CharSequence? {
+ return faceHelpMessageDeferral.getDeferredMessage()
+ }
+
+ private fun startUpdatingFaceHelpMessageDeferral() {
+ scope.launch {
+ biometricSettingsInteractor.faceAuthEnrolledAndEnabled
+ .flatMapLatest { faceEnrolledAndEnabled ->
+ if (faceEnrolledAndEnabled) {
+ faceAcquired
+ } else {
+ emptyFlow()
+ }
+ }
+ .collect {
+ if (it.acquiredInfo == FaceManager.FACE_ACQUIRED_START) {
+ faceHelpMessageDeferral.reset()
+ }
+ faceHelpMessageDeferral.processFrame(it.acquiredInfo)
+ }
+ }
+
+ scope.launch {
+ biometricSettingsInteractor.faceAuthEnrolledAndEnabled
+ .flatMapLatest { faceEnrolledAndEnabled ->
+ if (faceEnrolledAndEnabled) {
+ faceHelp
+ } else {
+ emptyFlow()
+ }
+ }
+ .collect { helpAuthenticationStatus ->
+ helpAuthenticationStatus.msg?.let { msg ->
+ faceHelpMessageDeferral.updateMessage(helpAuthenticationStatus.msgId, msg)
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/BiometricMessageModels.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/BiometricMessageModels.kt
index 118215c6ba15..59c3f7f8aded 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/BiometricMessageModels.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/model/BiometricMessageModels.kt
@@ -27,6 +27,7 @@ sealed class BiometricMessage(
/** Face biometric message */
open class FaceMessage(faceMessage: String?) : BiometricMessage(faceMessage)
+/** Face timeout message. */
data class FaceTimeoutMessage(
private val faceTimeoutMessage: String?,
) : FaceMessage(faceTimeoutMessage)
diff --git a/packages/SystemUI/src/com/android/systemui/display/DisplayExtensions.kt b/packages/SystemUI/src/com/android/systemui/display/DisplayExtensions.kt
new file mode 100644
index 000000000000..0482bd8730f9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/display/DisplayExtensions.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 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.display
+
+import android.graphics.Rect
+import android.view.Display
+import android.view.DisplayInfo
+
+val Display.naturalBounds: Rect
+ get() {
+ val outDisplayInfo = DisplayInfo()
+ getDisplayInfo(outDisplayInfo)
+ return Rect(
+ /* left = */ 0,
+ /* top = */ 0,
+ /* right = */ outDisplayInfo.naturalWidth,
+ /* bottom = */ outDisplayInfo.naturalHeight
+ )
+ }
+
+val Display.naturalWidth: Int
+ get() {
+ val outDisplayInfo = DisplayInfo()
+ getDisplayInfo(outDisplayInfo)
+ return outDisplayInfo.naturalWidth
+ }
+
+val Display.naturalHeight: Int
+ get() {
+ val outDisplayInfo = DisplayInfo()
+ getDisplayInfo(outDisplayInfo)
+ return outDisplayInfo.naturalHeight
+ }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
index 557ad132bc9f..b97bace9584f 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
@@ -33,20 +33,15 @@ import com.android.systemui.complication.ComplicationLayoutParams.POSITION_BOTTO
import com.android.systemui.complication.ComplicationLayoutParams.POSITION_TOP
import com.android.systemui.complication.ComplicationLayoutParams.Position
import com.android.systemui.dreams.dagger.DreamOverlayModule
-import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel
+import com.android.systemui.dreams.ui.viewmodel.DreamOverlayViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
import com.android.systemui.log.dagger.DreamLog
-import com.android.systemui.res.R
import com.android.systemui.statusbar.BlurUtils
import com.android.systemui.statusbar.CrossFadeHelper
-import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
import javax.inject.Inject
import javax.inject.Named
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.launch
/** Controller for dream overlay animations. */
@@ -58,8 +53,7 @@ constructor(
private val mStatusBarViewController: DreamOverlayStatusBarViewController,
private val mOverlayStateController: DreamOverlayStateController,
@Named(DreamOverlayModule.DREAM_BLUR_RADIUS) private val mDreamBlurRadius: Int,
- private val transitionViewModel: DreamingToLockscreenTransitionViewModel,
- private val configController: ConfigurationController,
+ private val dreamOverlayViewModel: DreamOverlayViewModel,
@Named(DreamOverlayModule.DREAM_IN_BLUR_ANIMATION_DURATION)
private val mDreamInBlurAnimDurationMs: Long,
@Named(DreamOverlayModule.DREAM_IN_COMPLICATIONS_ANIMATION_DURATION)
@@ -91,59 +85,45 @@ constructor(
this.view = view
view.repeatWhenAttached {
- val configurationBasedDimensions = MutableStateFlow(loadFromResources(view))
- val configCallback =
- object : ConfigurationListener {
- override fun onDensityOrFontScaleChanged() {
- configurationBasedDimensions.value = loadFromResources(view)
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ launch {
+ dreamOverlayViewModel.dreamOverlayTranslationY.collect { px ->
+ ComplicationLayoutParams.iteratePositions(
+ { position: Int -> setElementsTranslationYAtPosition(px, position) },
+ POSITION_TOP or POSITION_BOTTOM
+ )
}
}
- configController.addCallback(configCallback)
-
- try {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- /* Translation animations, when moving from DREAMING->LOCKSCREEN state */
- launch {
- configurationBasedDimensions
- .flatMapLatest {
- transitionViewModel.dreamOverlayTranslationY(it.translationYPx)
- }
- .collect { px ->
- ComplicationLayoutParams.iteratePositions(
- { position: Int ->
- setElementsTranslationYAtPosition(px, position)
- },
- POSITION_TOP or POSITION_BOTTOM
- )
- }
+ launch {
+ dreamOverlayViewModel.dreamOverlayTranslationX.collect { px ->
+ ComplicationLayoutParams.iteratePositions(
+ { position: Int -> setElementsTranslationXAtPosition(px, position) },
+ POSITION_TOP or POSITION_BOTTOM
+ )
}
+ }
- /* Alpha animations, when moving from DREAMING->LOCKSCREEN state */
- launch {
- transitionViewModel.dreamOverlayAlpha.collect { alpha ->
- ComplicationLayoutParams.iteratePositions(
- { position: Int ->
- setElementsAlphaAtPosition(
- alpha = alpha,
- position = position,
- fadingOut = true,
- )
- },
- POSITION_TOP or POSITION_BOTTOM
- )
- }
+ launch {
+ dreamOverlayViewModel.dreamOverlayAlpha.collect { alpha ->
+ ComplicationLayoutParams.iteratePositions(
+ { position: Int ->
+ setElementsAlphaAtPosition(
+ alpha = alpha,
+ position = position,
+ fadingOut = true,
+ )
+ },
+ POSITION_TOP or POSITION_BOTTOM
+ )
}
+ }
- launch {
- transitionViewModel.transitionEnded.collect { _ ->
- mOverlayStateController.setExitAnimationsRunning(false)
- }
+ launch {
+ dreamOverlayViewModel.transitionEnded.collect { _ ->
+ mOverlayStateController.setExitAnimationsRunning(false)
}
}
- } finally {
- // Ensure the callback is removed when cancellation happens
- configController.removeCallback(configCallback)
}
}
}
@@ -373,14 +353,10 @@ constructor(
}
}
- private fun loadFromResources(view: View): ConfigurationBasedDimensions {
- return ConfigurationBasedDimensions(
- translationYPx =
- view.resources.getDimensionPixelSize(R.dimen.dream_overlay_exit_y_offset),
- )
+ /** Sets x translation of complications at the specified position. */
+ private fun setElementsTranslationXAtPosition(translationX: Float, position: Int) {
+ mComplicationHostViewController.getViewsAtPosition(position).forEach { v ->
+ v.translationX = translationX
+ }
}
-
- private data class ConfigurationBasedDimensions(
- val translationYPx: Int,
- )
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamOverlayViewModel.kt b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamOverlayViewModel.kt
new file mode 100644
index 000000000000..bd99f4b8230e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamOverlayViewModel.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2024 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.dreams.ui.viewmodel
+
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.ui.viewmodel.DreamingToGlanceableHubTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToDreamingTransitionViewModel
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.merge
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class DreamOverlayViewModel
+@Inject
+constructor(
+ configurationInteractor: ConfigurationInteractor,
+ toGlanceableHubTransitionViewModel: DreamingToGlanceableHubTransitionViewModel,
+ fromGlanceableHubTransitionInteractor: GlanceableHubToDreamingTransitionViewModel,
+ private val toLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel,
+) {
+
+ val dreamOverlayTranslationX: Flow<Float> =
+ merge(
+ toGlanceableHubTransitionViewModel.dreamOverlayTranslationX,
+ fromGlanceableHubTransitionInteractor.dreamOverlayTranslationX,
+ )
+
+ val dreamOverlayTranslationY: Flow<Float> =
+ configurationInteractor
+ .dimensionPixelSize(R.dimen.dream_overlay_exit_y_offset)
+ .flatMapLatest { px: Int ->
+ toLockscreenTransitionViewModel.dreamOverlayTranslationY(px)
+ }
+
+ val dreamOverlayAlpha: Flow<Float> =
+ merge(
+ toLockscreenTransitionViewModel.dreamOverlayAlpha,
+ toGlanceableHubTransitionViewModel.dreamOverlayAlpha,
+ fromGlanceableHubTransitionInteractor.dreamOverlayAlpha,
+ )
+
+ val transitionEnded = toLockscreenTransitionViewModel.transitionEnded
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt b/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt
index 7150d69e130d..9876fe4482c0 100644
--- a/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt
@@ -222,13 +222,18 @@ constructor(
val buffers = dumpManager.getLogBuffers()
val tableBuffers = dumpManager.getTableLogBuffers()
- targets.forEach { target ->
- findTargetInCollection(target, dumpables, buffers, tableBuffers)?.dump(pw, args)
- }
+ val matches =
+ if (args.matchAll) {
+ findAllMatchesInCollection(targets, dumpables, buffers, tableBuffers)
+ } else {
+ findBestMatchesInCollection(targets, dumpables, buffers, tableBuffers)
+ }
+ matches.forEach { it.dump(pw, args) }
} else {
if (args.listOnly) {
val dumpables = dumpManager.getDumpables()
val buffers = dumpManager.getLogBuffers()
+ val tableBuffers = dumpManager.getTableLogBuffers()
pw.println("Dumpables:")
listTargetNames(dumpables, pw)
@@ -236,18 +241,23 @@ constructor(
pw.println("Buffers:")
listTargetNames(buffers, pw)
+ pw.println()
+
+ pw.println("TableBuffers:")
+ listTargetNames(tableBuffers, pw)
} else {
pw.println("Nothing to dump :(")
}
}
}
+ /** Finds the best match for a particular target */
private fun findTargetInCollection(
target: String,
dumpables: Collection<DumpableEntry>,
logBuffers: Collection<LogBufferEntry>,
tableBuffers: Collection<TableLogBufferEntry>,
- ) =
+ ): DumpsysEntry? =
sequence {
findBestTargetMatch(dumpables, target)?.let { yield(it) }
findBestTargetMatch(logBuffers, target)?.let { yield(it) }
@@ -256,6 +266,31 @@ constructor(
.sortedBy { it.name }
.minByOrNull { it.name.length }
+ /** Finds the best match for each target, if any, in the order of the targets */
+ private fun findBestMatchesInCollection(
+ targets: List<String>,
+ dumpables: Collection<DumpableEntry>,
+ logBuffers: Collection<LogBufferEntry>,
+ tableBuffers: Collection<TableLogBufferEntry>,
+ ): List<DumpsysEntry> =
+ targets.mapNotNull { target ->
+ findTargetInCollection(target, dumpables, logBuffers, tableBuffers)
+ }
+
+ /** Finds all matches for any target, returning in the --list order. */
+ private fun findAllMatchesInCollection(
+ targets: List<String>,
+ dumpables: Collection<DumpableEntry>,
+ logBuffers: Collection<LogBufferEntry>,
+ tableBuffers: Collection<TableLogBufferEntry>,
+ ): List<DumpsysEntry> =
+ sequence {
+ yieldAll(dumpables.filter { it.matchesAny(targets) })
+ yieldAll(logBuffers.filter { it.matchesAny(targets) })
+ yieldAll(tableBuffers.filter { it.matchesAny(targets) })
+ }
+ .sortedBy { it.name }.toList()
+
private fun dumpConfig(pw: PrintWriter) {
config.dump(pw, arrayOf())
}
@@ -272,6 +307,11 @@ constructor(
pw.println("etc.")
pw.println()
+ pw.println("Print all matches, instead of the best match:")
+ pw.println("$ <invocation> --all <targets>")
+ pw.println("$ <invocation> --all Log")
+ pw.println()
+
pw.println("Special commands:")
pw.println("$ <invocation> dumpables")
pw.println("$ <invocation> buffers")
@@ -325,9 +365,10 @@ constructor(
"--help" -> {
pArgs.command = "help"
}
- // This flag is passed as part of the proto dump in Bug reports, we can ignore
- // it because this is our default behavior.
- "-a" -> {}
+ "-a",
+ "--all" -> {
+ pArgs.matchAll = true
+ }
else -> {
throw ArgParseException("Unknown flag: $arg")
}
@@ -386,15 +427,19 @@ constructor(
const val DUMPSYS_DUMPABLE_DIVIDER =
"----------------------------------------------------------------------------"
+ private fun DumpsysEntry.matches(target: String) = name.endsWith(target)
+ private fun DumpsysEntry.matchesAny(targets: Collection<String>) =
+ targets.any { matches(it) }
+
private fun findBestTargetMatch(c: Collection<DumpsysEntry>, target: String) =
- c.asSequence().filter { it.name.endsWith(target) }.minByOrNull { it.name.length }
+ c.asSequence().filter { it.matches(target) }.minByOrNull { it.name.length }
private fun findBestProtoTargetMatch(
c: Collection<DumpableEntry>,
target: String
): ProtoDumpable? =
c.asSequence()
- .filter { it.name.endsWith(target) }
+ .filter { it.matches(target) }
.filter { it.dumpable is ProtoDumpable }
.minByOrNull { it.name.length }
?.dumpable as? ProtoDumpable
@@ -440,40 +485,34 @@ constructor(
}
/**
- * Utility to write a [DumpableEntry] to the given [PrintWriter] in a
- * dumpsys-appropriate format.
+ * Utility to write a [DumpableEntry] to the given [PrintWriter] in a dumpsys-appropriate
+ * format.
*/
private fun dumpDumpable(
- entry: DumpableEntry,
- pw: PrintWriter,
- args: Array<String> = arrayOf(),
- ) = pw.wrapSection(entry) {
- entry.dumpable.dump(pw, args)
- }
+ entry: DumpableEntry,
+ pw: PrintWriter,
+ args: Array<String> = arrayOf(),
+ ) = pw.wrapSection(entry) { entry.dumpable.dump(pw, args) }
/**
- * Utility to write a [LogBufferEntry] to the given [PrintWriter] in a
- * dumpsys-appropriate format.
+ * Utility to write a [LogBufferEntry] to the given [PrintWriter] in a dumpsys-appropriate
+ * format.
*/
private fun dumpBuffer(
- entry: LogBufferEntry,
- pw: PrintWriter,
- tailLength: Int = 0,
- ) = pw.wrapSection(entry) {
- entry.buffer.dump(pw, tailLength)
- }
+ entry: LogBufferEntry,
+ pw: PrintWriter,
+ tailLength: Int = 0,
+ ) = pw.wrapSection(entry) { entry.buffer.dump(pw, tailLength) }
/**
* Utility to write a [TableLogBufferEntry] to the given [PrintWriter] in a
* dumpsys-appropriate format.
*/
private fun dumpTableBuffer(
- entry: TableLogBufferEntry,
- pw: PrintWriter,
- args: Array<String> = arrayOf(),
- ) = pw.wrapSection(entry) {
- entry.table.dump(pw, args)
- }
+ entry: TableLogBufferEntry,
+ pw: PrintWriter,
+ args: Array<String> = arrayOf(),
+ ) = pw.wrapSection(entry) { entry.table.dump(pw, args) }
/**
* Zero-arg utility to write a [DumpsysEntry] to the given [PrintWriter] in a
@@ -513,6 +552,7 @@ private class ParsedArgs(val rawArgs: Array<String>, val nonFlagArgs: List<Strin
var tailLength: Int = 0
var command: String? = null
var listOnly = false
+ var matchAll = false
var proto = false
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
index 41ce3fd11e8a..7a24d7693035 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
@@ -29,7 +29,6 @@ import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW
import com.android.systemui.keyguard.shared.ComposeLockscreen
-import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor
@@ -52,15 +51,11 @@ class FlagDependencies @Inject constructor(featureFlags: FeatureFlagsClassic, ha
FooterViewRefactor.token dependsOn NotificationIconContainerRefactor.token
NotificationAvalancheSuppression.token dependsOn VisualInterruptionRefactor.token
- // Internal keyguard dependencies
- KeyguardShadeMigrationNssl.token dependsOn keyguardBottomAreaRefactor
-
// SceneContainer dependencies
SceneContainerFlag.getFlagDependencies().forEach { (alpha, beta) -> alpha dependsOn beta }
SceneContainerFlag.getMainStaticFlag() dependsOn MIGRATE_KEYGUARD_STATUS_BAR_VIEW
// ComposeLockscreen dependencies
- ComposeLockscreen.token dependsOn KeyguardShadeMigrationNssl.token
ComposeLockscreen.token dependsOn keyguardBottomAreaRefactor
ComposeLockscreen.token dependsOn migrateClocksToBlueprint
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependenciesBase.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependenciesBase.kt
index 14fda5e96a9c..2fe1dd450088 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependenciesBase.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependenciesBase.kt
@@ -19,6 +19,7 @@ package com.android.systemui.flags
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
+import android.app.NotificationManager.IMPORTANCE_DEFAULT
import android.content.Context
import android.util.Log
import com.android.systemui.CoreStartable
@@ -48,10 +49,14 @@ abstract class FlagDependenciesBase(
private var unmetDependencies = emptyList<Dependency>()
override fun start() {
+ if (!handler.enableDependencies) {
+ return
+ }
defineDependencies()
allDependencies = workingDependencies.toList()
unmetDependencies = workingDependencies.filter { !it.isMet }
workingDependencies.clear()
+ handler.onCollected(allDependencies)
if (unmetDependencies.isNotEmpty()) {
handler.warnAboutBadFlagConfiguration(all = allDependencies, unmet = unmetDependencies)
}
@@ -106,14 +111,24 @@ abstract class FlagDependenciesBase(
/** Add a dependency to the working list */
private fun addDependency(first: FlagToken, second: FlagToken) {
- if (!Compile.IS_DEBUG) return // `user` builds should omit all this code
+ if (!handler.enableDependencies) return
workingDependencies.add(
Dependency(first.name, first.isEnabled, second.name, second.isEnabled)
)
}
- /** An interface which handles a warning about a bad flag configuration. */
+ /** An interface which handles dependency collection. */
interface Handler {
+ /**
+ * Should FlagDependencies do anything?
+ *
+ * @return false for user builds so that we skip this overhead.
+ */
+ val enableDependencies: Boolean
+ get() = Compile.IS_DEBUG
+ /** Handle the complete list of dependencies. */
+ fun onCollected(all: List<Dependency>) {}
+ /** Handle a bad flag configuration. */
fun warnAboutBadFlagConfiguration(all: List<Dependency>, unmet: List<Dependency>)
}
}
@@ -133,10 +148,10 @@ constructor(
all: List<FlagDependenciesBase.Dependency>,
unmet: List<FlagDependenciesBase.Dependency>
) {
- val title = "Invalid flag dependencies: ${unmet.size} of ${all.size}"
+ val title = "Invalid flag dependencies: ${unmet.size}"
val details = unmet.joinToString("\n") { it.shortUnmetString() }
Log.e("FlagDependencies", "$title:\n$details")
- val channel = NotificationChannel("FLAGS", "Flags", NotificationManager.IMPORTANCE_DEFAULT)
+ val channel = NotificationChannel(CHANNEL_ID, CHANNEL_NAME, IMPORTANCE_DEFAULT)
val notification =
Notification.Builder(context, channel.id)
.setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
@@ -146,7 +161,18 @@ constructor(
.setVisibility(Notification.VISIBILITY_PUBLIC)
.build()
notifManager.createNotificationChannel(channel)
- notifManager.notify("flags", 0, notification)
+ notifManager.notify(NOTIF_TAG, NOTIF_ID, notification)
+ }
+
+ override fun onCollected(all: List<FlagDependenciesBase.Dependency>) {
+ notifManager.cancel(NOTIF_TAG, NOTIF_ID)
+ }
+
+ companion object {
+ private const val CHANNEL_ID = "FLAGS"
+ private const val CHANNEL_NAME = "Flags"
+ private const val NOTIF_TAG = "FlagDependenciesNotifier"
+ private const val NOTIF_ID = 0
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 6eff79284847..33a69bf0d774 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -102,11 +102,6 @@ object Flags {
default = true
)
- // TODO(b/301955929)
- @JvmField
- val NOTIF_LS_BACKGROUND_THREAD =
- releasedFlag("notification_lockscreen_mgr_bg_thread")
-
// 200 - keyguard/lockscreen
// ** Flag retired **
// public static final BooleanFlag KEYGUARD_LAYOUT =
@@ -329,9 +324,6 @@ object Flags {
// TODO(b/254512673): Tracking Bug
@JvmField val DREAM_MEDIA_TAP_TO_OPEN = unreleasedFlag("dream_media_tap_to_open")
- // TODO(b/263272731): Tracking Bug
- val MEDIA_TTT_RECEIVER_SUCCESS_RIPPLE = releasedFlag("media_ttt_receiver_success_ripple")
-
// TODO(b/266157412): Tracking Bug
val MEDIA_RETAIN_SESSIONS = unreleasedFlag("media_retain_sessions")
@@ -482,11 +474,6 @@ object Flags {
val WARN_ON_BLOCKING_BINDER_TRANSACTIONS =
unreleasedFlag("warn_on_blocking_binder_transactions")
- // TODO(b/283071711): Tracking bug
- @JvmField
- val TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK =
- unreleasedFlag("trim_resources_with_background_trim_on_lock")
-
// TODO:(b/283203305): Tracking bug
@JvmField val TRIM_FONT_CACHES_AT_UNLOCK = unreleasedFlag("trim_font_caches_on_unlock")
@@ -555,10 +542,6 @@ object Flags {
@JvmField
val ENABLE_NEW_PRIVACY_DIALOG = releasedFlag("enable_new_privacy_dialog")
- // TODO(b/302087895): Tracking Bug
- @JvmField val CALL_LAYOUT_ASYNC_SET_DATA =
- unreleasedFlag("call_layout_async_set_data", teamfood = true)
-
// TODO(b/302144438): Tracking Bug
@JvmField val DECOUPLE_REMOTE_INPUT_DELEGATE_AND_CALLBACK_UPDATE =
unreleasedFlag("decouple_remote_input_delegate_and_callback_update")
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index afcb03da39da..0bc29a8d0f16 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -122,6 +122,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.globalactions.domain.interactor.GlobalActionsInteractor;
import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
import com.android.systemui.plugins.GlobalActionsPanelPlugin;
import com.android.systemui.scrim.ScrimDrawable;
@@ -257,6 +258,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
private final ShadeController mShadeController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final DialogTransitionAnimator mDialogTransitionAnimator;
+ private final GlobalActionsInteractor mInteractor;
@VisibleForTesting
public enum GlobalActionsEvent implements UiEventLogger.UiEventEnum {
@@ -368,7 +370,8 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
ShadeController shadeController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
DialogTransitionAnimator dialogTransitionAnimator,
- SelectedUserInteractor selectedUserInteractor) {
+ SelectedUserInteractor selectedUserInteractor,
+ GlobalActionsInteractor interactor) {
mContext = context;
mWindowManagerFuncs = windowManagerFuncs;
mAudioManager = audioManager;
@@ -404,6 +407,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mDialogTransitionAnimator = dialogTransitionAnimator;
mSelectedUserInteractor = selectedUserInteractor;
+ mInteractor = interactor;
// receive broadcasts
IntentFilter filter = new IntentFilter();
@@ -1333,6 +1337,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
mUiEventLogger.log(GlobalActionsEvent.GA_POWER_MENU_CLOSE);
mWindowManagerFuncs.onGlobalActionsHidden();
mLifecycle.setCurrentState(Lifecycle.State.CREATED);
+ mInteractor.onDismissed();
}
/**
@@ -1342,6 +1347,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene
public void onShow(DialogInterface dialog) {
mMetricsLogger.visible(MetricsEvent.POWER_MENU);
mUiEventLogger.log(GlobalActionsEvent.GA_POWER_MENU_OPEN);
+ mInteractor.onShown();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/data/repository/GlobalActionsRepository.kt b/packages/SystemUI/src/com/android/systemui/globalactions/data/repository/GlobalActionsRepository.kt
new file mode 100644
index 000000000000..2550504cdc42
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/data/repository/GlobalActionsRepository.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 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.globalactions.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/** Encapsulates application state for global actions. */
+@SysUISingleton
+class GlobalActionsRepository @Inject constructor() {
+ private val _isVisible: MutableStateFlow<Boolean> = MutableStateFlow(false)
+ /** Is the global actions dialog visible. */
+ val isVisible = _isVisible.asStateFlow()
+
+ /** Sets whether the global actions dialog is visible. */
+ fun setVisible(isVisible: Boolean) {
+ _isVisible.value = isVisible
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/domain/interactor/GlobalActionsInteractor.kt b/packages/SystemUI/src/com/android/systemui/globalactions/domain/interactor/GlobalActionsInteractor.kt
new file mode 100644
index 000000000000..c484a48a4058
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/domain/interactor/GlobalActionsInteractor.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2024 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.globalactions.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.globalactions.data.repository.GlobalActionsRepository
+import javax.inject.Inject
+import kotlinx.coroutines.flow.StateFlow
+
+@SysUISingleton
+class GlobalActionsInteractor
+@Inject
+constructor(
+ private val repository: GlobalActionsRepository,
+) {
+ /** Is the global actions dialog visible. */
+ val isVisible: StateFlow<Boolean> = repository.isVisible
+
+ /** Notifies that the global actions dialog is shown. */
+ fun onShown() {
+ repository.setVisible(true)
+ }
+
+ /** Notifies that the global actions dialog has been dismissed. */
+ fun onDismissed() {
+ repository.setVisible(false)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/shared/model/StickyKey.kt b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/shared/model/StickyKey.kt
index 72a81cbac9d5..0f1cc99bfef7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/shared/model/StickyKey.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/shared/model/StickyKey.kt
@@ -20,8 +20,8 @@ package com.android.systemui.keyboard.stickykeys.shared.model
value class Locked(val locked: Boolean)
enum class ModifierKey(val displayedText: String) {
- ALT("ALT LEFT"),
- ALT_GR("ALT RIGHT"),
+ ALT("ALT"),
+ ALT_GR("ALT"),
CTRL("CTRL"),
META("ACTION"),
SHIFT("SHIFT"),
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
index e23ec894f8f3..00ec1a14bb93 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
@@ -404,6 +404,7 @@ public class KeyguardIndicationRotateTextViewController extends
public static final int INDICATION_TYPE_BIOMETRIC_MESSAGE = 11;
public static final int INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP = 12;
public static final int INDICATION_IS_DISMISSIBLE = 13;
+ public static final int INDICATION_TYPE_ADAPTIVE_AUTH = 14;
@IntDef({
INDICATION_TYPE_NONE,
@@ -419,7 +420,8 @@ public class KeyguardIndicationRotateTextViewController extends
INDICATION_TYPE_REVERSE_CHARGING,
INDICATION_TYPE_BIOMETRIC_MESSAGE,
INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP,
- INDICATION_IS_DISMISSIBLE
+ INDICATION_IS_DISMISSIBLE,
+ INDICATION_TYPE_ADAPTIVE_AUTH
})
@Retention(RetentionPolicy.SOURCE)
public @interface IndicationType{}
@@ -455,6 +457,8 @@ public class KeyguardIndicationRotateTextViewController extends
return "biometric_message";
case INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP:
return "biometric_message_followup";
+ case INDICATION_TYPE_ADAPTIVE_AUTH:
+ return "adaptive_auth";
default:
return "unknown[" + type + "]";
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 86b99ecac66c..301942f6242b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -53,6 +53,7 @@ import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel
import com.android.systemui.plugins.FalsingManager
@@ -98,6 +99,7 @@ constructor(
private val falsingManager: FalsingManager,
private val aodAlphaViewModel: AodAlphaViewModel,
private val keyguardClockViewModel: KeyguardClockViewModel,
+ private val smartspaceViewModel: KeyguardSmartspaceViewModel,
private val lockscreenContentViewModel: LockscreenContentViewModel,
private val lockscreenSceneBlueprintsLazy: Lazy<Set<LockscreenSceneBlueprint>>,
private val keyguardBlueprintViewBinder: KeyguardBlueprintViewBinder,
@@ -148,6 +150,7 @@ constructor(
keyguardRootView,
keyguardBlueprintViewModel,
keyguardClockViewModel,
+ smartspaceViewModel,
)
}
}
@@ -167,7 +170,6 @@ constructor(
KeyguardIndicationAreaBinder.bind(
notificationShadeWindowView.requireViewById(R.id.keyguard_indication_area),
keyguardIndicationAreaViewModel,
- aodAlphaViewModel,
indicationController,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index b5ca79e71c33..c6b99528b2ba 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -30,6 +30,7 @@ import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NAV_BA
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_OCCLUSION;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_FROM_AOD;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_UNLOCK_ANIMATION;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
@@ -152,7 +153,7 @@ import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeExpansionStateManager;
-import com.android.systemui.shade.ShadeViewController;
+import com.android.systemui.shade.ShadeLockscreenInteractor;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShadeDepthController;
@@ -690,18 +691,17 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
} else {
resetStateLocked();
}
- } else {
- if (lastSimStateWasLocked && mShowing) {
- if (DEBUG_SIM_STATES) {
- Log.d(TAG, "SIM moved to "
- + "NOT_READY/ABSENT/UNKNOWN when the previous state "
- + "was locked. Reset the state.");
- }
+ }
+ if (simState == TelephonyManager.SIM_STATE_ABSENT) {
+ // MVNO SIMs can become transiently NOT_READY when switching networks,
+ // so we should only lock when they are ABSENT.
+ if (lastSimStateWasLocked) {
+ if (DEBUG_SIM_STATES) Log.d(TAG, "SIM moved to ABSENT when the "
+ + "previous state was locked. Reset the state.");
resetStateLocked();
}
+ mSimWasLocked.append(slotId, false);
}
-
- mSimWasLocked.append(slotId, false);
}
break;
case TelephonyManager.SIM_STATE_PIN_REQUIRED:
@@ -921,15 +921,17 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
return KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
} else if ((strongAuth & STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW) != 0) {
return KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN;
+ } else if (any && ((strongAuth & STRONG_AUTH_REQUIRED_AFTER_LOCKOUT) != 0
+ || mUpdateMonitor.isFingerprintLockedOut())) {
+ return KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT;
+ } else if ((strongAuth & SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST) != 0) {
+ return KeyguardSecurityView.PROMPT_REASON_ADAPTIVE_AUTH_REQUEST;
} else if (trustAgentsEnabled
&& (strongAuth & SOME_AUTH_REQUIRED_AFTER_USER_REQUEST) != 0) {
return KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
} else if (trustAgentsEnabled
&& (strongAuth & SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED) != 0) {
return KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED;
- } else if (any && ((strongAuth & STRONG_AUTH_REQUIRED_AFTER_LOCKOUT) != 0
- || mUpdateMonitor.isFingerprintLockedOut())) {
- return KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT;
} else if (any && (strongAuth & STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE) != 0) {
return KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE;
} else if (any && (strongAuth
@@ -3527,14 +3529,14 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
* @return the View Controller for the Keyguard View this class is mediating.
*/
public KeyguardViewController registerCentralSurfaces(CentralSurfaces centralSurfaces,
- ShadeViewController panelView,
+ ShadeLockscreenInteractor shadeLockscreenInteractor,
@Nullable ShadeExpansionStateManager shadeExpansionStateManager,
BiometricUnlockController biometricUnlockController,
View notificationContainer, KeyguardBypassController bypassController) {
mCentralSurfaces = centralSurfaces;
mKeyguardViewControllerLazy.get().registerCentralSurfaces(
centralSurfaces,
- panelView,
+ shadeLockscreenInteractor,
shadeExpansionStateManager,
biometricUnlockController,
notificationContainer,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
index c52ca68ee37f..e101b0ab64aa 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
@@ -32,13 +32,13 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.utils.GlobalWindowManager
+import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
-import javax.inject.Inject
/**
* Releases cached resources on allocated by keyguard.
@@ -62,7 +62,7 @@ constructor(
override fun start() {
Log.d(LOG_TAG, "Resource trimmer registered.")
- if (featureFlags.isEnabled(Flags.TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)) {
+ if (com.android.systemui.Flags.trimResourcesWithBackgroundTrimAtLock()) {
applicationScope.launch(bgDispatcher) {
// We need to wait for the AoD transition (and animation) to complete.
// This means we're waiting for isDreaming (== implies isDoze) and dozeAmount == 1f
@@ -107,19 +107,16 @@ constructor(
@WorkerThread
private fun onWakefulnessUpdated(
- isAsleep: Boolean,
- isDreaming: Boolean,
- isDozingFully: Boolean
+ isAsleep: Boolean,
+ isDreaming: Boolean,
+ isDozingFully: Boolean
) {
- if (!featureFlags.isEnabled(Flags.TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)) {
+ if (!com.android.systemui.Flags.trimResourcesWithBackgroundTrimAtLock()) {
return
}
if (DEBUG) {
- Log.d(
- LOG_TAG,
- "isAsleep: $isAsleep Dreaming: $isDreaming DozeAmount: $isDozingFully"
- )
+ Log.d(LOG_TAG, "isAsleep: $isAsleep Dreaming: $isDreaming DozeAmount: $isDozingFully")
}
// There are three scenarios:
// * No dozing and no AoD at all - where we go directly to ASLEEP with isDreaming = false
@@ -129,8 +126,7 @@ constructor(
// * AoD - where we go to ASLEEP with iDreaming = true and dozeAmount slowly increases
// to 1f
val dozeDisabledAndScreenOff = isAsleep && !isDreaming
- val dozeEnabledAndDozeAnimationCompleted =
- isAsleep && isDreaming && isDozingFully
+ val dozeEnabledAndDozeAnimationCompleted = isAsleep && isDreaming && isDozingFully
if (dozeDisabledAndScreenOff || dozeEnabledAndDozeAnimationCompleted) {
Trace.beginSection("ResourceTrimmer#trimMemory")
Log.d(LOG_TAG, "SysUI asleep, trimming memory.")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
index 42f14f1eb317..9b3f13d16911 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
@@ -151,9 +151,13 @@ constructor(
awaitClose { revealAmountAnimator.removeUpdateListener(updateListener) }
}
+ private var willBeOrIsRevealed: Boolean? = null
+
override fun startRevealAmountAnimator(reveal: Boolean) {
+ if (reveal == willBeOrIsRevealed) return
+ willBeOrIsRevealed = reveal
if (reveal) revealAmountAnimator.start() else revealAmountAnimator.reverse()
- scrimLogger.d(TAG, "startRevealAmountAnimator, reveal", reveal)
+ scrimLogger.d(TAG, "startRevealAmountAnimator, reveal: ", reveal)
}
override val revealEffect =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
index 13ffd6396cda..acfa107cc1f1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
@@ -18,6 +18,8 @@ package com.android.systemui.keyguard.domain.interactor
import android.animation.ValueAnimator
import com.android.app.animation.Interpolators
+import com.android.app.tracing.coroutines.launch
+import com.android.systemui.Flags.communalHub
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
@@ -44,6 +46,7 @@ constructor(
@Background bgDispatcher: CoroutineDispatcher,
@Main mainDispatcher: CoroutineDispatcher,
private val keyguardInteractor: KeyguardInteractor,
+ private val glanceableHubTransitions: GlanceableHubTransitions,
) :
TransitionInteractor(
fromState = KeyguardState.DREAMING,
@@ -57,6 +60,18 @@ constructor(
listenForDreamingToGone()
listenForDreamingToAodOrDozing()
listenForTransitionToCamera(scope, keyguardInteractor)
+ listenForDreamingToGlanceableHub()
+ }
+
+ private fun listenForDreamingToGlanceableHub() {
+ if (!communalHub()) return
+ scope.launch("$TAG#listenForDreamingToGlanceableHub", mainDispatcher) {
+ glanceableHubTransitions.listenForGlanceableHubTransition(
+ transitionOwnerName = TAG,
+ fromState = KeyguardState.DREAMING,
+ toState = KeyguardState.GLANCEABLE_HUB,
+ )
+ }
}
fun startToLockscreenTransition() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
index 71d941ad8d22..786c3c6697d9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
@@ -20,7 +20,6 @@ import android.animation.ValueAnimator
import com.android.app.animation.Interpolators
import com.android.app.tracing.coroutines.launch
import com.android.systemui.Flags
-import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
@@ -28,13 +27,16 @@ import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepositor
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.sample as sampleMultiple
+import com.android.systemui.util.kotlin.BooleanFlowOperators.and
+import com.android.systemui.util.kotlin.BooleanFlowOperators.not
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
-import kotlin.time.Duration.Companion.milliseconds
+import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
@SysUISingleton
class FromGlanceableHubTransitionInteractor
@@ -59,19 +61,22 @@ constructor(
if (!Flags.communalHub()) {
return
}
- listenForHubToLockscreen()
+ listenForHubToLockscreenOrDreaming()
listenForHubToDozing()
listenForHubToPrimaryBouncer()
listenForHubToAlternateBouncer()
listenForHubToOccluded()
listenForHubToGone()
- listenForHubToDreaming()
}
override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator {
return ValueAnimator().apply {
interpolator = Interpolators.LINEAR
- duration = DEFAULT_DURATION.inWholeMilliseconds
+ duration =
+ when (toState) {
+ KeyguardState.LOCKSCREEN -> TO_LOCKSCREEN_DURATION
+ else -> DEFAULT_DURATION
+ }.inWholeMilliseconds
}
}
@@ -79,12 +84,24 @@ constructor(
* Listens for the glanceable hub transition to lock screen and directly drives the keyguard
* transition.
*/
- private fun listenForHubToLockscreen() {
- glanceableHubTransitions.listenForLockscreenAndHubTransition(
- transitionName = "listenForHubToLockscreen",
- transitionOwnerName = TAG,
- toScene = CommunalSceneKey.Blank,
- )
+ private fun listenForHubToLockscreenOrDreaming() {
+ scope.launch("$TAG#listenForGlanceableHubToLockscreenOrDream") {
+ keyguardInteractor.isDreaming.collectLatest { dreaming ->
+ withContext(mainDispatcher) {
+ val toState =
+ if (dreaming) {
+ KeyguardState.DREAMING
+ } else {
+ KeyguardState.LOCKSCREEN
+ }
+ glanceableHubTransitions.listenForGlanceableHubTransition(
+ transitionOwnerName = TAG,
+ fromState = KeyguardState.GLANCEABLE_HUB,
+ toState = toState,
+ )
+ }
+ }
+ }
}
private fun listenForHubToPrimaryBouncer() {
@@ -133,31 +150,15 @@ constructor(
}
}
- private fun listenForHubToDreaming() {
- val invalidFromStates = setOf(KeyguardState.AOD, KeyguardState.DOZING)
- scope.launch("$TAG#listenForHubToDreaming") {
- keyguardInteractor.isAbleToDream
- .sampleMultiple(startedKeyguardTransitionStep, finishedKeyguardState)
- .collect { (isAbleToDream, lastStartedTransition, finishedKeyguardState) ->
- val isOnHub = finishedKeyguardState == KeyguardState.GLANCEABLE_HUB
- val isTransitionInterruptible =
- lastStartedTransition.to == KeyguardState.GLANCEABLE_HUB &&
- !invalidFromStates.contains(lastStartedTransition.from)
- if (isAbleToDream && (isOnHub || isTransitionInterruptible)) {
- startTransitionTo(KeyguardState.DREAMING)
- }
- }
- }
- }
-
private fun listenForHubToOccluded() {
scope.launch {
- keyguardInteractor.isKeyguardOccluded.sample(startedKeyguardState, ::Pair).collect {
- (isOccluded, keyguardState) ->
- if (isOccluded && keyguardState == fromState) {
- startTransitionTo(KeyguardState.OCCLUDED)
+ and(keyguardInteractor.isKeyguardOccluded, not(keyguardInteractor.isDreaming))
+ .sample(startedKeyguardState, ::Pair)
+ .collect { (isOccludedAndNotDreaming, keyguardState) ->
+ if (isOccludedAndNotDreaming && keyguardState == fromState) {
+ startTransitionTo(KeyguardState.OCCLUDED)
+ }
}
- }
}
}
@@ -175,7 +176,7 @@ constructor(
companion object {
const val TAG = "FromGlanceableHubTransitionInteractor"
- val DEFAULT_DURATION = 400.milliseconds
+ val DEFAULT_DURATION = 1.seconds
val TO_LOCKSCREEN_DURATION = DEFAULT_DURATION
}
}
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 57e9ac707965..7263ae96b3a8 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
@@ -19,7 +19,6 @@ package com.android.systemui.keyguard.domain.interactor
import android.animation.ValueAnimator
import android.util.MathUtils
import com.android.app.animation.Interpolators
-import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
@@ -39,6 +38,7 @@ import com.android.systemui.util.kotlin.sample
import java.util.UUID
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
+import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
@@ -360,12 +360,13 @@ constructor(
if (!com.android.systemui.Flags.communalHub()) {
return
}
-
- glanceableHubTransitions.listenForLockscreenAndHubTransition(
- transitionName = "listenForLockscreenToGlanceableHub",
- transitionOwnerName = TAG,
- toScene = CommunalSceneKey.Communal
- )
+ scope.launch(mainDispatcher) {
+ glanceableHubTransitions.listenForGlanceableHubTransition(
+ transitionOwnerName = TAG,
+ fromState = KeyguardState.LOCKSCREEN,
+ toState = KeyguardState.GLANCEABLE_HUB,
+ )
+ }
}
override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator {
@@ -380,6 +381,7 @@ constructor(
KeyguardState.AOD -> TO_AOD_DURATION
KeyguardState.DOZING -> TO_DOZING_DURATION
KeyguardState.DREAMING_LOCKSCREEN_HOSTED -> TO_DREAMING_HOSTED_DURATION
+ KeyguardState.GLANCEABLE_HUB -> TO_GLANCEABLE_HUB_DURATION
else -> DEFAULT_DURATION
}.inWholeMilliseconds
}
@@ -395,6 +397,6 @@ constructor(
val TO_AOD_DURATION = 500.milliseconds
val TO_PRIMARY_BOUNCER_DURATION = DEFAULT_DURATION
val TO_GONE_DURATION = DEFAULT_DURATION
- val TO_GLANCEABLE_HUB_DURATION = DEFAULT_DURATION
+ val TO_GLANCEABLE_HUB_DURATION = 1.seconds
}
}
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 acbd9fb4c407..f45a9ecfaedf 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
@@ -134,8 +134,9 @@ constructor(
powerInteractor.isAwake,
startedKeyguardTransitionStep,
keyguardInteractor.isKeyguardOccluded,
+ keyguardInteractor.isDreaming,
keyguardInteractor.isActiveDreamLockscreenHosted,
- communalInteractor.isIdleOnCommunal
+ communalInteractor.isIdleOnCommunal,
)
.collect {
(
@@ -143,6 +144,7 @@ constructor(
isAwake,
lastStartedTransitionStep,
occluded,
+ isDreaming,
isActiveDreamLockscreenHosted,
isIdleOnCommunal) ->
if (
@@ -152,10 +154,12 @@ constructor(
!isActiveDreamLockscreenHosted
) {
val toState =
- if (occluded) {
+ if (occluded && !isDreaming) {
KeyguardState.OCCLUDED
} else if (isIdleOnCommunal) {
KeyguardState.GLANCEABLE_HUB
+ } else if (isDreaming) {
+ KeyguardState.DREAMING
} else {
KeyguardState.LOCKSCREEN
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt
index ca661536d988..6cb1eb493db3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt
@@ -18,11 +18,9 @@ package com.android.systemui.keyguard.domain.interactor
import android.animation.ValueAnimator
import com.android.app.animation.Interpolators
-import com.android.app.tracing.coroutines.launch
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.CommunalTransitionProgress
import com.android.systemui.communal.shared.model.CommunalSceneKey
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -32,13 +30,11 @@ import com.android.systemui.util.kotlin.sample
import java.util.UUID
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.flowOn
class GlanceableHubTransitions
@Inject
constructor(
- @Application private val scope: CoroutineScope,
@Background private val bgDispatcher: CoroutineDispatcher,
private val transitionInteractor: KeyguardTransitionInteractor,
private val transitionRepository: KeyguardTransitionRepository,
@@ -52,107 +48,101 @@ constructor(
* externally. The progress is used for both transitions caused by user touch input or by
* programmatic changes.
*/
- fun listenForLockscreenAndHubTransition(
- transitionName: String,
+ suspend fun listenForGlanceableHubTransition(
transitionOwnerName: String,
- toScene: CommunalSceneKey
+ fromState: KeyguardState,
+ toState: KeyguardState,
) {
- val fromState: KeyguardState
- val toState: KeyguardState
- if (toScene == CommunalSceneKey.Blank) {
- fromState = KeyguardState.GLANCEABLE_HUB
- toState = KeyguardState.LOCKSCREEN
- } else {
- fromState = KeyguardState.LOCKSCREEN
- toState = KeyguardState.GLANCEABLE_HUB
- }
+ val toScene =
+ if (fromState == KeyguardState.GLANCEABLE_HUB) {
+ CommunalSceneKey.Blank
+ } else {
+ CommunalSceneKey.Communal
+ }
var transitionId: UUID? = null
- scope.launch("$transitionOwnerName#$transitionName") {
- communalInteractor
- .transitionProgressToScene(toScene)
- .sample(
- transitionInteractor.startedKeyguardTransitionStep.flowOn(bgDispatcher),
- ::Pair
- )
- .collect { pair ->
- val (transitionProgress, lastStartedStep) = pair
- val id = transitionId
- if (id == null) {
- // No transition started.
- if (
- transitionProgress is CommunalTransitionProgress.Transition &&
- lastStartedStep.to == fromState
- ) {
- transitionId =
- transitionRepository.startTransition(
- TransitionInfo(
- ownerName = transitionOwnerName,
- from = fromState,
- to = toState,
- animator = null, // transition will be manually controlled
- )
+ communalInteractor
+ .transitionProgressToScene(toScene)
+ .sample(
+ transitionInteractor.startedKeyguardTransitionStep.flowOn(bgDispatcher),
+ ::Pair,
+ )
+ .collect { (transitionProgress, lastStartedStep) ->
+ val id = transitionId
+ if (id == null) {
+ // No transition started.
+ if (
+ transitionProgress is CommunalTransitionProgress.Transition &&
+ lastStartedStep.to == fromState
+ ) {
+ transitionId =
+ transitionRepository.startTransition(
+ TransitionInfo(
+ ownerName = transitionOwnerName,
+ from = fromState,
+ to = toState,
+ animator = null, // transition will be manually controlled
)
- }
- } else {
- if (lastStartedStep.to != toState) {
- return@collect
- }
- // An existing `id` means a transition is started, and calls to
- // `updateTransition` will control it until FINISHED or CANCELED
- val nextState: TransitionState
- val progressFraction: Float
- when (transitionProgress) {
- is CommunalTransitionProgress.Idle -> {
- if (transitionProgress.scene == toScene) {
- nextState = TransitionState.FINISHED
- progressFraction = 1f
- } else {
- nextState = TransitionState.CANCELED
- progressFraction = 0f
- }
- }
- is CommunalTransitionProgress.Transition -> {
- nextState = TransitionState.RUNNING
- progressFraction = transitionProgress.progress
- }
- is CommunalTransitionProgress.OtherTransition -> {
- // Shouldn't happen but if another transition starts during the
- // current one, mark the current one as canceled.
+ )
+ }
+ } else {
+ if (lastStartedStep.to != toState) {
+ return@collect
+ }
+ // An existing `id` means a transition is started, and calls to
+ // `updateTransition` will control it until FINISHED or CANCELED
+ val nextState: TransitionState
+ val progressFraction: Float
+ when (transitionProgress) {
+ is CommunalTransitionProgress.Idle -> {
+ if (transitionProgress.scene == toScene) {
+ nextState = TransitionState.FINISHED
+ progressFraction = 1f
+ } else {
nextState = TransitionState.CANCELED
progressFraction = 0f
}
}
- transitionRepository.updateTransition(
- id,
- progressFraction,
- nextState,
- )
-
- if (
- nextState == TransitionState.CANCELED ||
- nextState == TransitionState.FINISHED
- ) {
- transitionId = null
+ is CommunalTransitionProgress.Transition -> {
+ nextState = TransitionState.RUNNING
+ progressFraction = transitionProgress.progress
+ }
+ is CommunalTransitionProgress.OtherTransition -> {
+ // Shouldn't happen but if another transition starts during the
+ // current one, mark the current one as canceled.
+ nextState = TransitionState.CANCELED
+ progressFraction = 0f
}
+ }
+ transitionRepository.updateTransition(
+ id,
+ progressFraction,
+ nextState,
+ )
- // If canceled, just put the state back.
- if (nextState == TransitionState.CANCELED) {
- transitionRepository.startTransition(
- TransitionInfo(
- ownerName = transitionOwnerName,
- from = toState,
- to = fromState,
- animator =
- ValueAnimator().apply {
- interpolator = Interpolators.LINEAR
- duration = 0
- }
- )
+ if (
+ nextState == TransitionState.CANCELED ||
+ nextState == TransitionState.FINISHED
+ ) {
+ transitionId = null
+ }
+
+ // If canceled, just put the state back.
+ if (nextState == TransitionState.CANCELED) {
+ transitionRepository.startTransition(
+ TransitionInfo(
+ ownerName = transitionOwnerName,
+ from = toState,
+ to = fromState,
+ animator =
+ ValueAnimator().apply {
+ interpolator = Interpolators.LINEAR
+ duration = 0
+ }
)
- }
+ )
}
}
- }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
index 56d64a298bc0..bc3f0cce2fc7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
@@ -15,12 +15,15 @@
*
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.keyguard.domain.interactor
import android.content.Context
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardBlueprintRepository
+import com.android.systemui.keyguard.shared.ComposeLockscreen
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint
import com.android.systemui.keyguard.ui.view.layout.blueprints.SplitShadeKeyguardBlueprint
@@ -29,7 +32,9 @@ import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.Intra
import com.android.systemui.statusbar.policy.SplitShadeStateController
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
@@ -41,6 +46,7 @@ constructor(
@Application private val applicationScope: CoroutineScope,
private val context: Context,
private val splitShadeStateController: SplitShadeStateController,
+ private val clockInteractor: KeyguardClockInteractor,
) {
/** The current blueprint for the lockscreen. */
@@ -58,6 +64,7 @@ constructor(
.onStart { emit(Unit) }
.collect { updateBlueprint() }
}
+ applicationScope.launch { clockInteractor.currentClock.collect { updateBlueprint() } }
}
/**
@@ -67,12 +74,17 @@ constructor(
private fun updateBlueprint() {
val useSplitShade =
splitShadeStateController.shouldUseSplitNotificationShade(context.resources)
+ // TODO(b/326098079): Make ID a constant value.
+ val useWeatherClockLayout =
+ clockInteractor.currentClock.value?.config?.id == "DIGITAL_CLOCK_WEATHER" &&
+ ComposeLockscreen.isEnabled
val blueprintId =
- if (useSplitShade) {
- SplitShadeKeyguardBlueprint.ID
- } else {
- DefaultKeyguardBlueprint.DEFAULT
+ when {
+ useWeatherClockLayout && useSplitShade -> SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID
+ useWeatherClockLayout -> WEATHER_CLOCK_BLUEPRINT_ID
+ useSplitShade -> SplitShadeKeyguardBlueprint.ID
+ else -> DefaultKeyguardBlueprint.DEFAULT
}
transitionToBlueprint(blueprintId)
@@ -107,4 +119,13 @@ constructor(
fun getCurrentBlueprint(): KeyguardBlueprint {
return keyguardBlueprintRepository.blueprint.value
}
+
+ companion object {
+ /**
+ * These values live here because classes in the composable package do not exist in some
+ * systems.
+ */
+ const val WEATHER_CLOCK_BLUEPRINT_ID = "weather-clock"
+ const val SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID = "split-shade-weather-clock"
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 405d1d46456c..78749ead7ef9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -241,6 +241,9 @@ constructor(
/**
* When the lockscreen can be dismissed, emit an alpha value as the user swipes up. This is
* useful just before the code commits to moving to GONE.
+ *
+ * This uses legacyShadeExpansion to process swipe up events. In the future, the touch input
+ * signal should be sent directly to transitions.
*/
val dismissAlpha: Flow<Float?> =
combine(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index 8eb1a50086c6..b0a38811cdfc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -46,7 +46,6 @@ import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAfforda
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -56,7 +55,6 @@ import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
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
@@ -68,7 +66,6 @@ class KeyguardQuickAffordanceInteractor
@Inject
constructor(
private val keyguardInteractor: KeyguardInteractor,
- private val shadeInteractor: ShadeInteractor,
private val lockPatternUtils: LockPatternUtils,
private val keyguardStateController: KeyguardStateController,
private val userTracker: UserTracker,
@@ -103,10 +100,9 @@ constructor(
quickAffordanceAlwaysVisible(position),
keyguardInteractor.isDozing,
keyguardInteractor.isKeyguardShowing,
- shadeInteractor.anyExpansion.map { it < 1.0f }.distinctUntilChanged(),
biometricSettingsRepository.isCurrentUserInLockdown,
- ) { affordance, isDozing, isKeyguardShowing, isQuickSettingsVisible, isUserInLockdown ->
- if (!isDozing && isKeyguardShowing && isQuickSettingsVisible && !isUserInLockdown) {
+ ) { affordance, isDozing, isKeyguardShowing, isUserInLockdown ->
+ if (!isDozing && isKeyguardShowing && !isUserInLockdown) {
affordance
} else {
KeyguardQuickAffordanceModel.Hidden
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
index c7f262a2ac80..4d731eccd9bb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
@@ -21,7 +21,6 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.LightRevealScrimRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.power.shared.model.ScreenPowerState
import com.android.systemui.statusbar.LightRevealEffect
@@ -53,11 +52,9 @@ constructor(
scope.launch {
transitionInteractor.startedKeyguardTransitionStep.collect {
scrimLogger.d(TAG, "listenForStartedKeyguardTransitionStep", it)
- if (willTransitionChangeEndState(it)) {
- lightRevealScrimRepository.startRevealAmountAnimator(
- willBeRevealedInState(it.to)
- )
- }
+ lightRevealScrimRepository.startRevealAmountAnimator(
+ willBeRevealedInState(it.to),
+ )
}
}
}
@@ -92,33 +89,25 @@ constructor(
companion object {
- /**
- * Whether the transition requires a change in the reveal amount of the light reveal scrim.
- * If not, we don't care about the transition and don't need to listen to it.
- */
- fun willTransitionChangeEndState(transition: TransitionStep): Boolean {
- return willBeRevealedInState(transition.from) != willBeRevealedInState(transition.to)
- }
-
- /**
- * Whether the light reveal scrim will be fully revealed (revealAmount = 1.0f) in the given
- * state after the transition is complete. If false, scrim will be fully hidden.
- */
- fun willBeRevealedInState(state: KeyguardState): Boolean {
- return when (state) {
- KeyguardState.OFF -> false
- KeyguardState.DOZING -> false
- KeyguardState.AOD -> false
- KeyguardState.DREAMING -> true
- KeyguardState.DREAMING_LOCKSCREEN_HOSTED -> true
- KeyguardState.GLANCEABLE_HUB -> true
- KeyguardState.ALTERNATE_BOUNCER -> true
- KeyguardState.PRIMARY_BOUNCER -> true
- KeyguardState.LOCKSCREEN -> true
- KeyguardState.GONE -> true
- KeyguardState.OCCLUDED -> true
- }
+ /**
+ * Whether the light reveal scrim will be fully revealed (revealAmount = 1.0f) in the given
+ * state after the transition is complete. If false, scrim will be fully hidden.
+ */
+ private fun willBeRevealedInState(state: KeyguardState): Boolean {
+ return when (state) {
+ KeyguardState.OFF -> false
+ KeyguardState.DOZING -> false
+ KeyguardState.AOD -> false
+ KeyguardState.DREAMING -> true
+ KeyguardState.DREAMING_LOCKSCREEN_HOSTED -> true
+ KeyguardState.GLANCEABLE_HUB -> true
+ KeyguardState.ALTERNATE_BOUNCER -> true
+ KeyguardState.PRIMARY_BOUNCER -> true
+ KeyguardState.LOCKSCREEN -> true
+ KeyguardState.GONE -> true
+ KeyguardState.OCCLUDED -> true
}
+ }
val TAG = LightRevealScrimInteractor::class.simpleName!!
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/KeyguardShadeMigrationNssl.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/KeyguardShadeMigrationNssl.kt
deleted file mode 100644
index 23642a741fb8..000000000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/KeyguardShadeMigrationNssl.kt
+++ /dev/null
@@ -1,52 +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.keyguard.shared
-
-import com.android.systemui.Flags
-import com.android.systemui.flags.FlagToken
-import com.android.systemui.flags.RefactorFlagUtils
-
-/** Helper for reading or using the keyguard shade migration nssl flag state. */
-@Suppress("NOTHING_TO_INLINE")
-object KeyguardShadeMigrationNssl {
- /** The aconfig flag name */
- const val FLAG_NAME = Flags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL
-
- /** A token used for dependency declaration */
- val token: FlagToken
- get() = FlagToken(FLAG_NAME, isEnabled)
-
- /** Is the refactor enabled */
- @JvmStatic
- inline val isEnabled
- get() = Flags.keyguardShadeMigrationNssl()
-
- /**
- * Called to ensure code is only run when the flag is enabled. This protects users from the
- * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
- * build to ensure that the refactor author catches issues in testing.
- */
- @JvmStatic
- inline fun isUnexpectedlyInLegacyMode() =
- RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
-
- /**
- * Called to ensure code is only run when the flag is disabled. This will throw an exception if
- * the flag is enabled to ensure that the refactor author catches issues in testing.
- */
- @JvmStatic
- inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/AuthenticationFlags.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/AuthenticationFlags.kt
index cf5b88fde3dc..08904b6ffa86 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/AuthenticationFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/AuthenticationFlags.kt
@@ -60,6 +60,12 @@ data class AuthenticationFlags(val userId: Int, val flag: Int) {
LockPatternUtils.StrongAuthTracker
.STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT
)
+
+ val isSomeAuthRequiredAfterAdaptiveAuthRequest =
+ containsFlag(
+ flag,
+ LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST
+ )
}
private fun containsFlag(haystack: Int, needle: Int): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
index 8b278cdb9a6c..b8ba09801ee8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
@@ -185,13 +185,16 @@ constructor(
return getOrCreateFlow(edge)
.map { step ->
StateToValue(
- step.transitionState,
- when (step.transitionState) {
- STARTED -> stepToValue(step)
- RUNNING -> stepToValue(step)
- CANCELED -> onCancel?.invoke()
- FINISHED -> onFinish?.invoke()
- }
+ from = step.from,
+ to = step.to,
+ transitionState = step.transitionState,
+ value =
+ when (step.transitionState) {
+ STARTED -> stepToValue(step)
+ RUNNING -> stepToValue(step)
+ CANCELED -> onCancel?.invoke()
+ FINISHED -> onFinish?.invoke()
+ }
)
.also { logger.logTransitionStep(name, step, it.value) }
}
@@ -208,6 +211,10 @@ constructor(
}
data class StateToValue(
+ val from: KeyguardState? = null,
+ val to: KeyguardState? = null,
val transitionState: TransitionState = TransitionState.FINISHED,
val value: Float? = 0f,
-)
+) {
+ fun isToOrFrom(state: KeyguardState) = from == state || to == state
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
index 6e70368476ed..66fc99567d42 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
@@ -34,6 +34,7 @@ import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.Intra
import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Config
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import javax.inject.Inject
import kotlin.math.max
@@ -84,6 +85,7 @@ constructor(
constraintLayout: ConstraintLayout,
viewModel: KeyguardBlueprintViewModel,
clockViewModel: KeyguardClockViewModel,
+ smartspaceViewModel: KeyguardSmartspaceViewModel,
) {
constraintLayout.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
@@ -108,10 +110,18 @@ constructor(
) {
BaseBlueprintTransition(clockViewModel)
.addTransition(
- IntraBlueprintTransition(Config.DEFAULT, clockViewModel)
+ IntraBlueprintTransition(
+ Config.DEFAULT,
+ clockViewModel,
+ smartspaceViewModel
+ )
)
} else {
- IntraBlueprintTransition(Config.DEFAULT, clockViewModel)
+ IntraBlueprintTransition(
+ Config.DEFAULT,
+ clockViewModel,
+ smartspaceViewModel
+ )
}
runTransition(constraintLayout, transition, Config.DEFAULT) {
@@ -136,7 +146,11 @@ constructor(
runTransition(
constraintLayout,
- IntraBlueprintTransition(transition, clockViewModel),
+ IntraBlueprintTransition(
+ transition,
+ clockViewModel,
+ smartspaceViewModel
+ ),
transition,
) {
cs.applyTo(constraintLayout)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
index 7c1368af652c..841f52d7aa64 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
@@ -23,7 +23,7 @@ import android.widget.TextView
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.Flags.keyguardBottomAreaRefactor
-import com.android.systemui.keyguard.ui.viewmodel.AodAlphaViewModel
+import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
@@ -51,7 +51,6 @@ object KeyguardIndicationAreaBinder {
fun bind(
view: ViewGroup,
viewModel: KeyguardIndicationAreaViewModel,
- aodAlphaViewModel: AodAlphaViewModel,
indicationController: KeyguardIndicationController,
): DisposableHandle {
indicationController.setIndicationArea(view)
@@ -68,30 +67,10 @@ object KeyguardIndicationAreaBinder {
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
- if (keyguardBottomAreaRefactor()) {
- aodAlphaViewModel.alpha.collect { alpha ->
- view.apply {
- this.importantForAccessibility =
- if (alpha == 0f) {
- View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
- } else {
- View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
- }
- this.alpha = alpha
- }
- }
- } else {
- viewModel.alpha.collect { alpha ->
- view.apply {
- this.importantForAccessibility =
- if (alpha == 0f) {
- View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
- } else {
- View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
- }
- this.alpha = alpha
- }
- }
+ // Do not independently apply alpha, as [KeyguardRootViewModel] should work
+ // for this and all its children
+ if (!(migrateClocksToBlueprint() || keyguardBottomAreaRefactor())) {
+ viewModel.alpha.collect { alpha -> view.alpha = alpha }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index 5604ef23a142..dc1f33d53853 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -25,6 +25,7 @@ import android.graphics.Rect
import android.view.HapticFeedbackConstants
import android.view.View
import android.view.View.OnLayoutChangeListener
+import android.view.View.VISIBLE
import android.view.ViewGroup
import android.view.ViewGroup.OnHierarchyChangeListener
import android.view.ViewPropertyAnimator
@@ -43,7 +44,7 @@ import com.android.systemui.common.shared.model.Text
import com.android.systemui.common.shared.model.TintedIcon
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
-import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
+import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
@@ -66,6 +67,7 @@ import com.android.systemui.util.ui.isAnimating
import com.android.systemui.util.ui.stopAnimating
import com.android.systemui.util.ui.value
import javax.inject.Provider
+import kotlin.math.min
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.coroutineScope
@@ -102,6 +104,10 @@ object KeyguardRootViewBinder {
val burnInLayerId = R.id.burn_in_layer
val aodNotificationIconContainerId = R.id.aod_notification_icon_container
val largeClockId = R.id.lockscreen_clock_view_large
+ val indicationArea = R.id.keyguard_indication_area
+ val startButton = R.id.start_button
+ val endButton = R.id.end_button
+ val lockIcon = R.id.lock_icon_view
if (keyguardBottomAreaRefactor()) {
view.setOnTouchListener { _, event ->
@@ -146,7 +152,7 @@ object KeyguardRootViewBinder {
}
}
- if (KeyguardShadeMigrationNssl.isEnabled) {
+ if (migrateClocksToBlueprint()) {
launch {
viewModel.burnInLayerVisibility.collect { visibility ->
childViews[burnInLayerId]?.visibility = visibility
@@ -201,10 +207,29 @@ object KeyguardRootViewBinder {
launch {
burnInParams
.flatMapLatest { params -> viewModel.translationX(params) }
- .collect { x ->
- childViews[burnInLayerId]?.translationX = x
- childViews[largeClockId]?.translationX = x
- childViews[aodNotificationIconContainerId]?.translationX = x
+ .collect { state ->
+ val px = state.value ?: return@collect
+ when {
+ state.isToOrFrom(KeyguardState.AOD) -> {
+ childViews[largeClockId]?.translationX = px
+ childViews[burnInLayerId]?.translationX = px
+ childViews[aodNotificationIconContainerId]
+ ?.translationX = px
+ }
+ state.isToOrFrom(KeyguardState.GLANCEABLE_HUB) -> {
+ for ((key, childView) in childViews.entries) {
+ when (key) {
+ indicationArea,
+ startButton,
+ endButton,
+ lockIcon -> {
+ // Do not move these views
+ }
+ else -> childView.translationX = px
+ }
+ }
+ }
+ }
}
}
@@ -316,13 +341,13 @@ object KeyguardRootViewBinder {
}
}
- if (KeyguardShadeMigrationNssl.isEnabled) {
+ if (migrateClocksToBlueprint()) {
burnInParams.update { current ->
current.copy(translationY = { childViews[burnInLayerId]?.translationY })
}
}
- onLayoutChangeListener = OnLayoutChange(viewModel, burnInParams)
+ onLayoutChangeListener = OnLayoutChange(viewModel, childViews, burnInParams)
view.addOnLayoutChangeListener(onLayoutChangeListener)
// Views will be added or removed after the call to bind(). This is needed to avoid many
@@ -382,6 +407,7 @@ object KeyguardRootViewBinder {
private class OnLayoutChange(
private val viewModel: KeyguardRootViewModel,
+ private val childViews: Map<Int, View>,
private val burnInParams: MutableStateFlow<BurnInParameters>,
) : OnLayoutChangeListener {
override fun onLayoutChange(
@@ -395,7 +421,7 @@ object KeyguardRootViewBinder {
oldRight: Int,
oldBottom: Int
) {
- view.findViewById<View>(R.id.nssl_placeholder)?.let { notificationListPlaceholder ->
+ childViews[R.id.nssl_placeholder]?.let { notificationListPlaceholder ->
// After layout, ensure the notifications are positioned correctly
viewModel.onNotificationContainerBoundsChanged(
notificationListPlaceholder.top.toFloat(),
@@ -403,10 +429,34 @@ object KeyguardRootViewBinder {
)
}
- view.findViewById<View>(R.id.keyguard_status_view)?.let { statusView ->
- burnInParams.update { current -> current.copy(statusViewTop = statusView.top) }
+ burnInParams.update { current ->
+ current.copy(
+ minViewY =
+ if (migrateClocksToBlueprint()) {
+ // To ensure burn-in doesn't enroach the top inset, get the min top Y
+ childViews.entries.fold(Int.MAX_VALUE) { currentMin, (viewId, view) ->
+ min(
+ currentMin,
+ if (!isUserVisible(view)) {
+ Int.MAX_VALUE
+ } else {
+ view.getTop()
+ }
+ )
+ }
+ } else {
+ childViews[R.id.keyguard_status_view]?.top ?: 0
+ }
+ )
}
}
+
+ private fun isUserVisible(view: View): Boolean {
+ return view.id != R.id.burn_in_layer &&
+ view.visibility == VISIBLE &&
+ view.width > 0 &&
+ view.height > 0
+ }
}
suspend fun bindAodNotifIconVisibility(
@@ -415,7 +465,9 @@ object KeyguardRootViewBinder {
configuration: ConfigurationState,
screenOffAnimationController: ScreenOffAnimationController,
) {
- KeyguardShadeMigrationNssl.assertInLegacyMode()
+ if (migrateClocksToBlueprint()) {
+ throw IllegalStateException("should only be called in legacy code paths")
+ }
if (NotificationIconContainerRefactor.isUnexpectedlyInLegacyMode()) return
coroutineScope {
val iconAppearTranslationPx =
@@ -444,7 +496,7 @@ object KeyguardRootViewBinder {
}
when {
!isVisible.isAnimating -> {
- if (!KeyguardShadeMigrationNssl.isEnabled) {
+ if (!migrateClocksToBlueprint()) {
translationY = 0f
}
visibility =
@@ -494,7 +546,7 @@ object KeyguardRootViewBinder {
animatorListener: Animator.AnimatorListener,
) {
if (animate) {
- if (!KeyguardShadeMigrationNssl.isEnabled) {
+ if (!migrateClocksToBlueprint()) {
translationY = -iconAppearTranslation.toFloat()
}
alpha = 0f
@@ -502,19 +554,19 @@ object KeyguardRootViewBinder {
.alpha(1f)
.setInterpolator(Interpolators.LINEAR)
.setDuration(AOD_ICONS_APPEAR_DURATION)
- .apply { if (KeyguardShadeMigrationNssl.isEnabled) animateInIconTranslation() }
+ .apply { if (migrateClocksToBlueprint()) animateInIconTranslation() }
.setListener(animatorListener)
.start()
} else {
alpha = 1.0f
- if (!KeyguardShadeMigrationNssl.isEnabled) {
+ if (!migrateClocksToBlueprint()) {
translationY = 0f
}
}
}
private fun View.animateInIconTranslation() {
- if (!KeyguardShadeMigrationNssl.isEnabled) {
+ if (!migrateClocksToBlueprint()) {
animate().animateInIconTranslation().setDuration(AOD_ICONS_APPEAR_DURATION).start()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardIndicationArea.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardIndicationArea.kt
index 78099d9f5d90..a53c6d77d328 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardIndicationArea.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardIndicationArea.kt
@@ -50,6 +50,15 @@ class KeyguardIndicationArea(
)
}
+ override fun setAlpha(alpha: Float) {
+ super.setAlpha(alpha)
+
+ if (alpha == 0f) {
+ importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+ } else {
+ importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
+ }
+ }
private fun indicationTopRow(): KeyguardIndicationTextView {
return KeyguardIndicationTextView(context, attrs).apply {
id = R.id.keyguard_indication_text
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
index bc9671e65f24..77f7ac8571dd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
@@ -33,6 +33,7 @@ import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSec
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultUdfpsAccessibilityOverlaySection
import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSectionsModule.Companion.KEYGUARD_AMBIENT_INDICATION_AREA_SECTION
+import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSliceViewSection
import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection
import java.util.Optional
import javax.inject.Inject
@@ -65,6 +66,7 @@ constructor(
communalTutorialIndicatorSection: CommunalTutorialIndicatorSection,
clockSection: ClockSection,
smartspaceSection: SmartspaceSection,
+ keyguardSliceViewSection: KeyguardSliceViewSection,
udfpsAccessibilityOverlaySection: DefaultUdfpsAccessibilityOverlaySection,
) : KeyguardBlueprint {
override val id: String = DEFAULT
@@ -83,6 +85,7 @@ constructor(
aodBurnInSection,
communalTutorialIndicatorSection,
clockSection,
+ keyguardSliceViewSection,
defaultDeviceEntrySection,
udfpsAccessibilityOverlaySection, // Add LAST: Intentionally has z-order above others
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
index 4f1a754adbd5..b4e57cc93962 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
@@ -17,10 +17,13 @@
package com.android.systemui.keyguard.ui.view.layout.blueprints
-import com.android.systemui.communal.ui.view.layout.blueprints.DefaultCommunalBlueprint
+import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor.Companion.SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID
+import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor.Companion.WEATHER_CLOCK_BLUEPRINT_ID
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
+import com.android.systemui.keyguard.shared.model.KeyguardSection
import dagger.Binds
import dagger.Module
+import dagger.Provides
import dagger.multibindings.IntoSet
@Module
@@ -43,9 +46,25 @@ abstract class KeyguardBlueprintModule {
shortcutsBesideUdfpsLockscreenBlueprint: ShortcutsBesideUdfpsKeyguardBlueprint
): KeyguardBlueprint
- @Binds
- @IntoSet
- abstract fun bindDefaultCommunalBlueprint(
- defaultCommunalBlueprint: DefaultCommunalBlueprint
- ): KeyguardBlueprint
+ companion object {
+ /** This is a place holder for weather clock in compose. */
+ @Provides
+ @IntoSet
+ fun bindWeatherClockBlueprintPlaceHolder(): KeyguardBlueprint {
+ return object : KeyguardBlueprint {
+ override val id: String = WEATHER_CLOCK_BLUEPRINT_ID
+ override val sections: List<KeyguardSection> = listOf()
+ }
+ }
+
+ /** This is a place holder for weather clock in compose. */
+ @Provides
+ @IntoSet
+ fun bindSplitShadeWeatherClockBlueprintPlaceHolder(): KeyguardBlueprint {
+ return object : KeyguardBlueprint {
+ override val id: String = SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID
+ override val sections: List<KeyguardSection> = listOf()
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
index d118d4d11948..55b2381c79e4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
@@ -33,6 +33,7 @@ import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSec
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultUdfpsAccessibilityOverlaySection
import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSectionsModule
+import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSliceViewSection
import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection
import com.android.systemui.util.kotlin.getOrNull
import java.util.Optional
@@ -60,6 +61,7 @@ constructor(
communalTutorialIndicatorSection: CommunalTutorialIndicatorSection,
clockSection: ClockSection,
smartspaceSection: SmartspaceSection,
+ keyguardSliceViewSection: KeyguardSliceViewSection,
udfpsAccessibilityOverlaySection: DefaultUdfpsAccessibilityOverlaySection,
) : KeyguardBlueprint {
override val id: String = SHORTCUTS_BESIDE_UDFPS
@@ -78,6 +80,7 @@ constructor(
aodBurnInSection,
communalTutorialIndicatorSection,
clockSection,
+ keyguardSliceViewSection,
defaultDeviceEntrySection,
udfpsAccessibilityOverlaySection, // Add LAST: Intentionally has z-order above others
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt
index a7075d97459e..3adeb2aeb283 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt
@@ -20,10 +20,12 @@ import android.transition.TransitionSet
import com.android.systemui.keyguard.ui.view.layout.sections.transitions.ClockSizeTransition
import com.android.systemui.keyguard.ui.view.layout.sections.transitions.DefaultClockSteppingTransition
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
class IntraBlueprintTransition(
config: IntraBlueprintTransition.Config,
clockViewModel: KeyguardClockViewModel,
+ smartspaceViewModel: KeyguardSmartspaceViewModel,
) : TransitionSet() {
enum class Type(
@@ -56,7 +58,7 @@ class IntraBlueprintTransition(
Type.NoTransition -> {}
Type.DefaultClockStepping ->
addTransition(clockViewModel.clock?.let { DefaultClockSteppingTransition(it) })
- else -> addTransition(ClockSizeTransition(config, clockViewModel))
+ else -> addTransition(ClockSizeTransition(config, clockViewModel, smartspaceViewModel))
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInLayer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInLayer.kt
index 67a20e588198..dc2eeac872f5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInLayer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInLayer.kt
@@ -18,9 +18,12 @@ package com.android.systemui.keyguard.ui.view.layout.sections
import android.content.Context
import android.view.View
+import android.view.ViewTreeObserver.OnPreDrawListener
import androidx.constraintlayout.helper.widget.Layer
-class AodBurnInLayer(context: Context) : Layer(context) {
+class AodBurnInLayer(
+ context: Context,
+) : Layer(context) {
// For setScale in Layer class, it stores it in mScaleX/Y and directly apply scale to
// referenceViews instead of keeping the value in fields of View class
// when we try to clone ConstraintSet, it will call getScaleX from View class and return 1.0
@@ -28,13 +31,32 @@ class AodBurnInLayer(context: Context) : Layer(context) {
// which cause the flicker from AOD to LS
private var _scaleX = 1F
private var _scaleY = 1F
+
// As described for _scaleX and _scaleY, we have similar issue with translation
- private var _translationX = 1F
- private var _translationY = 1F
+ private var _translationX = 0F
+ private var _translationY = 0F
+
+ private val _predrawListener = OnPreDrawListener {
+ super.setScaleX(_scaleX)
+ super.setScaleY(_scaleY)
+ super.setTranslationX(_translationX)
+ super.setTranslationY(_translationY)
+ true
+ }
+
// avoid adding views with same ids
override fun addView(view: View?) {
view?.let { if (it.id !in referencedIds) super.addView(view) }
}
+
+ fun registerListener(rootView: View) {
+ rootView.viewTreeObserver.addOnPreDrawListener(_predrawListener)
+ }
+
+ fun unregisterListener(rootView: View) {
+ rootView.viewTreeObserver.removeOnPreDrawListener(_predrawListener)
+ }
+
override fun setScaleX(scaleX: Float) {
_scaleX = scaleX
super.setScaleX(scaleX)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt
index 9a1fcc1a6a51..98bebd091f1a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt
@@ -22,8 +22,8 @@ import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import com.android.systemui.Flags.migrateClocksToBlueprint
-import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.keyguard.ui.view.KeyguardRootView
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
import com.android.systemui.res.R
import javax.inject.Inject
@@ -33,11 +33,12 @@ class AodBurnInSection
@Inject
constructor(
private val context: Context,
+ private val rootView: KeyguardRootView,
private val clockViewModel: KeyguardClockViewModel,
) : KeyguardSection() {
private lateinit var burnInLayer: AodBurnInLayer
override fun addViews(constraintLayout: ConstraintLayout) {
- if (!KeyguardShadeMigrationNssl.isEnabled) {
+ if (!migrateClocksToBlueprint()) {
return
}
@@ -47,6 +48,7 @@ constructor(
burnInLayer =
AodBurnInLayer(context).apply {
id = R.id.burn_in_layer
+ registerListener(rootView)
addView(emptyView)
if (!migrateClocksToBlueprint()) {
val statusView =
@@ -58,21 +60,20 @@ constructor(
}
override fun bindData(constraintLayout: ConstraintLayout) {
- if (!KeyguardShadeMigrationNssl.isEnabled) {
+ if (!migrateClocksToBlueprint()) {
return
}
- if (migrateClocksToBlueprint()) {
- clockViewModel.burnInLayer = burnInLayer
- }
+ clockViewModel.burnInLayer = burnInLayer
}
override fun applyConstraints(constraintSet: ConstraintSet) {
- if (!KeyguardShadeMigrationNssl.isEnabled) {
+ if (!migrateClocksToBlueprint()) {
return
}
}
override fun removeViews(constraintLayout: ConstraintLayout) {
+ burnInLayer.unregisterListener(rootView)
constraintLayout.removeView(R.id.burn_in_layer)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
index ad589dfcff9e..3d9c04e39679 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
@@ -28,7 +28,6 @@ import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.common.ui.ConfigurationState
-import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.res.R
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.AlwaysOnDisplayNotificationIconViewStore
@@ -59,7 +58,7 @@ constructor(
private lateinit var nic: NotificationIconContainer
override fun addViews(constraintLayout: ConstraintLayout) {
- if (!KeyguardShadeMigrationNssl.isEnabled) {
+ if (!migrateClocksToBlueprint()) {
return
}
nic =
@@ -78,7 +77,7 @@ constructor(
}
override fun bindData(constraintLayout: ConstraintLayout) {
- if (!KeyguardShadeMigrationNssl.isEnabled) {
+ if (!migrateClocksToBlueprint()) {
return
}
@@ -99,7 +98,7 @@ constructor(
}
override fun applyConstraints(constraintSet: ConstraintSet) {
- if (!KeyguardShadeMigrationNssl.isEnabled) {
+ if (!migrateClocksToBlueprint()) {
return
}
val bottomMargin =
@@ -114,12 +113,9 @@ constructor(
BOTTOM
}
constraintSet.apply {
- if (migrateClocksToBlueprint()) {
- connect(nicId, TOP, R.id.smart_space_barrier_bottom, BOTTOM, bottomMargin)
- setGoneMargin(nicId, BOTTOM, bottomMargin)
- } else {
- connect(nicId, TOP, R.id.keyguard_status_view, topAlignment, bottomMargin)
- }
+ connect(nicId, TOP, R.id.smart_space_barrier_bottom, BOTTOM, bottomMargin)
+ setGoneMargin(nicId, BOTTOM, bottomMargin)
+
connect(
nicId,
START,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
index 54a7ca47d4e7..a22671d42e94 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
@@ -205,7 +205,7 @@ constructor(
fun getDimen(context: Context, name: String): Int {
val res = context.packageManager.getResourcesForApplication(context.packageName)
val id = res.getIdentifier(name, "dimen", context.packageName)
- return res.getDimensionPixelSize(id)
+ return if (id == 0) 0 else res.getDimensionPixelSize(id)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
index ea05c1d878b8..3361343423a9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
@@ -25,7 +25,6 @@ import com.android.systemui.Flags.keyguardBottomAreaRefactor
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
import com.android.systemui.keyguard.ui.view.KeyguardIndicationArea
-import com.android.systemui.keyguard.ui.viewmodel.AodAlphaViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
import com.android.systemui.res.R
import com.android.systemui.statusbar.KeyguardIndicationController
@@ -37,7 +36,6 @@ class DefaultIndicationAreaSection
constructor(
private val context: Context,
private val keyguardIndicationAreaViewModel: KeyguardIndicationAreaViewModel,
- private val aodAlphaViewModel: AodAlphaViewModel,
private val indicationController: KeyguardIndicationController,
) : KeyguardSection() {
private val indicationAreaViewId = R.id.keyguard_indication_area
@@ -56,7 +54,6 @@ constructor(
KeyguardIndicationAreaBinder.bind(
constraintLayout.requireViewById(R.id.keyguard_indication_area),
keyguardIndicationAreaViewModel,
- aodAlphaViewModel,
indicationController,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
index 75132a59eb88..6a3b920f9692 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
@@ -24,10 +24,9 @@ import androidx.constraintlayout.widget.ConstraintSet.END
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
-import com.android.systemui.Flags.centralizedStatusBarDimensRefactor
+import com.android.systemui.Flags.centralizedStatusBarHeightFix
import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlags
import com.android.systemui.shade.LargeScreenHeaderHelper
@@ -71,7 +70,7 @@ constructor(
mainDispatcher,
) {
override fun applyConstraints(constraintSet: ConstraintSet) {
- if (!KeyguardShadeMigrationNssl.isEnabled) {
+ if (!migrateClocksToBlueprint()) {
return
}
constraintSet.apply {
@@ -81,7 +80,7 @@ constructor(
val useLargeScreenHeader =
context.resources.getBoolean(R.bool.config_use_large_screen_shade_header)
val marginTopLargeScreen =
- if (centralizedStatusBarDimensRefactor()) {
+ if (centralizedStatusBarHeightFix()) {
largeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight()
} else {
context.resources.getDimensionPixelSize(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
index 851a45f31705..6e8605bde864 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
@@ -31,10 +31,10 @@ import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
import com.android.keyguard.KeyguardStatusView
import com.android.keyguard.dagger.KeyguardStatusViewComponent
+import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.keyguard.KeyguardViewConfigurator
-import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.keyguard.shared.model.KeyguardSection
-import com.android.systemui.media.controls.ui.KeyguardMediaController
+import com.android.systemui.media.controls.ui.controller.KeyguardMediaController
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
import com.android.systemui.shade.NotificationPanelViewController
@@ -58,7 +58,7 @@ constructor(
private val statusViewId = R.id.keyguard_status_view
override fun addViews(constraintLayout: ConstraintLayout) {
- if (!KeyguardShadeMigrationNssl.isEnabled) {
+ if (!migrateClocksToBlueprint()) {
return
}
// At startup, 2 views with the ID `R.id.keyguard_status_view` will be available.
@@ -83,7 +83,7 @@ constructor(
}
override fun bindData(constraintLayout: ConstraintLayout) {
- if (KeyguardShadeMigrationNssl.isEnabled) {
+ if (migrateClocksToBlueprint()) {
constraintLayout.findViewById<KeyguardStatusView?>(R.id.keyguard_status_view)?.let {
val statusViewComponent =
keyguardStatusViewComponentFactory.build(it, context.display)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/KeyguardSliceViewSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/KeyguardSliceViewSection.kt
new file mode 100644
index 000000000000..d572c51d1146
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/KeyguardSliceViewSection.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2024 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.ui.view.layout.sections
+
+import android.view.View
+import android.view.ViewGroup
+import androidx.constraintlayout.widget.Barrier
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
+import com.android.systemui.Flags.migrateClocksToBlueprint
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
+import javax.inject.Inject
+
+class KeyguardSliceViewSection
+@Inject
+constructor(
+ val smartspaceController: LockscreenSmartspaceController,
+) : KeyguardSection() {
+ override fun addViews(constraintLayout: ConstraintLayout) {
+ if (!migrateClocksToBlueprint()) return
+ if (smartspaceController.isEnabled()) return
+
+ constraintLayout.findViewById<View?>(R.id.keyguard_slice_view)?.let {
+ (it.parent as ViewGroup).removeView(it)
+ constraintLayout.addView(it)
+ }
+ }
+
+ override fun bindData(constraintLayout: ConstraintLayout) {}
+
+ override fun applyConstraints(constraintSet: ConstraintSet) {
+ if (!migrateClocksToBlueprint()) return
+ if (smartspaceController.isEnabled()) return
+
+ constraintSet.apply {
+ connect(
+ R.id.keyguard_slice_view,
+ ConstraintSet.START,
+ ConstraintSet.PARENT_ID,
+ ConstraintSet.START
+ )
+ connect(
+ R.id.keyguard_slice_view,
+ ConstraintSet.END,
+ ConstraintSet.PARENT_ID,
+ ConstraintSet.END
+ )
+ constrainHeight(R.id.keyguard_slice_view, ConstraintSet.WRAP_CONTENT)
+
+ connect(
+ R.id.keyguard_slice_view,
+ ConstraintSet.TOP,
+ R.id.lockscreen_clock_view,
+ ConstraintSet.BOTTOM
+ )
+
+ createBarrier(
+ R.id.smart_space_barrier_bottom,
+ Barrier.BOTTOM,
+ 0,
+ *intArrayOf(R.id.keyguard_slice_view)
+ )
+ }
+ }
+
+ override fun removeViews(constraintLayout: ConstraintLayout) {
+ if (!migrateClocksToBlueprint()) return
+ if (smartspaceController.isEnabled()) return
+
+ constraintLayout.removeView(R.id.keyguard_slice_view)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt
index 52d94a087110..02c889dd771d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt
@@ -25,8 +25,8 @@ import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
import androidx.constraintlayout.widget.ConstraintSet.TOP
+import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
-import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlags
@@ -83,7 +83,7 @@ constructor(
}
override fun addViews(constraintLayout: ConstraintLayout) {
- if (!KeyguardShadeMigrationNssl.isEnabled) {
+ if (!migrateClocksToBlueprint()) {
return
}
// This moves the existing NSSL view to a different parent, as the controller is a
@@ -99,7 +99,7 @@ constructor(
}
override fun bindData(constraintLayout: ConstraintLayout) {
- if (!KeyguardShadeMigrationNssl.isEnabled) {
+ if (!migrateClocksToBlueprint()) {
return
}
@@ -111,7 +111,7 @@ constructor(
sceneContainerFlags,
controller,
notificationStackSizeCalculator,
- mainDispatcher,
+ mainImmediateDispatcher = mainDispatcher,
)
)
@@ -123,6 +123,7 @@ constructor(
notificationStackAppearanceViewModel,
ambientState,
controller,
+ mainImmediateDispatcher = mainDispatcher,
)
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
index 8255bcc87400..b0f7a258a4e6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
@@ -57,6 +57,7 @@ constructor(
override fun addViews(constraintLayout: ConstraintLayout) {
if (!migrateClocksToBlueprint()) return
+ if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return
smartspaceView = smartspaceController.buildAndConnectView(constraintLayout)
weatherView = smartspaceController.buildAndConnectWeatherView(constraintLayout)
dateView = smartspaceController.buildAndConnectDateView(constraintLayout)
@@ -83,6 +84,7 @@ constructor(
override fun bindData(constraintLayout: ConstraintLayout) {
if (!migrateClocksToBlueprint()) return
+ if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return
KeyguardSmartspaceViewBinder.bind(
constraintLayout,
keyguardClockViewModel,
@@ -93,6 +95,7 @@ constructor(
override fun applyConstraints(constraintSet: ConstraintSet) {
if (!migrateClocksToBlueprint()) return
+ if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return
val horizontalPaddingStart =
context.resources.getDimensionPixelSize(R.dimen.below_clock_padding_start) +
context.resources.getDimensionPixelSize(R.dimen.status_view_margin_horizontal)
@@ -189,6 +192,7 @@ constructor(
override fun removeViews(constraintLayout: ConstraintLayout) {
if (!migrateClocksToBlueprint()) return
+ if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return
listOf(smartspaceView, dateView, weatherView).forEach {
it?.let {
if (it.parent == constraintLayout) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeMediaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeMediaSection.kt
index b12a8a811955..21e945582aff 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeMediaSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeMediaSection.kt
@@ -30,7 +30,7 @@ import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.keyguard.shared.model.KeyguardSection
-import com.android.systemui.media.controls.ui.KeyguardMediaController
+import com.android.systemui.media.controls.ui.controller.KeyguardMediaController
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
index 3e35ae4b2dc3..2545302ccaa1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
@@ -23,8 +23,8 @@ import androidx.constraintlayout.widget.ConstraintSet.END
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
+import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlags
@@ -67,7 +67,7 @@ constructor(
mainDispatcher,
) {
override fun applyConstraints(constraintSet: ConstraintSet) {
- if (!KeyguardShadeMigrationNssl.isEnabled) {
+ if (!migrateClocksToBlueprint()) {
return
}
constraintSet.apply {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
index 64cbb3229a57..f65f3760f082 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
@@ -31,8 +31,10 @@ import com.android.app.animation.Interpolators
import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition
import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Type
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
import com.android.systemui.res.R
import com.android.systemui.shared.R as sharedR
+import com.google.android.material.math.MathUtils
import kotlin.math.abs
internal fun View.setRect(rect: Rect) =
@@ -41,12 +43,13 @@ internal fun View.setRect(rect: Rect) =
class ClockSizeTransition(
config: IntraBlueprintTransition.Config,
clockViewModel: KeyguardClockViewModel,
+ smartspaceViewModel: KeyguardSmartspaceViewModel,
) : TransitionSet() {
init {
ordering = ORDERING_TOGETHER
if (config.type != Type.SmartspaceVisibility) {
- addTransition(ClockFaceOutTransition(config, clockViewModel))
- addTransition(ClockFaceInTransition(config, clockViewModel))
+ addTransition(ClockFaceOutTransition(config, clockViewModel, smartspaceViewModel))
+ addTransition(ClockFaceInTransition(config, clockViewModel, smartspaceViewModel))
}
addTransition(SmartspaceMoveTransition(config, clockViewModel))
}
@@ -57,15 +60,6 @@ class ClockSizeTransition(
override fun captureEndValues(transition: TransitionValues) = captureValues(transition)
override fun captureStartValues(transition: TransitionValues) = captureValues(transition)
override fun getTransitionProperties(): Array<String> = TRANSITION_PROPERTIES
- open fun mutateBounds(
- view: View,
- fromVis: Int,
- toVis: Int,
- fromBounds: Rect,
- toBounds: Rect,
- fromSSBounds: Rect?,
- toSSBounds: Rect?
- ) {}
private fun captureValues(transition: TransitionValues) {
val view = transition.view
@@ -79,6 +73,16 @@ class ClockSizeTransition(
transition.values[SMARTSPACE_BOUNDS] = Rect(ss.left, ss.top, ss.right, ss.bottom)
}
+ open fun mutateBounds(
+ view: View,
+ fromIsVis: Boolean,
+ toIsVis: Boolean,
+ fromBounds: Rect,
+ toBounds: Rect,
+ fromSSBounds: Rect?,
+ toSSBounds: Rect?
+ ) {}
+
override fun createAnimator(
sceenRoot: ViewGroup,
startValues: TransitionValues?,
@@ -86,7 +90,6 @@ class ClockSizeTransition(
): Animator? {
if (startValues == null || endValues == null) return null
- val fromView = startValues.view
var fromVis = startValues.values[PROP_VISIBILITY] as Int
var fromIsVis = fromVis == View.VISIBLE
var fromAlpha = startValues.values[PROP_ALPHA] as Float
@@ -109,7 +112,7 @@ class ClockSizeTransition(
fromVis = View.INVISIBLE
}
- mutateBounds(toView, fromVis, toVis, fromBounds, toBounds, fromSSBounds, toSSBounds)
+ mutateBounds(toView, fromIsVis, toIsVis, fromBounds, toBounds, fromSSBounds, toSSBounds)
if (fromIsVis == toIsVis && fromBounds.equals(toBounds)) {
if (DEBUG) {
Log.w(
@@ -125,7 +128,7 @@ class ClockSizeTransition(
val sendToBack = fromIsVis && !toIsVis
fun lerp(start: Int, end: Int, fract: Float): Int =
- (start * (1f - fract) + end * fract).toInt()
+ MathUtils.lerp(start.toFloat(), end.toFloat(), fract).toInt()
fun computeBounds(fract: Float): Rect =
Rect(
lerp(fromBounds.left, toBounds.left, fract),
@@ -134,9 +137,14 @@ class ClockSizeTransition(
lerp(fromBounds.bottom, toBounds.bottom, fract)
)
- fun assignAnimValues(src: String, alpha: Float, fract: Float, vis: Int? = null) {
+ fun assignAnimValues(src: String, fract: Float, vis: Int? = null) {
val bounds = computeBounds(fract)
- if (DEBUG) Log.i(TAG, "$src: $toView; alpha=$alpha; vis=$vis; bounds=$bounds;")
+ val alpha = MathUtils.lerp(fromAlpha, toAlpha, fract)
+ if (DEBUG)
+ Log.i(
+ TAG,
+ "$src: $toView; fract=$fract; alpha=$alpha; vis=$vis; bounds=$bounds;"
+ )
toView.setVisibility(vis ?: View.VISIBLE)
toView.setAlpha(alpha)
toView.setRect(bounds)
@@ -152,12 +160,12 @@ class ClockSizeTransition(
)
}
- return ValueAnimator.ofFloat(fromAlpha, toAlpha).also { anim ->
+ return ValueAnimator.ofFloat(0f, 1f).also { anim ->
// We enforce the animation parameters on the target view every frame using a
// predraw listener. This is suboptimal but prevents issues with layout passes
// overwriting the animation for individual frames.
val predrawCallback = OnPreDrawListener {
- assignAnimValues("predraw", anim.animatedValue as Float, anim.animatedFraction)
+ assignAnimValues("predraw", anim.animatedFraction)
return@OnPreDrawListener true
}
@@ -167,16 +175,18 @@ class ClockSizeTransition(
anim.addListener(
object : AnimatorListenerAdapter() {
override fun onAnimationStart(anim: Animator) {
- assignAnimValues("start", fromAlpha, 0f)
+ assignAnimValues("start", 0f, fromVis)
}
override fun onAnimationEnd(anim: Animator) {
- assignAnimValues("end", toAlpha, 1f, toVis)
+ assignAnimValues("end", 1f, toVis)
if (sendToBack) toView.translationZ = 0f
toView.viewTreeObserver.removeOnPreDrawListener(predrawCallback)
}
}
)
+
+ assignAnimValues("init", 0f, fromVis)
toView.viewTreeObserver.addOnPreDrawListener(predrawCallback)
}
}
@@ -197,12 +207,13 @@ class ClockSizeTransition(
class ClockFaceInTransition(
config: IntraBlueprintTransition.Config,
val viewModel: KeyguardClockViewModel,
+ val smartspaceViewModel: KeyguardSmartspaceViewModel,
) : VisibilityBoundsTransition() {
init {
duration = CLOCK_IN_MILLIS
startDelay = CLOCK_IN_START_DELAY_MILLIS
interpolator = CLOCK_IN_INTERPOLATOR
- captureSmartspace = !viewModel.useLargeClock
+ captureSmartspace = !viewModel.useLargeClock && smartspaceViewModel.isSmartspaceEnabled
if (viewModel.useLargeClock) {
viewModel.clock?.let { it.largeClock.layout.views.forEach { addTarget(it) } }
@@ -213,13 +224,16 @@ class ClockSizeTransition(
override fun mutateBounds(
view: View,
- fromVis: Int,
- toVis: Int,
+ fromIsVis: Boolean,
+ toIsVis: Boolean,
fromBounds: Rect,
toBounds: Rect,
fromSSBounds: Rect?,
toSSBounds: Rect?
) {
+ // Move normally if clock is not changing visibility
+ if (fromIsVis == toIsVis) return
+
fromBounds.left = toBounds.left
fromBounds.right = toBounds.right
if (viewModel.useLargeClock) {
@@ -252,11 +266,12 @@ class ClockSizeTransition(
class ClockFaceOutTransition(
config: IntraBlueprintTransition.Config,
val viewModel: KeyguardClockViewModel,
+ val smartspaceViewModel: KeyguardSmartspaceViewModel,
) : VisibilityBoundsTransition() {
init {
duration = CLOCK_OUT_MILLIS
interpolator = CLOCK_OUT_INTERPOLATOR
- captureSmartspace = viewModel.useLargeClock
+ captureSmartspace = viewModel.useLargeClock && smartspaceViewModel.isSmartspaceEnabled
if (viewModel.useLargeClock) {
addTarget(R.id.lockscreen_clock_view)
@@ -267,13 +282,16 @@ class ClockSizeTransition(
override fun mutateBounds(
view: View,
- fromVis: Int,
- toVis: Int,
+ fromIsVis: Boolean,
+ toIsVis: Boolean,
fromBounds: Rect,
toBounds: Rect,
fromSSBounds: Rect?,
toSSBounds: Rect?
) {
+ // Move normally if clock is not changing visibility
+ if (fromIsVis == toIsVis) return
+
toBounds.left = fromBounds.left
toBounds.right = fromBounds.right
if (!viewModel.useLargeClock) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/DefaultClockSteppingTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/DefaultClockSteppingTransition.kt
index 60ab40c0a16b..f60115517111 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/DefaultClockSteppingTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/DefaultClockSteppingTransition.kt
@@ -78,8 +78,8 @@ class DefaultClockSteppingTransition(
}
companion object {
- private const val PROP_BOUNDS_LEFT = "splitShadeTransitionAdapter:boundsLeft"
- private const val PROP_X_IN_WINDOW = "splitShadeTransitionAdapter:xInWindow"
+ private const val PROP_BOUNDS_LEFT = "DefaultClockSteppingTransition:boundsLeft"
+ private const val PROP_X_IN_WINDOW = "DefaultClockSteppingTransition:xInWindow"
private val TRANSITION_PROPERTIES = arrayOf(PROP_BOUNDS_LEFT, PROP_X_IN_WINDOW)
private const val KEYGUARD_STATUS_VIEW_CUSTOM_CLOCK_MOVE_DURATION_MS = 1000L
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerMessageAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerMessageAreaViewModel.kt
index 49c64bdcdd23..9edb4d159d4a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerMessageAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerMessageAreaViewModel.kt
@@ -40,23 +40,17 @@ constructor(
alternateBouncerInteractor: AlternateBouncerInteractor,
) {
- private val faceHelp: Flow<FaceMessage> =
- biometricMessageInteractor.faceMessage.filterNot { faceMessage ->
- faceMessage !is FaceTimeoutMessage
- }
- private val fingerprintMessages: Flow<FingerprintMessage> =
- biometricMessageInteractor.fingerprintMessage.filterNot { fingerprintMessage ->
- // On lockout, the device will show the bouncer. Let's not show the message
- // before the transition or else it'll look flickery.
- fingerprintMessage is FingerprintLockoutMessage
- }
+ private val faceMessage: Flow<FaceMessage> =
+ biometricMessageInteractor.faceMessage.filterNot { it is FaceTimeoutMessage }
+ private val fingerprintMessage: Flow<FingerprintMessage> =
+ biometricMessageInteractor.fingerprintMessage.filterNot { it is FingerprintLockoutMessage }
val message: Flow<BiometricMessage?> =
alternateBouncerInteractor.isVisible.flatMapLatest { isVisible ->
if (isVisible) {
merge(
- faceHelp,
- fingerprintMessages,
+ faceMessage,
+ fingerprintMessage,
)
} else {
flowOf(null)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt
index f208e85bde3e..8a3b57ba027f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt
@@ -18,7 +18,9 @@
package com.android.systemui.keyguard.ui.viewmodel
+import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
@@ -38,6 +40,7 @@ constructor(
keyguardTransitionInteractor: KeyguardTransitionInteractor,
goneToAodTransitionViewModel: GoneToAodTransitionViewModel,
goneToDozingTransitionViewModel: GoneToDozingTransitionViewModel,
+ keyguardInteractor: KeyguardInteractor,
) {
/** The alpha level for the entire lockscreen while in AOD. */
@@ -46,7 +49,8 @@ constructor(
keyguardTransitionInteractor.transitions,
goneToAodTransitionViewModel.enterFromTopAnimationAlpha.onStart { emit(0f) },
goneToDozingTransitionViewModel.lockscreenAlpha.onStart { emit(0f) },
- ) { step, goneToAodAlpha, goneToDozingAlpha ->
+ keyguardInteractor.keyguardAlpha.onStart { emit(1f) },
+ ) { step, goneToAodAlpha, goneToDozingAlpha, keyguardAlpha ->
if (step.to == GONE) {
// When transitioning to GONE, only emit a value when complete as other
// transitions may be controlling the alpha fade
@@ -57,6 +61,8 @@ constructor(
emit(goneToAodAlpha)
} else if (step.from == GONE && step.to == DOZING) {
emit(goneToDozingAlpha)
+ } else if (!migrateClocksToBlueprint()) {
+ emit(keyguardAlpha)
}
}
.distinctUntilChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
index 6fcbf48eab82..8665aabc3ef7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
@@ -158,9 +158,9 @@ constructor(
val burnInY = MathUtils.lerp(0, burnIn.translationY, interpolated).toInt()
val translationY =
if (Flags.migrateClocksToBlueprint()) {
- burnInY
+ max(params.topInset - params.minViewY, burnInY)
} else {
- max(params.topInset, params.statusViewTop + burnInY) - params.statusViewTop
+ max(params.topInset, params.minViewY + burnInY) - params.minViewY
}
BurnInModel(
@@ -194,8 +194,8 @@ data class BurnInParameters(
val clockControllerProvider: Provider<ClockController>? = null,
/** System insets that keyguard needs to stay out of */
val topInset: Int = 0,
- /** Status view top, without translation added in */
- val statusViewTop: Int = 0,
+ /** The min y-value of the visible elements on lockscreen */
+ val minViewY: Int = Int.MAX_VALUE,
/** The current y translation of the view */
val translationY: () -> Float? = { null }
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
index a3888c3341db..c40902871388 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
@@ -25,10 +25,13 @@ import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.StateToValue
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
/**
* Breaks down AOD->LOCKSCREEN transition into discrete steps for corresponding views to consume.
@@ -39,6 +42,7 @@ class AodToLockscreenTransitionViewModel
@Inject
constructor(
deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+ shadeInteractor: ShadeInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
) : DeviceEntryIconTransition {
@@ -64,26 +68,32 @@ constructor(
/** Ensure alpha is set to be visible */
fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> {
- var startAlpha: Float? = null
+ var startAlpha = 1f
return transitionAnimation.sharedFlow(
duration = 500.milliseconds,
- onStep = {
- if (startAlpha == null) {
- startAlpha = viewState.alpha()
- }
- MathUtils.lerp(startAlpha!!, 1f, it)
- },
- onFinish = {
- startAlpha = null
- 1f
- },
- onCancel = {
- startAlpha = null
- 1f
- },
+ onStart = { startAlpha = viewState.alpha() },
+ onStep = { MathUtils.lerp(startAlpha, 1f, it) },
)
}
+ val notificationAlpha: Flow<Float> =
+ combine(
+ shadeInteractor.shadeExpansion.map { it > 0f },
+ shadeInteractor.qsExpansion.map { it > 0f },
+ transitionAnimation.sharedFlow(
+ duration = 500.milliseconds,
+ onStep = { it },
+ onCancel = { 1f },
+ ),
+ ) { isShadeExpanded, isQsExpanded, alpha ->
+ if (isShadeExpanded || isQsExpanded) {
+ // One example of this happening is dragging a notification while pulsing on AOD
+ 1f
+ } else {
+ alpha
+ }
+ }
+
val shortcutsAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
duration = 167.milliseconds,
@@ -102,7 +112,6 @@ constructor(
override val deviceEntryParentViewAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
duration = 500.milliseconds,
- onStart = { 1f },
onStep = { 1f },
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt
new file mode 100644
index 000000000000..c64f277b519a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2024 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.ui.viewmodel
+
+import com.android.app.animation.Interpolators.EMPHASIZED
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flatMapLatest
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class DreamingToGlanceableHubTransitionViewModel
+@Inject
+constructor(
+ animationFlow: KeyguardTransitionAnimationFlow,
+ configurationInteractor: ConfigurationInteractor,
+) {
+
+ private val transitionAnimation =
+ animationFlow.setup(
+ duration = TO_GLANCEABLE_HUB_DURATION,
+ from = KeyguardState.DREAMING,
+ to = KeyguardState.GLANCEABLE_HUB,
+ )
+
+ val dreamOverlayTranslationX: Flow<Float> =
+ configurationInteractor
+ .dimensionPixelSize(R.dimen.dreaming_to_hub_transition_dream_overlay_translation_x)
+ .flatMapLatest { translatePx ->
+ transitionAnimation.sharedFlow(
+ duration = TO_GLANCEABLE_HUB_DURATION,
+ onStep = { value -> value * translatePx },
+ interpolator = EMPHASIZED,
+ onCancel = { 0f },
+ name = "DREAMING->GLANCEABLE_HUB: overlayTranslationX",
+ )
+ }
+
+ val dreamOverlayAlpha: Flow<Float> =
+ transitionAnimation.sharedFlow(
+ duration = 167.milliseconds,
+ onStep = { 1f - it },
+ name = "DREAMING->GLANCEABLE_HUB: dreamOverlayAlpha",
+ )
+
+ private companion object {
+ val TO_GLANCEABLE_HUB_DURATION = 1.seconds
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt
new file mode 100644
index 000000000000..478c4faa1be3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2024 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.ui.viewmodel
+
+import com.android.app.animation.Interpolators
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flatMapLatest
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class GlanceableHubToDreamingTransitionViewModel
+@Inject
+constructor(
+ animationFlow: KeyguardTransitionAnimationFlow,
+ configurationInteractor: ConfigurationInteractor,
+) {
+
+ private val transitionAnimation =
+ animationFlow.setup(
+ duration = FROM_GLANCEABLE_HUB_DURATION,
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.DREAMING,
+ )
+
+ val dreamOverlayAlpha: Flow<Float> =
+ transitionAnimation.sharedFlow(
+ duration = 167.milliseconds,
+ startTime = 167.milliseconds,
+ onStep = { it },
+ name = "GLANCEABLE_HUB->DREAMING: dreamOverlayAlpha",
+ )
+
+ val dreamOverlayTranslationX: Flow<Float> =
+ configurationInteractor
+ .dimensionPixelSize(R.dimen.hub_to_dreaming_transition_dream_overlay_translation_x)
+ .flatMapLatest { translatePx: Int ->
+ transitionAnimation.sharedFlow(
+ duration = FROM_GLANCEABLE_HUB_DURATION,
+ onStep = { value -> -translatePx + value * translatePx },
+ interpolator = Interpolators.EMPHASIZED,
+ onCancel = { -translatePx.toFloat() },
+ name = "GLANCEABLE_HUB->LOCKSCREEN: dreamOverlayTranslationX"
+ )
+ }
+
+ private companion object {
+ val FROM_GLANCEABLE_HUB_DURATION = 1.seconds
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt
index 6aa2ecabae75..e5b596419efe 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt
@@ -16,13 +16,22 @@
package com.android.systemui.keyguard.ui.viewmodel
+import com.android.app.animation.Interpolators.EMPHASIZED
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.domain.interactor.FromGlanceableHubTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.FromGlanceableHubTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.StateToValue
+import com.android.systemui.res.R
import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
/**
* Breaks down GLANCEABLE_HUB->LOCKSCREEN transition into discrete steps for corresponding views to
@@ -33,32 +42,43 @@ import kotlinx.coroutines.flow.Flow
class GlanceableHubToLockscreenTransitionViewModel
@Inject
constructor(
+ configurationInteractor: ConfigurationInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
) {
private val transitionAnimation =
animationFlow.setup(
- duration = FromGlanceableHubTransitionInteractor.TO_LOCKSCREEN_DURATION,
+ duration = TO_LOCKSCREEN_DURATION,
from = KeyguardState.GLANCEABLE_HUB,
to = KeyguardState.LOCKSCREEN,
)
- // TODO(b/315205222): implement full animation spec instead of just a simple fade.
val keyguardAlpha: Flow<Float> =
- transitionAnimation.sharedFlow(
- duration = FromGlanceableHubTransitionInteractor.TO_LOCKSCREEN_DURATION,
- onStep = { it },
- onFinish = { 1f },
- onCancel = { 0f },
- name = "GLANCEABLE_HUB->LOCKSCREEN: keyguardAlpha",
- )
+ transitionAnimation
+ .sharedFlow(
+ duration = 167.milliseconds,
+ startTime = 167.milliseconds,
+ onStep = { it },
+ onFinish = { 1f },
+ onCancel = { 0f },
+ name = "GLANCEABLE_HUB->LOCKSCREEN: keyguardAlpha",
+ )
+ .onStart { emit(0f) }
- // TODO(b/315205216): implement full animation spec instead of just a simple fade.
- val notificationAlpha: Flow<Float> =
- transitionAnimation.sharedFlow(
- duration = FromGlanceableHubTransitionInteractor.TO_LOCKSCREEN_DURATION,
- onStep = { it },
- onFinish = { 1f },
- onCancel = { 0f },
- name = "GLANCEABLE_HUB->LOCKSCREEN: notificationAlpha",
- )
+ val keyguardTranslationX: Flow<StateToValue> =
+ configurationInteractor
+ .dimensionPixelSize(R.dimen.hub_to_lockscreen_transition_lockscreen_translation_x)
+ .flatMapLatest { translatePx: Int ->
+ transitionAnimation.sharedFlowWithState(
+ duration = TO_LOCKSCREEN_DURATION,
+ onStep = { value -> -translatePx + value * translatePx },
+ interpolator = EMPHASIZED,
+ onCancel = { -translatePx.toFloat() },
+ name = "GLANCEABLE_HUB->LOCKSCREEN: keyguardTranslationX"
+ )
+ }
+
+ val notificationAlpha: Flow<Float> = keyguardAlpha
+
+ val notificationTranslationX: Flow<Float> =
+ keyguardTranslationX.map { it.value }.filterNotNull()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
index 85885b065264..3540bec5d3e7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
@@ -53,23 +53,27 @@ constructor(
return transitionAnimation.sharedFlowWithState(
startTime = 600.milliseconds,
duration = 500.milliseconds,
- onStart = { translatePx },
onStep = { translatePx + it * -translatePx },
onFinish = { 0f },
- onCancel = { 0f },
interpolator = EMPHASIZED_DECELERATE,
)
}
+ val notificationAlpha: Flow<Float> =
+ transitionAnimation.sharedFlow(
+ duration = 200.milliseconds,
+ onStep = { 1f - it },
+ // Needs to be 1f in order for HUNs to appear on AOD
+ onFinish = { 1f },
+ )
+
/** alpha animation upon entering AOD */
val enterFromTopAnimationAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
startTime = 700.milliseconds,
duration = 400.milliseconds,
- onStart = { 0f },
onStep = { it },
onFinish = { 1f },
- onCancel = { 1f },
)
val deviceEntryBackgroundViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(0f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
index 188be244be4a..4db942cc460c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
@@ -20,7 +20,9 @@ package com.android.systemui.keyguard.ui.viewmodel
import androidx.annotation.VisibleForTesting
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
+import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -57,6 +59,7 @@ constructor(
lockscreenToGoneTransitionViewModel: LockscreenToGoneTransitionViewModel,
lockscreenToOccludedTransitionViewModel: LockscreenToOccludedTransitionViewModel,
lockscreenToPrimaryBouncerTransitionViewModel: LockscreenToPrimaryBouncerTransitionViewModel,
+ transitionInteractor: KeyguardTransitionInteractor,
) {
data class PreviewMode(
@@ -71,6 +74,24 @@ constructor(
*/
private val previewMode = MutableStateFlow(PreviewMode())
+ private val showingLockscreen: Flow<Boolean> =
+ transitionInteractor.finishedKeyguardState.map { keyguardState ->
+ keyguardState == KeyguardState.LOCKSCREEN
+ }
+
+ /** The only time the expansion is important is while lockscreen is actively displayed */
+ private val shadeExpansionAlpha =
+ combine(
+ showingLockscreen,
+ shadeInteractor.anyExpansion,
+ ) { showingLockscreen, expansion ->
+ if (showingLockscreen) {
+ 1 - expansion
+ } else {
+ 0f
+ }
+ }
+
/**
* ID of the slot that's currently selected in the preview that renders exclusively in the
* wallpaper picker application. This is ignored for the actual, real lock screen experience.
@@ -101,7 +122,7 @@ constructor(
lockscreenToGoneTransitionViewModel.shortcutsAlpha,
lockscreenToOccludedTransitionViewModel.shortcutsAlpha,
lockscreenToPrimaryBouncerTransitionViewModel.shortcutsAlpha,
- shadeInteractor.qsExpansion.map { 1 - it },
+ shadeExpansionAlpha,
)
/** The source of truth of alpha for all of the quick affordances on lockscreen */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index f790d356620d..921eb66cd3cc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -18,6 +18,7 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.graphics.Point
+import android.util.MathUtils
import android.view.View.VISIBLE
import com.android.systemui.Flags.newAodTransition
import com.android.systemui.common.shared.model.NotificationContainerBounds
@@ -32,6 +33,8 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
+import com.android.systemui.keyguard.ui.StateToValue
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
@@ -42,6 +45,7 @@ import com.android.systemui.util.ui.AnimatedValue
import com.android.systemui.util.ui.toAnimatedValueFlow
import com.android.systemui.util.ui.zip
import javax.inject.Inject
+import kotlin.math.max
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
@@ -62,7 +66,7 @@ constructor(
private val dozeParameters: DozeParameters,
private val keyguardInteractor: KeyguardInteractor,
private val communalInteractor: CommunalInteractor,
- keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val notificationsKeyguardInteractor: NotificationsKeyguardInteractor,
private val alternateBouncerToGoneTransitionViewModel:
AlternateBouncerToGoneTransitionViewModel,
@@ -86,6 +90,7 @@ constructor(
private val screenOffAnimationController: ScreenOffAnimationController,
private val aodBurnInViewModel: AodBurnInViewModel,
private val aodAlphaViewModel: AodAlphaViewModel,
+ private val shadeInteractor: ShadeInteractor,
) {
val burnInLayerVisibility: Flow<Int> =
@@ -101,6 +106,16 @@ constructor(
.onStart { emit(false) }
.distinctUntilChanged()
+ private val alphaOnShadeExpansion: Flow<Float> =
+ combine(
+ shadeInteractor.qsExpansion,
+ shadeInteractor.shadeExpansion,
+ ) { qsExpansion, shadeExpansion ->
+ // Fade out quickly as the shade expands
+ 1f - MathUtils.constrainedMap(0f, 1f, 0f, 0.2f, max(qsExpansion, shadeExpansion))
+ }
+ .distinctUntilChanged()
+
/** Last point that the root view was tapped */
val lastRootViewTapPosition: Flow<Point?> = keyguardInteractor.lastRootViewTapPosition
@@ -118,10 +133,12 @@ constructor(
fun alpha(viewState: ViewStateAccessor): Flow<Float> {
return combine(
communalInteractor.isIdleOnCommunal,
+ keyguardTransitionInteractor.transitionValue(GONE).onStart { emit(0f) },
// The transitions are mutually exclusive, so they are safe to merge to get the last
// value emitted by any of them. Do not add flows that cannot make this guarantee.
merge(
aodAlphaViewModel.alpha,
+ alphaOnShadeExpansion,
keyguardInteractor.dismissAlpha.filterNotNull(),
alternateBouncerToGoneTransitionViewModel.lockscreenAlpha,
aodToLockscreenTransitionViewModel.lockscreenAlpha(viewState),
@@ -139,11 +156,12 @@ constructor(
primaryBouncerToLockscreenTransitionViewModel.lockscreenAlpha,
)
.onStart { emit(1f) }
- ) { isIdleOnCommunal, alpha ->
- if (isIdleOnCommunal) {
+ ) { isIdleOnCommunal, goneValue, alpha ->
+ if (isIdleOnCommunal || goneValue == 1f) {
// Keyguard should not show while the communal hub is fully visible. This check
// is added since at the moment, closing the notification shade will cause the
- // keyguard alpha to be set back to 1.
+ // keyguard alpha to be set back to 1. Also ensure keyguard is never visible
+ // when GONE.
0f
} else {
alpha
@@ -165,8 +183,12 @@ constructor(
return aodBurnInViewModel.translationY(params)
}
- fun translationX(params: BurnInParameters): Flow<Float> {
- return aodBurnInViewModel.translationX(params)
+ fun translationX(params: BurnInParameters): Flow<StateToValue> {
+ return merge(
+ aodBurnInViewModel.translationX(params).map { StateToValue(to = AOD, value = it) },
+ lockscreenToGlanceableHubTransitionViewModel.keyguardTranslationX,
+ glanceableHubToLockscreenTransitionViewModel.keyguardTranslationX,
+ )
}
fun scale(params: BurnInParameters): Flow<BurnInScaleViewModel> {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
index fa185570e7fc..9afe8fcd93d0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
@@ -16,7 +16,7 @@
package com.android.systemui.keyguard.ui.viewmodel
-import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
@@ -36,7 +36,7 @@ class LockscreenSceneViewModel
constructor(
@Application applicationScope: CoroutineScope,
deviceEntryInteractor: DeviceEntryInteractor,
- communalSettingsInteractor: CommunalSettingsInteractor,
+ communalInteractor: CommunalInteractor,
val longPress: KeyguardLongPressViewModel,
val notifications: NotificationsPlaceholderViewModel,
) {
@@ -56,7 +56,7 @@ constructor(
/** The key of the scene we should switch to when swiping left. */
val leftDestinationSceneKey: StateFlow<SceneKey?> =
- communalSettingsInteractor.isCommunalEnabled
+ communalInteractor.isCommunalAvailable
.map { available -> if (available) SceneKey.Communal else null }
.stateIn(
scope = applicationScope,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt
index 3afa49e50167..978e71e2a825 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt
@@ -16,13 +16,22 @@
package com.android.systemui.keyguard.ui.viewmodel
+import com.android.app.animation.Interpolators.EMPHASIZED
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.StateToValue
+import com.android.systemui.res.R
import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
/**
* Breaks down LOCKSCREEN->GLANCEABLE_HUB transition into discrete steps for corresponding views to
@@ -33,6 +42,7 @@ import kotlinx.coroutines.flow.Flow
class LockscreenToGlanceableHubTransitionViewModel
@Inject
constructor(
+ configurationInteractor: ConfigurationInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
) {
private val transitionAnimation =
@@ -42,23 +52,35 @@ constructor(
to = KeyguardState.GLANCEABLE_HUB,
)
- // TODO(b/315205222): implement full animation spec instead of just a simple fade.
val keyguardAlpha: Flow<Float> =
- transitionAnimation.sharedFlow(
- duration = FromLockscreenTransitionInteractor.TO_GLANCEABLE_HUB_DURATION,
- onStep = { 1f - it },
- onFinish = { 0f },
- onCancel = { 1f },
- name = "LOCKSCREEN->GLANCEABLE_HUB: keyguardAlpha",
- )
+ transitionAnimation
+ .sharedFlow(
+ duration = 167.milliseconds,
+ onStep = { 1f - it },
+ onFinish = { 0f },
+ onCancel = { 1f },
+ name = "LOCKSCREEN->GLANCEABLE_HUB: keyguardAlpha",
+ )
+ .onStart { emit(1f) }
- // TODO(b/315205216): implement full animation spec instead of just a simple fade.
- val notificationAlpha: Flow<Float> =
- transitionAnimation.sharedFlow(
- duration = FromLockscreenTransitionInteractor.TO_GLANCEABLE_HUB_DURATION,
- onStep = { 1f - it },
- onFinish = { 0f },
- onCancel = { 1f },
- name = "LOCKSCREEN->GLANCEABLE_HUB: notificationAlpha",
- )
+ val keyguardTranslationX: Flow<StateToValue> =
+ configurationInteractor
+ .dimensionPixelSize(R.dimen.lockscreen_to_hub_transition_lockscreen_translation_x)
+ .flatMapLatest { translatePx: Int ->
+ transitionAnimation.sharedFlowWithState(
+ duration = FromLockscreenTransitionInteractor.TO_GLANCEABLE_HUB_DURATION,
+ onStep = { value -> value * translatePx },
+ // Move notifications back to their original position since they can be
+ // accessed from the shade.
+ onFinish = { 0f },
+ onCancel = { 0f },
+ interpolator = EMPHASIZED,
+ name = "LOCKSCREEN->GLANCEABLE_HUB: keyguardTranslationX"
+ )
+ }
+
+ val notificationAlpha: Flow<Float> = keyguardAlpha
+
+ val notificationTranslationX: Flow<Float> =
+ keyguardTranslationX.map { it.value }.filterNotNull()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
index 15459f4af9a4..4e6aa030d993 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
@@ -55,23 +55,13 @@ constructor(
)
fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> {
- var startAlpha: Float? = null
+ var startAlpha = 1f
return transitionAnimation.sharedFlow(
duration = 200.milliseconds,
- onStep = {
- if (startAlpha == null) {
- startAlpha = viewState.alpha()
- }
- MathUtils.lerp(startAlpha!!, 0f, it)
- },
- onFinish = {
- startAlpha = null
- 0f
- },
- onCancel = {
- startAlpha = null
- 1f
- },
+ onStart = { startAlpha = viewState.alpha() },
+ onStep = { MathUtils.lerp(startAlpha, 0f, it) },
+ onFinish = { 0f },
+ onCancel = { 1f },
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt
index c61b1f5cc3be..d7ba46b6e708 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt
@@ -54,7 +54,6 @@ constructor(
startTime = 233.milliseconds,
duration = 250.milliseconds,
onStep = { it },
- onStart = { 0f },
)
override val deviceEntryParentViewAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
index 19c11a947fe2..3a19780c7017 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
@@ -78,7 +78,6 @@ constructor(
startTime = 233.milliseconds,
duration = 250.milliseconds,
onStep = { it },
- onStart = { 0f },
name = "OCCLUDED->LOCKSCREEN: lockscreenAlpha",
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
index 4f28b4608e49..378ce52b4331 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
@@ -108,6 +108,7 @@ constructor(
0f
}
},
+ onFinish = { 0f },
)
}
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 ac579d6d2491..8b7c85b65824 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -119,6 +119,16 @@ public class LogModule {
return factory.create("LSShadeTransitionLog", 50);
}
+ /** */
+ @Provides
+ @SysUISingleton
+ @SensitiveNotificationProtectionLog
+ public static LogBuffer provideSensitiveNotificationProtectionLogBuffer(
+ LogBufferFactory factory
+ ) {
+ return factory.create("SensitiveNotificationProtectionLog", 10);
+ }
+
/** Provides a logging buffer for shade window messages. */
@Provides
@SysUISingleton
@@ -333,7 +343,7 @@ public class LogModule {
/**
* Provides a buffer for our connections and disconnections to MediaBrowserService.
*
- * See {@link com.android.systemui.media.controls.resume.ResumeMediaBrowser}.
+ * See {@link com.android.systemui.media.controls.domain.resume.ResumeMediaBrowser}.
*/
@Provides
@SysUISingleton
@@ -345,7 +355,7 @@ public class LogModule {
/**
* Provides a buffer for updates to the media carousel.
*
- * See {@link com.android.systemui.media.controls.ui.MediaCarouselController}.
+ * See {@link com.android.systemui.media.controls.ui.controller.MediaCarouselController}.
*/
@Provides
@SysUISingleton
@@ -637,4 +647,11 @@ public class LogModule {
return factory.create("NavBarButtonClick", 50);
}
+ /** Provides a {@link LogBuffer} for NavBar Orientation Tracking. */
+ @Provides
+ @SysUISingleton
+ @NavbarOrientationTrackingLog
+ public static LogBuffer provideNavbarOrientationTrackingLogBuffer(LogBufferFactory factory) {
+ return factory.create("NavbarOrientationTrackingLog", 50);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaBrowserLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaBrowserLog.java
index 1c00c93f4e38..901559bd3d8b 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaBrowserLog.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaBrowserLog.java
@@ -26,7 +26,8 @@ import java.lang.annotation.Retention;
import javax.inject.Qualifier;
/**
- * A {@link LogBuffer} for {@link com.android.systemui.media.controls.resume.ResumeMediaBrowser}
+ * A {@link LogBuffer} for
+ * {@link com.android.systemui.media.controls.domain.resume.ResumeMediaBrowser}
*/
@Qualifier
@Documented
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaCarouselControllerLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaCarouselControllerLog.java
index 86a916ef6541..abbfd4fa32ed 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaCarouselControllerLog.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaCarouselControllerLog.java
@@ -26,7 +26,8 @@ import java.lang.annotation.Retention;
import javax.inject.Qualifier;
/**
- * A {@link LogBuffer} for {@link com.android.systemui.media.controls.ui.MediaCarouselController}
+ * A {@link LogBuffer} for
+ * {@link com.android.systemui.media.controls.ui.controller.MediaCarouselController}
*/
@Qualifier
@Documented
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTimeoutListenerLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTimeoutListenerLog.java
index 98e6556d7f53..0239caa39544 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTimeoutListenerLog.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaTimeoutListenerLog.java
@@ -26,7 +26,8 @@ import java.lang.annotation.Retention;
import javax.inject.Qualifier;
/**
- * A {@link LogBuffer} for {@link com.android.systemui.media.controls.pipeline.MediaTimeoutLogger}
+ * A {@link LogBuffer} for
+ * {@link com.android.systemui.media.controls.domain.pipeline.MediaTimeoutLogger}
*/
@Qualifier
@Documented
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaViewLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaViewLog.java
index dde0ee0796d4..27a6a64b1302 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/MediaViewLog.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaViewLog.java
@@ -26,7 +26,7 @@ import java.lang.annotation.Retention;
import javax.inject.Qualifier;
/**
- * A {@link LogBuffer} for {@link com.android.systemui.media.controls.ui.MediaViewLogger}
+ * A {@link LogBuffer} for {@link com.android.systemui.media.controls.ui.controller.MediaViewLogger}
*/
@Qualifier
@Documented
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/NavbarOrientationTrackingLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/NavbarOrientationTrackingLog.java
new file mode 100644
index 000000000000..46790a66cbb6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/NavbarOrientationTrackingLog.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 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 static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.android.systemui.log.LogBuffer;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+/** A {@link LogBuffer} for {@link com.android.systemui.navigationbar.NavigationBar}. */
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface NavbarOrientationTrackingLog {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/SensitiveNotificationProtectionLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/SensitiveNotificationProtectionLog.kt
new file mode 100644
index 000000000000..54e87cde4686
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/SensitiveNotificationProtectionLog.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 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 [com.android.systemui.log.LogBuffer] for SensitiveNotificationProtection. */
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class SensitiveNotificationProtectionLog
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataCombineLatest.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatest.kt
index 789ef407ea9d..ad70db5a3300 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataCombineLatest.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatest.kt
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.player.MediaDeviceData
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.MediaDeviceData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
import javax.inject.Inject
/** Combines [MediaDataManager.Listener] events with [MediaDeviceManager.Listener] events. */
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilter.kt
index 185a78369a9e..bc539efdfe69 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilter.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
import android.content.Context
import android.content.pm.UserInfo
@@ -24,9 +24,9 @@ import com.android.internal.annotations.KeepForWeakReference
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.recommendation.EXTRA_KEY_TRIGGER_RESUME
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
+import com.android.systemui.media.controls.shared.model.EXTRA_KEY_TRIGGER_RESUME
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.settings.UserTracker
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManager.kt
index 47df3b79b8bb..6fc22ea60a9e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManager.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
import android.annotation.SuppressLint
import android.app.BroadcastOptions
@@ -66,17 +66,17 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.controls.models.player.MediaAction
-import com.android.systemui.media.controls.models.player.MediaButton
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.player.MediaDeviceData
-import com.android.systemui.media.controls.models.player.MediaViewHolder
-import com.android.systemui.media.controls.models.recommendation.EXTRA_KEY_TRIGGER_SOURCE
-import com.android.systemui.media.controls.models.recommendation.EXTRA_VALUE_TRIGGER_PERIODIC
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaDataProvider
-import com.android.systemui.media.controls.resume.MediaResumeListener
-import com.android.systemui.media.controls.resume.ResumeMediaBrowser
+import com.android.systemui.media.controls.domain.resume.MediaResumeListener
+import com.android.systemui.media.controls.domain.resume.ResumeMediaBrowser
+import com.android.systemui.media.controls.shared.model.EXTRA_KEY_TRIGGER_SOURCE
+import com.android.systemui.media.controls.shared.model.EXTRA_VALUE_TRIGGER_PERIODIC
+import com.android.systemui.media.controls.shared.model.MediaAction
+import com.android.systemui.media.controls.shared.model.MediaButton
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.MediaDeviceData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaDataProvider
+import com.android.systemui.media.controls.ui.view.MediaViewHolder
import com.android.systemui.media.controls.util.MediaControllerFactory
import com.android.systemui.media.controls.util.MediaDataUtils
import com.android.systemui.media.controls.util.MediaFlags
@@ -583,6 +583,10 @@ class MediaDataManager(
}
return
}
+ // Update last active if media was still active.
+ if (it.active) {
+ it.lastActive = systemClock.elapsedRealtime()
+ }
it.active = !timedOut
if (DEBUG) Log.d(TAG, "Updating $key timedOut: $timedOut")
onMediaDataLoaded(key, key, it)
@@ -1520,6 +1524,12 @@ class MediaDataManager(
context.packageManager.getLaunchIntentForPackage(data.packageName)?.let {
PendingIntent.getActivity(context, 0, it, PendingIntent.FLAG_IMMUTABLE)
}
+ val lastActive =
+ if (data.active) {
+ systemClock.elapsedRealtime()
+ } else {
+ data.lastActive
+ }
val updated =
data.copy(
token = null,
@@ -1531,6 +1541,7 @@ class MediaDataManager(
isPlaying = false,
isClearable = true,
clickIntent = launcherIntent,
+ lastActive = lastActive,
)
val pkg = data.packageName
val migrate = mediaEntries.put(pkg, updated) == null
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
index dcbf670460ef..f4d70a5e78c9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
import android.bluetooth.BluetoothLeBroadcast
import android.bluetooth.BluetoothLeBroadcastMetadata
@@ -30,6 +30,8 @@ import androidx.annotation.MainThread
import androidx.annotation.WorkerThread
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast
import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.settingslib.flags.Flags.enableLeAudioSharing
+import com.android.settingslib.flags.Flags.legacyLeAudioSharing
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
import com.android.settingslib.media.PhoneMediaDevice
@@ -37,8 +39,9 @@ import com.android.systemui.Dumpable
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.player.MediaDeviceData
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.MediaDeviceData
+import com.android.systemui.media.controls.util.LocalMediaManagerFactory
import com.android.systemui.media.controls.util.MediaControllerFactory
import com.android.systemui.media.controls.util.MediaDataUtils
import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionManager
@@ -331,14 +334,28 @@ constructor(
@WorkerThread
private fun updateCurrent() {
if (isLeAudioBroadcastEnabled()) {
- current =
- MediaDeviceData(
- /* enabled */ true,
- /* icon */ context.getDrawable(R.drawable.settings_input_antenna),
- /* name */ broadcastDescription,
- /* intent */ null,
- /* showBroadcastButton */ showBroadcastButton = true
- )
+ if (enableLeAudioSharing()) {
+ current =
+ MediaDeviceData(
+ enabled = false,
+ icon =
+ context.getDrawable(
+ com.android.settingslib.R.drawable.ic_bt_le_audio_sharing
+ ),
+ name = context.getString(R.string.audio_sharing_description),
+ intent = null,
+ showBroadcastButton = false
+ )
+ } else {
+ current =
+ MediaDeviceData(
+ /* enabled */ true,
+ /* icon */ context.getDrawable(R.drawable.settings_input_antenna),
+ /* name */ broadcastDescription,
+ /* intent */ null,
+ /* showBroadcastButton */ showBroadcastButton = true
+ )
+ }
} else {
val aboutToConnect = aboutToConnectDeviceOverride
if (
@@ -419,6 +436,7 @@ constructor(
@WorkerThread
private fun isLeAudioBroadcastEnabled(): Boolean {
+ if (!enableLeAudioSharing() && !legacyLeAudioSharing()) return false
val localBluetoothManager = localBluetoothManager.get()
if (localBluetoothManager != null) {
val profileManager = localBluetoothManager.profileManager
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaSessionBasedFilter.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilter.kt
index 6a8ffb7d8c42..b2a8f2e71cc6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaSessionBasedFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilter.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
import android.content.ComponentName
import android.content.Context
@@ -25,8 +25,8 @@ import android.media.session.MediaSessionManager
import android.util.Log
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
import com.android.systemui.statusbar.phone.NotificationListenerWithPlugins
import java.util.concurrent.Executor
import javax.inject.Inject
@@ -199,9 +199,7 @@ constructor(
packageControllers.put(controller.packageName, tokens)
}
}
- controllers?.map { TokenId(it.sessionToken) }?.let {
- tokensWithNotifications.retainAll(it)
- }
+ controllers?.map { TokenId(it.sessionToken) }?.let { tokensWithNotifications.retainAll(it) }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListener.kt
index ed4eef9eaa2b..29f396700831 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListener.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
import android.media.session.MediaController
import android.media.session.MediaSession
@@ -23,8 +23,8 @@ import android.os.SystemProperties
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
import com.android.systemui.media.controls.util.MediaControllerFactory
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.plugins.statusbar.StatusBarStateController
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutLogger.kt
index 534241edb253..c50c46a853a5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutLogger.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
import android.media.session.PlaybackState
import com.android.systemui.dagger.SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaBrowserFactory.java b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/MediaBrowserFactory.java
index 00620b5b2575..2c45ddb5542a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaBrowserFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/MediaBrowserFactory.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.resume;
+package com.android.systemui.media.controls.domain.resume;
import android.content.ComponentName;
import android.content.Context;
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/MediaResumeListener.kt
index 23ee00d88fdc..e4047e5f96c5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/MediaResumeListener.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.resume
+package com.android.systemui.media.controls.domain.resume
import android.content.BroadcastReceiver
import android.content.ComponentName
@@ -34,9 +34,9 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.pipeline.MediaDataManager
-import com.android.systemui.media.controls.pipeline.RESUME_MEDIA_TIMEOUT
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.RESUME_MEDIA_TIMEOUT
+import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.settings.UserTracker
import com.android.systemui.tuner.TunerService
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowser.java b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowser.java
index ceaccafd8f40..b2960cd287c6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowser.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowser.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.resume;
+package com.android.systemui.media.controls.domain.resume;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserFactory.java b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserFactory.java
index e37419127f5b..50eb77642632 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserFactory.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.resume;
+package com.android.systemui.media.controls.domain.resume;
import android.annotation.UserIdInt;
import android.content.ComponentName;
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserLogger.kt
index 888b9c7cc901..ce2a0803bbfb 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserLogger.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.resume
+package com.android.systemui.media.controls.domain.resume
import android.content.ComponentName
import com.android.systemui.dagger.SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaData.kt
index 5caa27f02bd3..4fa7cb54431f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaData.kt
@@ -11,10 +11,10 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
*/
-package com.android.systemui.media.controls.models.player
+package com.android.systemui.media.controls.shared.model
import android.app.PendingIntent
import android.graphics.drawable.Drawable
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaData.kt b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/SmartspaceMediaData.kt
index ae03f27b32cd..52c605f55665 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/SmartspaceMediaData.kt
@@ -11,10 +11,10 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
*/
-package com.android.systemui.media.controls.models.recommendation
+package com.android.systemui.media.controls.shared.model
import android.app.smartspace.SmartspaceAction
import android.content.Context
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataProvider.kt b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/SmartspaceMediaDataProvider.kt
index cacb3e2bbe4d..8726d8193800 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/SmartspaceMediaDataProvider.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.models.recommendation
+package com.android.systemui.media.controls.shared.model
import android.app.smartspace.SmartspaceTarget
import android.util.Log
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/AnimationBindHandler.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/AnimationBindHandler.kt
index f5cc04331f94..82580590a11c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/AnimationBindHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/AnimationBindHandler.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.animation
import android.graphics.drawable.Animatable2
import android.graphics.drawable.Drawable
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransition.kt
index c87fd14a943d..21407f3bd6d4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransition.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.animation
import android.animation.ArgbEvaluator
import android.animation.ValueAnimator
@@ -27,8 +27,9 @@ import android.graphics.drawable.RippleDrawable
import com.android.internal.R
import com.android.internal.annotations.VisibleForTesting
import com.android.settingslib.Utils
-import com.android.systemui.media.controls.models.player.MediaViewHolder
+import com.android.systemui.media.controls.ui.view.MediaViewHolder
import com.android.systemui.monet.ColorScheme
+import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect
import com.android.systemui.surfaceeffects.ripple.MultiRippleController
import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseController
@@ -118,6 +119,7 @@ internal constructor(
turbulenceNoiseController,
::AnimatingColorTransition
)
+ var loadingEffect: LoadingEffect? = null
val bgColor = context.getColor(com.google.android.material.R.color.material_dynamic_neutral20)
val surfaceColor =
@@ -128,7 +130,6 @@ internal constructor(
mediaViewHolder.albumView.backgroundTintList = colorList
mediaViewHolder.gutsViewHolder.setSurfaceColor(surfaceColor)
}
-
val accentPrimary =
animatingColorTransitionFactory(
loadDefaultColor(R.attr.textColorPrimary),
@@ -139,6 +140,7 @@ internal constructor(
mediaViewHolder.gutsViewHolder.setAccentPrimaryColor(accentPrimary)
multiRippleController.updateColor(accentPrimary)
turbulenceNoiseController.updateNoiseColor(accentPrimary)
+ loadingEffect?.updateColor(accentPrimary)
}
val accentSecondary =
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaColorSchemes.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/MediaColorSchemes.kt
index 2a8362b64cd6..3c57c83ff9fe 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaColorSchemes.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/MediaColorSchemes.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.animation
import com.android.systemui.monet.ColorScheme
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MetadataAnimationHandler.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/MetadataAnimationHandler.kt
index 1cdcf5ed2702..98202c51706a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MetadataAnimationHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/animation/MetadataAnimationHandler.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.animation
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
index 8d918e76b1e1..34f7c4dcaec0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.models.player
+package com.android.systemui.media.controls.ui.binder
import android.animation.Animator
import android.animation.ObjectAnimator
@@ -24,7 +24,9 @@ import androidx.lifecycle.Observer
import com.android.app.animation.Interpolators
import com.android.app.tracing.TraceStateLogger
import com.android.internal.annotations.VisibleForTesting
-import com.android.systemui.media.controls.ui.SquigglyProgress
+import com.android.systemui.media.controls.ui.drawable.SquigglyProgress
+import com.android.systemui.media.controls.ui.view.MediaViewHolder
+import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel
import com.android.systemui.res.R
private const val TAG = "SeekBarObserver"
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt
index e15e03822610..ba7d41008a01 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import android.content.Context
import android.content.res.Configuration
@@ -31,6 +31,8 @@ import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.media.dagger.MediaModule.KEYGUARD
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.StatusBarState
@@ -230,18 +232,12 @@ constructor(
val currentAllowMediaPlayerOnLockScreen = allowMediaPlayerOnLockScreen
val useSplitShade = useSplitShade
val shouldBeVisibleForSplitShade = shouldBeVisibleForSplitShade()
-
visible =
isMediaHostVisible &&
isBypassNotEnabled &&
keyguardOrUserSwitcher &&
currentAllowMediaPlayerOnLockScreen &&
shouldBeVisibleForSplitShade
- if (visible) {
- showMediaPlayer()
- } else {
- hideMediaPlayer()
- }
logger.logRefreshMediaPosition(
reason = reason,
visible = visible,
@@ -251,8 +247,17 @@ constructor(
mediaHostVisible = isMediaHostVisible,
bypassNotEnabled = isBypassNotEnabled,
currentAllowMediaPlayerOnLockScreen = currentAllowMediaPlayerOnLockScreen,
- shouldBeVisibleForSplitShade = shouldBeVisibleForSplitShade
+ shouldBeVisibleForSplitShade = shouldBeVisibleForSplitShade,
)
+ val currActiveContainer = activeContainer
+
+ logger.logActiveMediaContainer("before refreshMediaPosition", currActiveContainer)
+ if (visible) {
+ showMediaPlayer()
+ } else {
+ hideMediaPlayer()
+ }
+ logger.logActiveMediaContainer("after refreshMediaPosition", currActiveContainer)
lastUsedStatusBarState = currentState
}
@@ -293,10 +298,19 @@ constructor(
}
private fun setVisibility(view: ViewGroup?, newVisibility: Int) {
- val previousVisibility = view?.visibility
- view?.visibility = newVisibility
- if (previousVisibility != newVisibility) {
- visibilityChangedListener?.invoke(newVisibility == View.VISIBLE)
+ val currentMediaContainer = view ?: return
+
+ val isVisible = newVisibility == View.VISIBLE
+
+ if (currentMediaContainer is MediaContainerView) {
+ val previousVisibility = currentMediaContainer.visibility
+
+ currentMediaContainer.setKeyguardVisibility(isVisible)
+ if (previousVisibility != newVisibility) {
+ visibilityChangedListener?.invoke(isVisible)
+ }
+ } else {
+ currentMediaContainer.visibility = newVisibility
}
}
@@ -325,4 +339,7 @@ constructor(
}
}
}
+
+ private val activeContainer: ViewGroup? =
+ if (useSplitShade) splitShadeContainer else singlePaneContainer
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerLogger.kt
index 41fef88645a2..c0d9dc23a6d5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerLogger.kt
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
+import android.view.ViewGroup
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel.DEBUG
import com.android.systemui.log.dagger.KeyguardMediaControllerLog
@@ -36,8 +37,8 @@ constructor(@KeyguardMediaControllerLog private val logBuffer: LogBuffer) {
mediaHostVisible: Boolean,
bypassNotEnabled: Boolean,
currentAllowMediaPlayerOnLockScreen: Boolean,
- shouldBeVisibleForSplitShade: Boolean
- ) =
+ shouldBeVisibleForSplitShade: Boolean,
+ ) {
logBuffer.log(
TAG,
DEBUG,
@@ -63,6 +64,19 @@ constructor(@KeyguardMediaControllerLog private val logBuffer: LogBuffer) {
"shouldBeVisibleForSplitShade=$str3)"
}
)
+ }
+
+ fun logActiveMediaContainer(reason: String, activeContainer: ViewGroup?) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ str1 = reason
+ str2 = activeContainer.toString()
+ },
+ { "activeMediaContainerVisibility(reason=$str1, activeContainer=$str2)" }
+ )
+ }
private companion object {
private const val TAG = "KeyguardMediaControllerLog"
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
index 992eeca77e54..b721236eab01 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import android.app.PendingIntent
import android.content.Context
@@ -46,12 +46,15 @@ import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.player.MediaViewHolder
-import com.android.systemui.media.controls.models.recommendation.RecommendationViewHolder
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
-import com.android.systemui.media.controls.pipeline.MediaDataManager
-import com.android.systemui.media.controls.ui.MediaControlPanel.SMARTSPACE_CARD_DISMISS_EVENT
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.ui.controller.MediaControlPanel.SMARTSPACE_CARD_DISMISS_EVENT
+import com.android.systemui.media.controls.ui.view.MediaCarouselScrollHandler
+import com.android.systemui.media.controls.ui.view.MediaHostState
+import com.android.systemui.media.controls.ui.view.MediaScrollView
+import com.android.systemui.media.controls.ui.view.MediaViewHolder
+import com.android.systemui.media.controls.ui.view.RecommendationViewHolder
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.media.controls.util.SmallHash
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt
index 3dc00004e900..ebf1c6a10703 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselControllerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
index aa92814a584d..4e940f1f84da 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui;
+package com.android.systemui.media.controls.ui.controller;
import static android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS;
-import static com.android.systemui.media.controls.models.recommendation.SmartspaceMediaDataKt.NUM_REQUIRED_RECOMMENDATIONS;
+import static com.android.systemui.Flags.legacyLeAudioSharing;
+import static com.android.systemui.media.controls.shared.model.SmartspaceMediaDataKt.NUM_REQUIRED_RECOMMENDATIONS;
import android.animation.Animator;
import android.animation.AnimatorInflater;
@@ -41,6 +42,7 @@ import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Matrix;
+import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.BitmapDrawable;
@@ -81,23 +83,28 @@ import com.android.internal.logging.InstanceId;
import com.android.internal.widget.CachingIconView;
import com.android.settingslib.widget.AdaptiveIcon;
import com.android.systemui.ActivityIntentHelper;
+import com.android.systemui.Flags;
import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.animation.GhostedViewTransitionAnimatorController;
import com.android.systemui.bluetooth.BroadcastDialogController;
import com.android.systemui.broadcast.BroadcastSender;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.media.controls.models.GutsViewHolder;
-import com.android.systemui.media.controls.models.player.MediaAction;
-import com.android.systemui.media.controls.models.player.MediaButton;
-import com.android.systemui.media.controls.models.player.MediaData;
-import com.android.systemui.media.controls.models.player.MediaDeviceData;
-import com.android.systemui.media.controls.models.player.MediaViewHolder;
-import com.android.systemui.media.controls.models.player.SeekBarObserver;
-import com.android.systemui.media.controls.models.player.SeekBarViewModel;
-import com.android.systemui.media.controls.models.recommendation.RecommendationViewHolder;
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData;
-import com.android.systemui.media.controls.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.shared.model.MediaAction;
+import com.android.systemui.media.controls.shared.model.MediaButton;
+import com.android.systemui.media.controls.shared.model.MediaData;
+import com.android.systemui.media.controls.shared.model.MediaDeviceData;
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData;
+import com.android.systemui.media.controls.ui.animation.AnimationBindHandler;
+import com.android.systemui.media.controls.ui.animation.ColorSchemeTransition;
+import com.android.systemui.media.controls.ui.animation.MediaColorSchemesKt;
+import com.android.systemui.media.controls.ui.animation.MetadataAnimationHandler;
+import com.android.systemui.media.controls.ui.binder.SeekBarObserver;
+import com.android.systemui.media.controls.ui.view.GutsViewHolder;
+import com.android.systemui.media.controls.ui.view.MediaViewHolder;
+import com.android.systemui.media.controls.ui.view.RecommendationViewHolder;
+import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel;
import com.android.systemui.media.controls.util.MediaDataUtils;
import com.android.systemui.media.controls.util.MediaFlags;
import com.android.systemui.media.controls.util.MediaUiEventLogger;
@@ -111,6 +118,9 @@ import com.android.systemui.res.R;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect;
+import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect.Companion.AnimationState;
+import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffectView;
import com.android.systemui.surfaceeffects.ripple.MultiRippleController;
import com.android.systemui.surfaceeffects.ripple.MultiRippleView;
import com.android.systemui.surfaceeffects.ripple.RippleAnimation;
@@ -248,13 +258,33 @@ public class MediaControlPanel {
private String mCurrentBroadcastApp;
private MultiRippleController mMultiRippleController;
private TurbulenceNoiseController mTurbulenceNoiseController;
+ private LoadingEffect mLoadingEffect;
private final GlobalSettings mGlobalSettings;
-
private TurbulenceNoiseAnimationConfig mTurbulenceNoiseAnimationConfig;
private boolean mWasPlaying = false;
private boolean mButtonClicked = false;
- private final Random mRandom = new Random();
+ private final LoadingEffect.Companion.PaintDrawCallback mNoiseDrawCallback =
+ new LoadingEffect.Companion.PaintDrawCallback() {
+ @Override
+ public void onDraw(@NonNull Paint loadingPaint) {
+ mMediaViewHolder.getLoadingEffectView().draw(loadingPaint);
+ }
+ };
+ private final LoadingEffect.Companion.AnimationStateChangedCallback mStateChangedCallback =
+ new LoadingEffect.Companion.AnimationStateChangedCallback() {
+ @Override
+ public void onStateChanged(@NonNull AnimationState oldState,
+ @NonNull AnimationState newState) {
+ LoadingEffectView loadingEffectView =
+ mMediaViewHolder.getLoadingEffectView();
+ if (newState == AnimationState.NOT_PLAYING) {
+ loadingEffectView.setVisibility(View.INVISIBLE);
+ } else {
+ loadingEffectView.setVisibility(View.VISIBLE);
+ }
+ }
+ };
/**
* Initialize a new control panel
@@ -456,6 +486,10 @@ public class MediaControlPanel {
TurbulenceNoiseView turbulenceNoiseView = vh.getTurbulenceNoiseView();
turbulenceNoiseView.setBlendMode(BlendMode.SCREEN);
+ LoadingEffectView loadingEffectView = vh.getLoadingEffectView();
+ loadingEffectView.setBlendMode(BlendMode.SCREEN);
+ loadingEffectView.setVisibility(View.INVISIBLE);
+
mTurbulenceNoiseController = new TurbulenceNoiseController(turbulenceNoiseView);
mColorSchemeTransition = new ColorSchemeTransition(
@@ -567,7 +601,9 @@ public class MediaControlPanel {
// Show the broadcast dialog button only when the le audio is enabled.
mShowBroadcastDialogButton =
- data.getDevice() != null && data.getDevice().getShowBroadcastButton();
+ legacyLeAudioSharing()
+ && data.getDevice() != null
+ && data.getDevice().getShowBroadcastButton();
bindOutputSwitcherAndBroadcastButton(mShowBroadcastDialogButton, data);
bindGutsMenuForPlayer(data);
bindPlayerContentDescription(data);
@@ -587,22 +623,41 @@ public class MediaControlPanel {
}
}
- // Turbulence noise
if (shouldPlayTurbulenceNoise()) {
+ // Need to create the config here to get the correct view size and color.
if (mTurbulenceNoiseAnimationConfig == null) {
mTurbulenceNoiseAnimationConfig =
- createTurbulenceNoiseAnimation();
+ createTurbulenceNoiseConfig();
+ }
+
+ if (Flags.shaderlibLoadingEffectRefactor()) {
+ if (mLoadingEffect == null) {
+ mLoadingEffect = new LoadingEffect(
+ Type.SIMPLEX_NOISE,
+ mTurbulenceNoiseAnimationConfig,
+ mNoiseDrawCallback,
+ mStateChangedCallback
+ );
+ mColorSchemeTransition.setLoadingEffect(mLoadingEffect);
+ }
+
+ mLoadingEffect.play();
+ mMainExecutor.executeDelayed(
+ mLoadingEffect::finish,
+ TURBULENCE_NOISE_PLAY_DURATION
+ );
+ } else {
+ mTurbulenceNoiseController.play(
+ Type.SIMPLEX_NOISE,
+ mTurbulenceNoiseAnimationConfig
+ );
+ mMainExecutor.executeDelayed(
+ mTurbulenceNoiseController::finish,
+ TURBULENCE_NOISE_PLAY_DURATION
+ );
}
- // Color will be correctly updated in ColorSchemeTransition.
- mTurbulenceNoiseController.play(
- Type.SIMPLEX_NOISE,
- mTurbulenceNoiseAnimationConfig
- );
- mMainExecutor.executeDelayed(
- mTurbulenceNoiseController::finish,
- TURBULENCE_NOISE_PLAY_DURATION
- );
}
+
mButtonClicked = false;
mWasPlaying = isPlaying();
@@ -1232,20 +1287,28 @@ public class MediaControlPanel {
return mButtonClicked && !mWasPlaying && isPlaying();
}
- private TurbulenceNoiseAnimationConfig createTurbulenceNoiseAnimation() {
+ private TurbulenceNoiseAnimationConfig createTurbulenceNoiseConfig() {
+ View targetView = Flags.shaderlibLoadingEffectRefactor()
+ ? mMediaViewHolder.getLoadingEffectView() :
+ mMediaViewHolder.getTurbulenceNoiseView();
+ int width = targetView.getWidth();
+ int height = targetView.getHeight();
+ Random random = new Random();
+
return new TurbulenceNoiseAnimationConfig(
/* gridCount= */ 2.14f,
TurbulenceNoiseAnimationConfig.DEFAULT_LUMINOSITY_MULTIPLIER,
- /* noiseOffsetX= */ mRandom.nextFloat(),
- /* noiseOffsetY= */ mRandom.nextFloat(),
- /* noiseOffsetZ= */ mRandom.nextFloat(),
+ /* noiseOffsetX= */ random.nextFloat(),
+ /* noiseOffsetY= */ random.nextFloat(),
+ /* noiseOffsetZ= */ random.nextFloat(),
/* noiseMoveSpeedX= */ 0.42f,
/* noiseMoveSpeedY= */ 0f,
TurbulenceNoiseAnimationConfig.DEFAULT_NOISE_SPEED_Z,
+ // Color will be correctly updated in ColorSchemeTransition.
/* color= */ mColorSchemeTransition.getAccentPrimary().getCurrentColor(),
/* backgroundColor= */ Color.BLACK,
- /* width= */ mMediaViewHolder.getTurbulenceNoiseView().getWidth(),
- /* height= */ mMediaViewHolder.getTurbulenceNoiseView().getHeight(),
+ width,
+ height,
TurbulenceNoiseAnimationConfig.DEFAULT_MAX_DURATION_IN_MILLIS,
/* easeInDuration= */ 1350f,
/* easeOutDuration= */ 1350f,
@@ -1874,3 +1937,4 @@ public class MediaControlPanel {
interactedSubcardCardinality);
}
}
+
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
index 35e0271c1b8f..dbd71f3e3a04 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
@@ -42,7 +42,8 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dreams.DreamOverlayStateController
import com.android.systemui.keyguard.WakefulnessLifecycle
-import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.dream.MediaDreamComplication
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -57,6 +58,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.statusbar.policy.SplitShadeStateController
import com.android.systemui.util.animation.UniqueObjectHostView
+import com.android.systemui.util.kotlin.BooleanFlowOperators.and
import com.android.systemui.util.settings.SecureSettings
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -582,12 +584,14 @@ constructor(
UserHandle.USER_ALL
)
- // Listen to the communal UI state.
+ // Listen to the communal UI state. Make sure that communal UI is showing and hub itself is
+ // available, ie. not disabled and able to be shown.
coroutineScope.launch {
- communalInteractor.isCommunalShowing.collect { value ->
- isCommunalShowing = value
- updateDesiredLocation(forceNoAnimation = true)
- }
+ and(communalInteractor.isCommunalShowing, communalInteractor.isCommunalAvailable)
+ .collect { value ->
+ isCommunalShowing = value
+ updateDesiredLocation()
+ }
}
}
@@ -1149,12 +1153,16 @@ constructor(
when {
mediaFlags.isSceneContainerEnabled() -> desiredLocation
dreamOverlayActive && dreamMediaComplicationActive -> LOCATION_DREAM_OVERLAY
+
+ // UMO should show in communal unless the shade is expanding or visible.
+ isCommunalShowing && qsExpansion == 0.0f -> LOCATION_COMMUNAL_HUB
(qsExpansion > 0.0f || inSplitShade) && !onLockscreen -> LOCATION_QS
qsExpansion > 0.4f && onLockscreen -> LOCATION_QS
onLockscreen && isSplitShadeExpanding() -> LOCATION_QS
onLockscreen && isTransformingToFullShadeAndInQQS() -> LOCATION_QQS
- // TODO(b/311234666): revisit logic once interactions between the hub and
- // shade/keyguard state are finalized
+
+ // Communal does not have its own StatusBarState so it should always have higher
+ // priority for the UMO over the lockscreen.
isCommunalShowing -> LOCATION_COMMUNAL_HUB
onLockscreen && allowMediaPlayerOnLockScreen -> LOCATION_LOCKSCREEN
else -> LOCATION_QQS
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHostStatesManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHostStatesManager.kt
index 1f711cfdd966..8660d12bcb85 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHostStatesManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHostStatesManager.kt
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import com.android.app.tracing.traceSection
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.util.animation.MeasurementOutput
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
index 962764c028fc..ad7990b92931 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
@@ -11,10 +11,10 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import android.content.Context
import android.content.res.Configuration
@@ -22,10 +22,11 @@ import androidx.annotation.VisibleForTesting
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT
import com.android.app.tracing.traceSection
-import com.android.systemui.media.controls.models.GutsViewHolder
-import com.android.systemui.media.controls.models.player.MediaViewHolder
-import com.android.systemui.media.controls.models.recommendation.RecommendationViewHolder
-import com.android.systemui.media.controls.ui.MediaCarouselController.Companion.calculateAlpha
+import com.android.systemui.media.controls.ui.controller.MediaCarouselController.Companion.calculateAlpha
+import com.android.systemui.media.controls.ui.view.GutsViewHolder
+import com.android.systemui.media.controls.ui.view.MediaHostState
+import com.android.systemui.media.controls.ui.view.MediaViewHolder
+import com.android.systemui.media.controls.ui.view.RecommendationViewHolder
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.ConfigurationController
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewLogger.kt
index 3ff2315956ad..1514db38d882 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewLogger.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/IlluminationDrawable.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/drawable/IlluminationDrawable.kt
index 5aa6824a98b1..260d30061296 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/IlluminationDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/drawable/IlluminationDrawable.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.drawable
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/LightSourceDrawable.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/drawable/LightSourceDrawable.kt
index 6ee072d41ecc..e4ce1353d30f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/LightSourceDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/drawable/LightSourceDrawable.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.drawable
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/SquigglyProgress.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/drawable/SquigglyProgress.kt
index 47df021eaf83..c417fe60219a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/SquigglyProgress.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/drawable/SquigglyProgress.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.drawable
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/GutsViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/GutsViewHolder.kt
index f5f5d388a278..a667c5819062 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/GutsViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/GutsViewHolder.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.models
+package com.android.systemui.media.controls.ui.view
import android.content.res.ColorStateList
import android.util.Log
@@ -22,11 +22,11 @@ import android.view.View
import android.view.ViewGroup
import android.widget.ImageButton
import android.widget.TextView
-import com.android.systemui.res.R
-import com.android.systemui.media.controls.ui.accentPrimaryFromScheme
-import com.android.systemui.media.controls.ui.surfaceFromScheme
-import com.android.systemui.media.controls.ui.textPrimaryFromScheme
+import com.android.systemui.media.controls.ui.animation.accentPrimaryFromScheme
+import com.android.systemui.media.controls.ui.animation.surfaceFromScheme
+import com.android.systemui.media.controls.ui.animation.textPrimaryFromScheme
import com.android.systemui.monet.ColorScheme
+import com.android.systemui.res.R
/**
* A view holder for the guts menu of a media player. The guts are shown when the user long-presses
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandler.kt
index 038582c4e999..c033e466d7d7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandler.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.view
import android.graphics.Outline
import android.util.MathUtils
@@ -31,6 +31,7 @@ import com.android.internal.annotations.VisibleForTesting
import com.android.settingslib.Utils
import com.android.systemui.Gefingerpoken
import com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS
+import com.android.systemui.media.controls.ui.controller.MediaControlPanel
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.PageIndicator
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
index 437218f9f440..d92168bf9fa4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
@@ -14,15 +14,18 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.view
import android.graphics.Rect
import android.util.ArraySet
import android.view.View
import android.view.View.OnAttachStateChangeListener
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
-import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.controller.MediaHostStatesManager
+import com.android.systemui.media.controls.ui.controller.MediaLocation
import com.android.systemui.util.animation.DisappearParameters
import com.android.systemui.util.animation.MeasurementInput
import com.android.systemui.util.animation.MeasurementOutput
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaScrollView.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaScrollView.kt
index 10512f1c4aed..b6259081e174 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaScrollView.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaScrollView.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.view
import android.content.Context
import android.os.SystemClock
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaViewHolder.kt
index 1b14f75d54ef..35309ea65a4c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/MediaViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaViewHolder.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.models.player
+package com.android.systemui.media.controls.ui.view
import android.view.LayoutInflater
import android.view.View
@@ -26,7 +26,7 @@ import android.widget.TextView
import androidx.constraintlayout.widget.Barrier
import com.android.internal.widget.CachingIconView
import com.android.systemui.res.R
-import com.android.systemui.media.controls.models.GutsViewHolder
+import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffectView
import com.android.systemui.surfaceeffects.ripple.MultiRippleView
import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseView
import com.android.systemui.util.animation.TransitionLayout
@@ -42,6 +42,7 @@ class MediaViewHolder constructor(itemView: View) {
val multiRippleView = itemView.requireViewById<MultiRippleView>(R.id.touch_ripple_view)
val turbulenceNoiseView =
itemView.requireViewById<TurbulenceNoiseView>(R.id.turbulence_noise_view)
+ val loadingEffectView = itemView.requireViewById<LoadingEffectView>(R.id.loading_effect_view)
val appIcon = itemView.requireViewById<ImageView>(R.id.icon)
val titleText = itemView.requireViewById<TextView>(R.id.header_title)
val artistText = itemView.requireViewById<TextView>(R.id.header_artist)
@@ -171,6 +172,7 @@ class MediaViewHolder constructor(itemView: View) {
setOf(
R.id.album_art,
R.id.turbulence_noise_view,
+ R.id.loading_effect_view,
R.id.touch_ripple_view,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/RecommendationViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/RecommendationViewHolder.kt
index 8ac8a2da4478..2d028d0213ff 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/RecommendationViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/RecommendationViewHolder.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.models.recommendation
+package com.android.systemui.media.controls.ui.view
import android.view.LayoutInflater
import android.view.View
@@ -23,9 +23,8 @@ import android.widget.ImageView
import android.widget.SeekBar
import android.widget.TextView
import com.android.internal.widget.CachingIconView
+import com.android.systemui.media.controls.ui.drawable.IlluminationDrawable
import com.android.systemui.res.R
-import com.android.systemui.media.controls.models.GutsViewHolder
-import com.android.systemui.media.controls.ui.IlluminationDrawable
import com.android.systemui.util.animation.TransitionLayout
private const val TAG = "RecommendationViewHolder"
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
index 13d743f8a4e4..cef1e69e7b6a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModel.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.models.player
+package com.android.systemui.media.controls.ui.viewmodel
import android.media.MediaMetadata
import android.media.session.MediaController
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/LocalMediaManagerFactory.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/LocalMediaManagerFactory.kt
index 1d3cfd2569aa..5d113a97c42f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/LocalMediaManagerFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/LocalMediaManagerFactory.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.util
import android.content.Context
import com.android.settingslib.bluetooth.LocalBluetoothManager
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaUiEventLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaUiEventLogger.kt
index 16a703a6bfdd..f8c816ca0b52 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaUiEventLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaUiEventLogger.kt
@@ -21,9 +21,9 @@ import com.android.internal.logging.InstanceIdSequence
import com.android.internal.logging.UiEvent
import com.android.internal.logging.UiEventLogger
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.ui.MediaHierarchyManager
-import com.android.systemui.media.controls.ui.MediaLocation
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.controller.MediaLocation
import com.android.systemui.res.R
import java.lang.IllegalArgumentException
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
index 8f752e59e806..d84e5dde6967 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -19,10 +19,10 @@ package com.android.systemui.media.dagger;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.LogBufferFactory;
-import com.android.systemui.media.controls.pipeline.MediaDataManager;
-import com.android.systemui.media.controls.ui.MediaHierarchyManager;
-import com.android.systemui.media.controls.ui.MediaHost;
-import com.android.systemui.media.controls.ui.MediaHostStatesManager;
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
+import com.android.systemui.media.controls.ui.controller.MediaHostStatesManager;
+import com.android.systemui.media.controls.ui.view.MediaHost;
import com.android.systemui.media.dream.dagger.MediaComplicationComponent;
import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper;
import com.android.systemui.media.taptotransfer.MediaTttFlags;
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
index 2f5f92586260..c379d0e68e76 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
@@ -16,6 +16,8 @@
package com.android.systemui.media.dialog;
+import static com.android.systemui.Flags.legacyLeAudioSharing;
+
import android.content.Context;
import android.os.Bundle;
import android.util.FeatureFlagUtils;
@@ -108,6 +110,7 @@ public class MediaOutputDialog extends MediaOutputBaseDialog {
@Override
public boolean isBroadcastSupported() {
+ if (!legacyLeAudioSharing()) return false;
boolean isBluetoothLeDevice = false;
boolean isBroadcastEnabled = false;
if (FeatureFlagUtils.isEnabled(mContext,
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
index 25d89fac1af5..02be0c1a6c2d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
@@ -35,10 +35,10 @@ import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import javax.inject.Inject
-/**
- * Factory to create [MediaOutputDialog] objects.
- */
-open class MediaOutputDialogFactory @Inject constructor(
+/** Factory to create [MediaOutputDialog] objects. */
+open class MediaOutputDialogFactory
+@Inject
+constructor(
private val context: Context,
private val mediaSessionManager: MediaSessionManager,
private val lbm: LocalBluetoothManager?,
@@ -55,46 +55,93 @@ open class MediaOutputDialogFactory @Inject constructor(
private val userTracker: UserTracker
) {
companion object {
- private const val INTERACTION_JANK_TAG = "media_output"
+ const val INTERACTION_JANK_TAG = "media_output"
var mediaOutputDialog: MediaOutputDialog? = null
}
/** Creates a [MediaOutputDialog] for the given package. */
open fun create(packageName: String, aboveStatusBar: Boolean, view: View? = null) {
- create(packageName, aboveStatusBar, view, includePlaybackAndAppMetadata = true)
+ createWithController(
+ packageName,
+ aboveStatusBar,
+ controller =
+ view?.let {
+ DialogTransitionAnimator.Controller.fromView(
+ it,
+ DialogCuj(
+ InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
+ INTERACTION_JANK_TAG
+ )
+ )
+ },
+ )
}
- open fun createDialogForSystemRouting() {
- create(packageName = null, aboveStatusBar = false, includePlaybackAndAppMetadata = false)
+ /** Creates a [MediaOutputDialog] for the given package. */
+ open fun createWithController(
+ packageName: String,
+ aboveStatusBar: Boolean,
+ controller: DialogTransitionAnimator.Controller?,
+ ) {
+ create(
+ packageName,
+ aboveStatusBar,
+ dialogTransitionAnimatorController = controller,
+ includePlaybackAndAppMetadata = true
+ )
+ }
+
+ open fun createDialogForSystemRouting(controller: DialogTransitionAnimator.Controller? = null) {
+ create(
+ packageName = null,
+ aboveStatusBar = false,
+ dialogTransitionAnimatorController = null,
+ includePlaybackAndAppMetadata = false
+ )
}
private fun create(
- packageName: String?,
- aboveStatusBar: Boolean,
- view: View? = null,
- includePlaybackAndAppMetadata: Boolean = true
+ packageName: String?,
+ aboveStatusBar: Boolean,
+ dialogTransitionAnimatorController: DialogTransitionAnimator.Controller?,
+ includePlaybackAndAppMetadata: Boolean = true
) {
// Dismiss the previous dialog, if any.
mediaOutputDialog?.dismiss()
- val controller = MediaOutputController(
- context, packageName,
- mediaSessionManager, lbm, starter, notifCollection,
- dialogTransitionAnimator, nearbyMediaDevicesManager, audioManager,
- powerExemptionManager, keyGuardManager, featureFlags, userTracker)
+ val controller =
+ MediaOutputController(
+ context,
+ packageName,
+ mediaSessionManager,
+ lbm,
+ starter,
+ notifCollection,
+ dialogTransitionAnimator,
+ nearbyMediaDevicesManager,
+ audioManager,
+ powerExemptionManager,
+ keyGuardManager,
+ featureFlags,
+ userTracker
+ )
val dialog =
- MediaOutputDialog(context, aboveStatusBar, broadcastSender, controller,
- dialogTransitionAnimator, uiEventLogger, includePlaybackAndAppMetadata)
+ MediaOutputDialog(
+ context,
+ aboveStatusBar,
+ broadcastSender,
+ controller,
+ dialogTransitionAnimator,
+ uiEventLogger,
+ includePlaybackAndAppMetadata
+ )
mediaOutputDialog = dialog
// Show the dialog.
- if (view != null) {
- dialogTransitionAnimator.showFromView(
- dialog, view,
- cuj = DialogCuj(
- InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
- INTERACTION_JANK_TAG
- )
+ if (dialogTransitionAnimatorController != null) {
+ dialogTransitionAnimator.show(
+ dialog,
+ dialogTransitionAnimatorController,
)
} else {
dialog.show()
diff --git a/packages/SystemUI/src/com/android/systemui/media/dream/MediaComplicationViewController.java b/packages/SystemUI/src/com/android/systemui/media/dream/MediaComplicationViewController.java
index b4153d72e64c..7f6398be526d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dream/MediaComplicationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dream/MediaComplicationViewController.java
@@ -21,9 +21,9 @@ import static com.android.systemui.media.dream.dagger.MediaComplicationComponent
import android.widget.FrameLayout;
-import com.android.systemui.media.controls.ui.MediaHierarchyManager;
-import com.android.systemui.media.controls.ui.MediaHost;
-import com.android.systemui.media.controls.ui.MediaHostState;
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
+import com.android.systemui.media.controls.ui.view.MediaHost;
+import com.android.systemui.media.controls.ui.view.MediaHostState;
import com.android.systemui.util.ViewController;
import javax.inject.Inject;
diff --git a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
index 08c626c9e0eb..88a5f78e407d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
@@ -27,9 +27,9 @@ import com.android.systemui.CoreStartable;
import com.android.systemui.complication.DreamMediaEntryComplication;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.media.controls.models.player.MediaData;
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData;
-import com.android.systemui.media.controls.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.shared.model.MediaData;
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData;
import javax.inject.Inject;
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt
index 8a565fa86b35..03bc9350674b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt
@@ -26,8 +26,4 @@ import javax.inject.Inject
class MediaTttFlags @Inject constructor(private val featureFlags: FeatureFlags) {
/** */
fun isMediaTttEnabled(): Boolean = featureFlags.isEnabled(Flags.MEDIA_TAP_TO_TRANSFER)
-
- /** Check whether the flag for the receiver success state is enabled. */
- fun isMediaTttReceiverSuccessRippleEnabled(): Boolean =
- featureFlags.isEnabled(Flags.MEDIA_TTT_RECEIVER_SUCCESS_RIPPLE)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 6e9485e5947c..4f062afc2af7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -72,7 +72,7 @@ open class MediaTttChipControllerReceiver @Inject constructor(
context: Context,
logger: MediaTttReceiverLogger,
windowManager: WindowManager,
- mainExecutor: DelayableExecutor,
+ @Main mainExecutor: DelayableExecutor,
accessibilityManager: AccessibilityManager,
configurationController: ConfigurationController,
dumpManager: DumpManager,
@@ -266,8 +266,7 @@ open class MediaTttChipControllerReceiver @Inject constructor(
// translation animation.
bounceAnimator.removeAllUpdateListeners()
bounceAnimator.cancel()
- if (removalReason == ChipStateReceiver.TRANSFER_TO_RECEIVER_SUCCEEDED.name &&
- mediaTttFlags.isMediaTttReceiverSuccessRippleEnabled()) {
+ if (removalReason == ChipStateReceiver.TRANSFER_TO_RECEIVER_SUCCEEDED.name) {
rippleController.expandToSuccessState(rippleView, onAnimationEnd)
animateViewTranslationAndFade(
iconContainerView,
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavbarOrientationTrackingLogger.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/NavbarOrientationTrackingLogger.kt
new file mode 100644
index 000000000000..b1bd2863695a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavbarOrientationTrackingLogger.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2024 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.navigationbar
+
+import android.view.Surface
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.dagger.NavbarOrientationTrackingLog
+import javax.inject.Inject
+
+class NavbarOrientationTrackingLogger
+@Inject
+constructor(@NavbarOrientationTrackingLog private val buffer: LogBuffer) {
+ fun logPrimaryAndSecondaryVisibility(
+ methodName: String,
+ isViewVisible: Boolean,
+ isImmersiveMode: Boolean,
+ isSecondaryHandleVisible: Boolean,
+ currentRotation: Int,
+ startingQuickSwitchRotation: Int
+ ) {
+ buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = methodName
+ bool1 = isViewVisible
+ bool2 = isImmersiveMode
+ bool3 = isSecondaryHandleVisible
+ int1 = startingQuickSwitchRotation
+ int2 = currentRotation
+ },
+ {
+ "Caller Method: $str1\n" +
+ "\tNavbar Visible: $bool1\n" +
+ "\tImmersive Mode: $bool2\n" +
+ "\tSecondary Handle Visible: $bool3\n" +
+ "\tDelta Rotation: ${getDeltaRotation(int1, int2)}\n" +
+ "\tStarting QuickSwitch Rotation: $int1\n" +
+ "\tCurrent Rotation: $int2\n"
+ }
+ )
+ }
+
+ private fun getDeltaRotation(oldRotation: Int, newRotation: Int): String {
+ var rotation: String = "0"
+ when (deltaRotation(oldRotation, newRotation)) {
+ Surface.ROTATION_90 -> {
+ rotation = "90"
+ }
+ Surface.ROTATION_180 -> {
+ rotation = "180"
+ }
+ Surface.ROTATION_270 -> {
+ rotation = "270"
+ }
+ }
+ return rotation
+ }
+
+ private fun deltaRotation(oldRotation: Int, newRotation: Int): Int {
+ var delta = newRotation - oldRotation
+ if (delta < 0) delta += 4
+ return delta
+ }
+}
+
+private const val TAG = "NavbarOrientationTracking"
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 95b75ac7a875..f4903f1f054f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -282,6 +282,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
private final Rect mSamplingBounds = new Rect();
private final Binder mInsetsSourceOwner = new Binder();
private final NavBarButtonClickLogger mNavBarButtonClickLogger;
+ private final NavbarOrientationTrackingLogger mNavbarOrientationTrackingLogger;
/**
* When quickswitching between apps of different orientations, we draw a secondary home handle
@@ -557,7 +558,8 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
WakefulnessLifecycle wakefulnessLifecycle,
TaskStackChangeListeners taskStackChangeListeners,
DisplayTracker displayTracker,
- NavBarButtonClickLogger navBarButtonClickLogger) {
+ NavBarButtonClickLogger navBarButtonClickLogger,
+ NavbarOrientationTrackingLogger navbarOrientationTrackingLogger) {
super(navigationBarView);
mFrame = navigationBarFrame;
mContext = context;
@@ -600,6 +602,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mDisplayTracker = displayTracker;
mEdgeBackGestureHandler = navBarHelper.getEdgeBackGestureHandler();
mNavBarButtonClickLogger = navBarButtonClickLogger;
+ mNavbarOrientationTrackingLogger = navbarOrientationTrackingLogger;
mNavColorSampleMargin = getResources()
.getDimensionPixelSize(R.dimen.navigation_handle_sample_horizontal_margin);
@@ -906,6 +909,8 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
| WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT;
mWindowManager.addView(mOrientationHandle, mOrientationParams);
mOrientationHandle.setVisibility(View.GONE);
+
+ logNavbarOrientation("initSecondaryHomeHandleForRotation");
mOrientationParams.setFitInsetsTypes(0 /* types*/);
mOrientationHandleGlobalLayoutListener =
() -> {
@@ -966,6 +971,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mWindowManager.updateViewLayout(mOrientationHandle, mOrientationParams);
mView.setVisibility(View.GONE);
mOrientationHandle.setVisibility(View.VISIBLE);
+ logNavbarOrientation("orientSecondaryHomeHandle");
}
private void resetSecondaryHandle() {
@@ -975,9 +981,23 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mOrientationHandle.setVisibility(View.GONE);
}
mView.setVisibility(View.VISIBLE);
+ logNavbarOrientation("resetSecondaryHandle");
setOrientedHandleSamplingRegion(null);
}
+ /**
+ * Logging method for issues concerning Navbar/secondary handle visibility.
+ */
+ private void logNavbarOrientation(String methodName) {
+ boolean isViewVisible = (mView != null) && (mView.getVisibility() == View.VISIBLE);
+ boolean isSecondaryHandleVisible =
+ (mOrientationHandle != null) && (mOrientationHandle.getVisibility()
+ == View.VISIBLE);
+ mNavbarOrientationTrackingLogger.logPrimaryAndSecondaryVisibility(methodName, isViewVisible,
+ mShowOrientedHandleForImmersiveMode, isSecondaryHandleVisible, mCurrentRotation,
+ mStartingQuickSwitchRotation);
+ }
+
private void parseCurrentSysuiState() {
NavBarHelper.CurrentSysuiState state = mNavBarHelper.getCurrentSysuiState();
if (state.mWindowStateDisplayId == mDisplayId) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
index 092f1ed7d498..152f193be3f9 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
@@ -44,6 +44,7 @@ import android.view.WindowManagerGlobal;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.RegisterStatusBarResult;
import com.android.settingslib.applications.InterestingConfigChanges;
@@ -89,7 +90,16 @@ public class NavigationBarControllerImpl implements
private final TaskbarDelegate mTaskbarDelegate;
private final NavBarHelper mNavBarHelper;
private int mNavMode;
+ /**
+ * Indicates whether the active display is a large screen, e.g. tablets, foldable devices in
+ * the unfolded state.
+ */
@VisibleForTesting boolean mIsLargeScreen;
+ /**
+ * Indicates whether the device is a phone, rather than everything else (e.g. foldables,
+ * tablets) is considered not a handheld device.
+ */
+ @VisibleForTesting boolean mIsPhone;
/** A displayId - nav bar maps. */
@VisibleForTesting
@@ -139,6 +149,8 @@ public class NavigationBarControllerImpl implements
dumpManager, autoHideController, lightBarController, pipOptional,
backAnimation.orElse(null), taskStackChangeListeners);
mIsLargeScreen = isLargeScreen(mContext);
+ mIsPhone =
+ mContext.getResources().getIntArray(R.array.config_foldedDeviceStates).length == 0;
dumpManager.registerDumpable(this);
}
@@ -253,9 +265,8 @@ public class NavigationBarControllerImpl implements
/** @return {@code true} if taskbar is enabled, false otherwise */
private boolean initializeTaskbarIfNecessary() {
- // Enable for large screens or (phone AND flag is set); assuming phone = !mIsLargeScreen
- boolean taskbarEnabled = (mIsLargeScreen || enableTaskbarNavbarUnification())
- && shouldCreateNavBarAndTaskBar(mContext.getDisplayId());
+ boolean taskbarEnabled = supportsTaskbar() && shouldCreateNavBarAndTaskBar(
+ mContext.getDisplayId());
if (taskbarEnabled) {
Trace.beginSection("NavigationBarController#initializeTaskbarIfNecessary");
@@ -274,6 +285,12 @@ public class NavigationBarControllerImpl implements
return taskbarEnabled;
}
+ @VisibleForTesting
+ boolean supportsTaskbar() {
+ // Enable for tablets, unfolded state on a foldable device or (non handheld AND flag is set)
+ return mIsLargeScreen || (!mIsPhone && enableTaskbarNavbarUnification());
+ }
+
private final CommandQueue.Callbacks mCommandQueueCallbacks = new CommandQueue.Callbacks() {
@Override
public void onDisplayRemoved(int displayId) {
diff --git a/packages/SystemUI/src/com/android/systemui/process/ProcessWrapper.java b/packages/SystemUI/src/com/android/systemui/process/ProcessWrapper.java
index 3671dd439239..b4cc196b89ed 100644
--- a/packages/SystemUI/src/com/android/systemui/process/ProcessWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/process/ProcessWrapper.java
@@ -39,7 +39,10 @@ public class ProcessWrapper {
/**
* Returns {@link UserHandle} as returned statically by {@link Process#myUserHandle()}.
*
- * Please strongly consider using {@link com.android.systemui.settings.UserTracker} instead.
+ * This should not be used to get the "current" user. This information only applies to the
+ * current process, not the current state of SystemUI. Please use
+ * {@link com.android.systemui.settings.UserTracker} if you want to learn about the currently
+ * active user in SystemUI.
*/
public UserHandle myUserHandle() {
return Process.myUserHandle();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index c0d964405ab5..4ee2db796aef 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -18,7 +18,7 @@ package com.android.systemui.qs;
import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
-import static com.android.systemui.Flags.centralizedStatusBarDimensRefactor;
+import static com.android.systemui.Flags.centralizedStatusBarHeightFix;
import android.content.Context;
import android.graphics.Canvas;
@@ -194,7 +194,7 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
int topPadding = QSUtils.getQsHeaderSystemIconsAreaHeight(mContext);
if (!LargeScreenUtils.shouldUseLargeScreenShadeHeader(mContext.getResources())) {
topPadding =
- centralizedStatusBarDimensRefactor()
+ centralizedStatusBarHeightFix()
? LargeScreenHeaderHelper.getLargeScreenHeaderHeight(mContext)
: mContext.getResources()
.getDimensionPixelSize(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
index 8ff0e365f2ce..741336277119 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
@@ -51,7 +51,7 @@ import com.android.systemui.compose.ComposeFacade;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
-import com.android.systemui.media.controls.ui.MediaHost;
+import com.android.systemui.media.controls.ui.view.MediaHost;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.qs.QSContainerController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -207,6 +207,9 @@ public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateControl
mFooterActionsViewBinder = footerActionsViewBinder;
mListeningAndVisibilityLifecycleOwner = new ListeningAndVisibilityLifecycleOwner();
mSceneContainerFlags = sceneContainerFlags;
+ if (mSceneContainerFlags.isEnabled()) {
+ mStatusBarState = StatusBarState.SHADE;
+ }
}
/**
@@ -506,10 +509,20 @@ public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateControl
}
}
- private boolean isKeyguardState() {
- // We want the freshest state here since otherwise we'll have some weirdness if earlier
- // listeners trigger updates
- return mStatusBarStateController.getCurrentOrUpcomingState() == KEYGUARD;
+ @VisibleForTesting
+ boolean isKeyguardState() {
+ if (mSceneContainerFlags.isEnabled()) {
+ return false;
+ } else {
+ // We want the freshest state here since otherwise we'll have some weirdness if earlier
+ // listeners trigger updates
+ return mStatusBarStateController.getCurrentOrUpcomingState() == KEYGUARD;
+ }
+ }
+
+ @VisibleForTesting
+ int getStatusBarState() {
+ return mStatusBarState;
}
private void updateShowCollapsedOnKeyguard() {
@@ -562,15 +575,17 @@ public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateControl
}
private void setKeyguardShowing(boolean keyguardShowing) {
- if (DEBUG) Log.d(TAG, "setKeyguardShowing " + keyguardShowing);
- mLastQSExpansion = -1;
+ if (!mSceneContainerFlags.isEnabled()) {
+ if (DEBUG) Log.d(TAG, "setKeyguardShowing " + keyguardShowing);
+ mLastQSExpansion = -1;
- if (mQSAnimator != null) {
- mQSAnimator.setOnKeyguard(keyguardShowing);
- }
+ if (mQSAnimator != null) {
+ mQSAnimator.setOnKeyguard(keyguardShowing);
+ }
- mFooter.setKeyguardShowing(keyguardShowing);
- updateQsState();
+ mFooter.setKeyguardShowing(keyguardShowing);
+ updateQsState();
+ }
}
@Override
@@ -971,7 +986,7 @@ public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateControl
@Override
public void onStateChanged(int newState) {
- if (newState == mStatusBarState) {
+ if (mSceneContainerFlags.isEnabled() || newState == mStatusBarState) {
return;
}
mStatusBarState = newState;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index c3f5086b0096..2440651555d7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -27,9 +27,9 @@ import android.view.View;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.media.controls.ui.MediaHierarchyManager;
-import com.android.systemui.media.controls.ui.MediaHost;
-import com.android.systemui.media.controls.ui.MediaHostState;
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
+import com.android.systemui.media.controls.ui.view.MediaHost;
+import com.android.systemui.media.controls.ui.view.MediaHostState;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.dagger.QSScope;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 1c37510fde34..975c871bd006 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -32,7 +32,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.Dumpable;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.media.controls.ui.MediaHost;
+import com.android.systemui.media.controls.ui.view.MediaHost;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.qs.customize.QSCustomizerController;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index f278dce047e0..a8e88da5d288 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -25,8 +25,8 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.media.controls.ui.MediaHierarchyManager;
-import com.android.systemui.media.controls.ui.MediaHost;
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
+import com.android.systemui.media.controls.ui.view.MediaHost;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.dagger.QSScope;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt
index 4bad45f19673..16aa99e22ae2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt
@@ -25,6 +25,8 @@ import com.android.systemui.qs.pipeline.data.repository.DefaultTilesQSHostReposi
import com.android.systemui.qs.pipeline.data.repository.DefaultTilesRepository
import com.android.systemui.qs.pipeline.data.repository.InstalledTilesComponentRepository
import com.android.systemui.qs.pipeline.data.repository.InstalledTilesComponentRepositoryImpl
+import com.android.systemui.qs.pipeline.data.repository.MinimumTilesRepository
+import com.android.systemui.qs.pipeline.data.repository.MinimumTilesResourceRepository
import com.android.systemui.qs.pipeline.data.repository.QSSettingsRestoredBroadcastRepository
import com.android.systemui.qs.pipeline.data.repository.QSSettingsRestoredRepository
import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository
@@ -81,6 +83,11 @@ abstract class QSPipelineModule {
impl: QSSettingsRestoredBroadcastRepository
): QSSettingsRestoredRepository
+ @Binds
+ abstract fun provideMinimumTilesRepository(
+ impl: MinimumTilesResourceRepository
+ ): MinimumTilesRepository
+
companion object {
/**
* Provides a logging buffer for all logs related to the new Quick Settings pipeline to log
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/MinimumTilesRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/MinimumTilesRepository.kt
new file mode 100644
index 000000000000..3a005c0ebfed
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/MinimumTilesRepository.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 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.qs.pipeline.data.repository
+
+import android.content.res.Resources
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+/**
+ * Provides the minimum number of tiles required in QS. The default number of tiles should be at
+ * least this many.
+ */
+interface MinimumTilesRepository {
+ val minNumberOfTiles: Int
+}
+
+/**
+ * Minimum number of tiles using the corresponding resource. The value will be read once upon
+ * creation, as it's not expected to change.
+ */
+@SysUISingleton
+class MinimumTilesResourceRepository @Inject constructor(@Main resources: Resources) :
+ MinimumTilesRepository {
+ override val minNumberOfTiles: Int =
+ resources.getInteger(R.integer.quick_settings_min_num_tiles)
+}
+
+/** Provides a fixed minimum number of tiles. */
+class MinimumTilesFixedRepository(override val minNumberOfTiles: Int = 0) : MinimumTilesRepository
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt
index e718eea09665..a2bb9e9a2f63 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt
@@ -6,6 +6,8 @@ import android.os.UserHandle
import android.provider.Settings
import android.util.Log
import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.common.shared.model.PackageChangeModel.Empty.user
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
@@ -13,18 +15,28 @@ import com.android.systemui.qs.pipeline.data.model.RestoreData
import com.android.systemui.qs.pipeline.data.repository.QSSettingsRestoredRepository.Companion.BUFFER_CAPACITY
import com.android.systemui.qs.pipeline.data.repository.TilesSettingConverter.toTilesList
import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.util.kotlin.emitOnStart
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.buffer
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flattenConcat
import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
/** Provides restored data (from Backup and Restore) for Quick Settings pipeline */
interface QSSettingsRestoredRepository {
@@ -44,34 +56,87 @@ class QSSettingsRestoredBroadcastRepository
@Inject
constructor(
broadcastDispatcher: BroadcastDispatcher,
+ private val deviceProvisionedController: DeviceProvisionedController,
logger: QSPipelineLogger,
@Application private val scope: CoroutineScope,
@Background private val backgroundDispatcher: CoroutineDispatcher,
) : QSSettingsRestoredRepository {
+ private val onUserSetupChangedForSomeUser =
+ conflatedCallbackFlow {
+ val callback =
+ object : DeviceProvisionedController.DeviceProvisionedListener {
+ override fun onUserSetupChanged() {
+ trySend(Unit)
+ }
+ }
+ deviceProvisionedController.addCallback(callback)
+ awaitClose { deviceProvisionedController.removeCallback(callback) }
+ }
+ .emitOnStart()
+
+ @OptIn(ExperimentalCoroutinesApi::class)
override val restoreData =
- flow {
+ run {
+ val mutex = Mutex()
val firstIntent = mutableMapOf<Int, Intent>()
- broadcastDispatcher
- .broadcastFlow(INTENT_FILTER, UserHandle.ALL) { intent, receiver ->
- intent to receiver.sendingUserId
- }
- .filter { it.first.isCorrectSetting() }
- .collect { (intent, user) ->
- if (user !in firstIntent) {
- firstIntent[user] = intent
- } else {
- val firstRestored = firstIntent.remove(user)!!
- emit(processIntents(user, firstRestored, intent))
+
+ val restoresFromTwoBroadcasts: Flow<RestoreData> =
+ broadcastDispatcher
+ .broadcastFlow(INTENT_FILTER, UserHandle.ALL) { intent, receiver ->
+ intent to receiver.sendingUserId
}
- }
+ .filter { it.first.isCorrectSetting() }
+ .mapNotNull { (intent, user) ->
+ mutex.withLock {
+ if (user !in firstIntent) {
+ firstIntent[user] = intent
+ null
+ } else {
+ val firstRestored = firstIntent.remove(user)!!
+ processIntents(user, firstRestored, intent)
+ }
+ }
+ }
+ .catch { Log.e(TAG, "Error parsing broadcast", it) }
+
+ val restoresFromUserSetup: Flow<RestoreData> =
+ onUserSetupChangedForSomeUser
+ .map {
+ mutex.withLock {
+ firstIntent
+ .filter { (userId, _) ->
+ deviceProvisionedController.isUserSetup(userId)
+ }
+ .onEach { firstIntent.remove(it.key) }
+ .map { processSingleIntent(it.key, it.value) }
+ .asFlow()
+ }
+ }
+ .flattenConcat()
+ .catch { Log.e(TAG, "Error parsing tiles intent after user setup", it) }
+ .onEach { logger.logSettingsRestoredOnUserSetupComplete(it.userId) }
+ merge(restoresFromTwoBroadcasts, restoresFromUserSetup)
}
- .catch { Log.e(TAG, "Error parsing broadcast", it) }
.flowOn(backgroundDispatcher)
.buffer(BUFFER_CAPACITY)
.shareIn(scope, SharingStarted.Eagerly)
.onEach(logger::logSettingsRestored)
+ private fun processSingleIntent(user: Int, intent: Intent): RestoreData {
+ intent.validateIntent()
+ if (intent.getStringExtra(Intent.EXTRA_SETTING_NAME) != TILES_SETTING) {
+ throw IllegalStateException(
+ "Single intent restored for user $user is not tiles: $intent"
+ )
+ }
+ return RestoreData(
+ (intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE) ?: "").toTilesList(),
+ emptySet(),
+ user,
+ )
+ }
+
private fun processIntents(user: Int, intent1: Intent, intent2: Intent): RestoreData {
intent1.validateIntent()
intent2.validateIntent()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt
index 00ea0b5c5ed3..214e9f097642 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt
@@ -19,12 +19,12 @@ package com.android.systemui.qs.pipeline.data.repository
import android.annotation.UserIdInt
import android.content.res.Resources
import android.util.SparseArray
-import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.qs.pipeline.data.model.RestoreData
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import com.android.systemui.res.R
import com.android.systemui.retail.data.repository.RetailModeRepository
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -68,6 +68,9 @@ interface TileSpecRepository {
suspend fun reconcileRestore(restoreData: RestoreData, currentAutoAdded: Set<TileSpec>)
+ /** Prepend the default list of tiles to the current set of tiles */
+ suspend fun prependDefault(@UserIdInt userId: Int)
+
companion object {
/** Position to indicate the end of the list */
const val POSITION_AT_END = -1
@@ -152,6 +155,12 @@ constructor(
?.reconcileRestore(restoreData, currentAutoAdded)
}
+ override suspend fun prependDefault(
+ userId: Int,
+ ) {
+ userTileRepositories.get(userId)?.prependDefault()
+ }
+
companion object {
private const val DELIMITER = TilesSettingConverter.DELIMITER
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt
index 152fd0f83811..8ad5cb2c0a34 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt
@@ -50,9 +50,8 @@ constructor(
private val defaultTiles: List<TileSpec>
get() = defaultTilesRepository.defaultTiles
- private val changeEvents = MutableSharedFlow<ChangeAction>(
- extraBufferCapacity = CHANGES_BUFFER_SIZE
- )
+ private val changeEvents =
+ MutableSharedFlow<ChangeAction>(extraBufferCapacity = CHANGES_BUFFER_SIZE)
private lateinit var _tiles: StateFlow<List<TileSpec>>
@@ -163,6 +162,10 @@ constructor(
changeEvents.emit(RestoreTiles(restoreData, currentAutoAdded))
}
+ suspend fun prependDefault() {
+ changeEvents.emit(PrependDefault(defaultTiles))
+ }
+
sealed interface ChangeAction {
fun apply(currentTiles: List<TileSpec>): List<TileSpec>
}
@@ -199,6 +202,12 @@ constructor(
}
}
+ private data class PrependDefault(val defaultTiles: List<TileSpec>) : ChangeAction {
+ override fun apply(currentTiles: List<TileSpec>): List<TileSpec> {
+ return defaultTiles + currentTiles
+ }
+ }
+
private data class RestoreTiles(
val restoreData: RestoreData,
val currentAutoAdded: Set<TileSpec>,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddable.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddable.kt
index b22119966460..3f619c08261d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddable.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/WorkTileAutoAddable.kt
@@ -64,7 +64,8 @@ constructor(
fun maybeSend(profiles: List<UserInfo>) {
if (profiles.any { it.id == userId }) {
// We are looking at the profiles of the correct user.
- if (profiles.any { it.isManagedProfile }) {
+ // They need to be a managed enabled profile.
+ if (profiles.any { it.isManagedProfile && it.isEnabled }) {
trySend(
AutoAddSignal.Add(
spec,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractor.kt
index cde38359a871..187b4445637b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/AutoAddInteractor.kt
@@ -35,6 +35,7 @@ import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filterIsInstance
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.take
@@ -55,6 +56,7 @@ constructor(
) : Dumpable {
private val initialized = AtomicBoolean(false)
+ private lateinit var currentTilesInteractor: CurrentTilesInteractor
/** Start collection of signals following the user from [currentTilesInteractor]. */
fun init(currentTilesInteractor: CurrentTilesInteractor) {
@@ -62,56 +64,72 @@ constructor(
return
}
+ this.currentTilesInteractor = currentTilesInteractor
dumpManager.registerNormalDumpable(TAG, this)
scope.launch {
currentTilesInteractor.userId.collectLatest { userId ->
coroutineScope {
- val previouslyAdded = repository.autoAddedTiles(userId).stateIn(this)
+ launch { collectAutoAddSignalsForUser(userId) }
+ launch { markTrackIfNotAddedTilesThatAreCurrent(userId) }
+ }
+ }
+ }
+ }
- autoAddables
- .map { addable ->
- val autoAddSignal = addable.autoAddSignal(userId)
- when (val lifecycle = addable.autoAddTracking) {
- is AutoAddTracking.Always -> autoAddSignal
- is AutoAddTracking.Disabled -> emptyFlow()
- is AutoAddTracking.IfNotAdded -> {
- if (lifecycle.spec !in previouslyAdded.value) {
- autoAddSignal.filterIsInstance<AutoAddSignal.Add>().take(1)
- } else {
- emptyFlow()
- }
- }
- }
+ private suspend fun markTrackIfNotAddedTilesThatAreCurrent(userId: Int) {
+ val trackIfNotAddedSpecs =
+ autoAddables
+ .map { it.autoAddTracking }
+ .filterIsInstance<AutoAddTracking.IfNotAdded>()
+ .map { it.spec }
+ currentTilesInteractor.currentTiles
+ .map { tiles -> tiles.map { it.spec } }
+ .collect {
+ it.filter { it in trackIfNotAddedSpecs }
+ .forEach { spec -> repository.markTileAdded(userId, spec) }
+ }
+ }
+
+ private suspend fun CoroutineScope.collectAutoAddSignalsForUser(userId: Int) {
+ val previouslyAdded = repository.autoAddedTiles(userId).stateIn(this)
+
+ autoAddables
+ .map { addable ->
+ val autoAddSignal = addable.autoAddSignal(userId)
+ when (val lifecycle = addable.autoAddTracking) {
+ is AutoAddTracking.Always -> autoAddSignal
+ is AutoAddTracking.Disabled -> emptyFlow()
+ is AutoAddTracking.IfNotAdded -> {
+ if (lifecycle.spec !in previouslyAdded.value) {
+ autoAddSignal.filterIsInstance<AutoAddSignal.Add>().take(1)
+ } else {
+ emptyFlow()
}
- .merge()
- .collect { signal ->
- when (signal) {
- is AutoAddSignal.Add -> {
- if (signal.spec !in previouslyAdded.value) {
- currentTilesInteractor.addTile(signal.spec, signal.position)
- qsPipelineLogger.logTileAutoAdded(
- userId,
- signal.spec,
- signal.position
- )
- repository.markTileAdded(userId, signal.spec)
- }
- }
- is AutoAddSignal.Remove -> {
- currentTilesInteractor.removeTiles(setOf(signal.spec))
- qsPipelineLogger.logTileAutoRemoved(userId, signal.spec)
- repository.unmarkTileAdded(userId, signal.spec)
- }
- is AutoAddSignal.RemoveTracking -> {
- qsPipelineLogger.logTileUnmarked(userId, signal.spec)
- repository.unmarkTileAdded(userId, signal.spec)
- }
- }
+ }
+ }
+ }
+ .merge()
+ .collect { signal ->
+ when (signal) {
+ is AutoAddSignal.Add -> {
+ if (signal.spec !in previouslyAdded.value) {
+ currentTilesInteractor.addTile(signal.spec, signal.position)
+ qsPipelineLogger.logTileAutoAdded(userId, signal.spec, signal.position)
+ repository.markTileAdded(userId, signal.spec)
}
+ }
+ is AutoAddSignal.Remove -> {
+ currentTilesInteractor.removeTiles(setOf(signal.spec))
+ qsPipelineLogger.logTileAutoRemoved(userId, signal.spec)
+ repository.unmarkTileAdded(userId, signal.spec)
+ }
+ is AutoAddSignal.RemoveTracking -> {
+ qsPipelineLogger.logTileUnmarked(userId, signal.spec)
+ repository.unmarkTileAdded(userId, signal.spec)
+ }
}
}
- }
}
override fun dump(pw: PrintWriter, args: Array<out String>) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
index 957cb1eb95d1..61896f0a3816 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
@@ -35,6 +35,7 @@ import com.android.systemui.qs.external.TileLifecycleManager
import com.android.systemui.qs.external.TileServiceKey
import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository
import com.android.systemui.qs.pipeline.data.repository.InstalledTilesComponentRepository
+import com.android.systemui.qs.pipeline.data.repository.MinimumTilesRepository
import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository
import com.android.systemui.qs.pipeline.domain.model.TileModel
import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository
@@ -131,6 +132,7 @@ constructor(
private val tileSpecRepository: TileSpecRepository,
private val installedTilesComponentRepository: InstalledTilesComponentRepository,
private val userRepository: UserRepository,
+ private val minimumTilesRepository: MinimumTilesRepository,
private val customTileStatePersister: CustomTileStatePersister,
private val newQSTileFactory: Lazy<NewQSTileFactory>,
private val tileFactory: QSFactory,
@@ -255,17 +257,23 @@ constructor(
val resolvedSpecs = newTileMap.keys.toList()
specsToTiles.clear()
specsToTiles.putAll(newTileMap)
- _currentSpecsAndTiles.value =
+ val newResolvedTiles =
newTileMap
.filter { it.value is TileOrNotInstalled.Tile }
.map {
TileModel(it.key, (it.value as TileOrNotInstalled.Tile).tile)
}
+
+ _currentSpecsAndTiles.value = newResolvedTiles
logger.logTilesNotInstalled(
newTileMap.filter { it.value is TileOrNotInstalled.NotInstalled }.keys,
newUser
)
- if (resolvedSpecs != newTileList) {
+ if (newResolvedTiles.size < minimumTilesRepository.minNumberOfTiles) {
+ // We ended up with not enough tiles (some may be not installed).
+ // Prepend the default set of tiles
+ launch { tileSpecRepository.prependDefault(currentUser.value) }
+ } else if (resolvedSpecs != newTileList) {
// There were some tiles that couldn't be created. Change the value in
// the
// repository
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt
index 7d2c6c8f70fb..e237ca9f462b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt
@@ -221,6 +221,15 @@ constructor(
)
}
+ fun logSettingsRestoredOnUserSetupComplete(userId: Int) {
+ restoreLogBuffer.log(
+ RESTORE_TAG,
+ LogLevel.DEBUG,
+ { int1 = userId },
+ { "Restored from single intent after user setup complete for user $int1" }
+ )
+ }
+
fun logSettingsRestored(restoreData: RestoreData) {
restoreLogBuffer.log(
RESTORE_TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index 1b504a81a651..24b2d8a1d7da 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -52,7 +52,7 @@ import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
-import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
+import com.android.systemui.qs.tiles.dialog.InternetDialogManager;
import com.android.systemui.statusbar.connectivity.AccessPointController;
import com.android.systemui.statusbar.connectivity.IconState;
import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
@@ -83,7 +83,7 @@ public class InternetTile extends QSTileImpl<QSTile.BooleanState> {
private int mLastTileState = LAST_STATE_UNKNOWN;
protected final InternetSignalCallback mSignalCallback = new InternetSignalCallback();
- private final InternetDialogFactory mInternetDialogFactory;
+ private final InternetDialogManager mInternetDialogManager;
final Handler mHandler;
@Inject
@@ -99,11 +99,11 @@ public class InternetTile extends QSTileImpl<QSTile.BooleanState> {
QSLogger qsLogger,
NetworkController networkController,
AccessPointController accessPointController,
- InternetDialogFactory internetDialogFactory
+ InternetDialogManager internetDialogManager
) {
super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
- mInternetDialogFactory = internetDialogFactory;
+ mInternetDialogManager = internetDialogManager;
mHandler = mainHandler;
mController = networkController;
mAccessPointController = accessPointController;
@@ -125,7 +125,7 @@ public class InternetTile extends QSTileImpl<QSTile.BooleanState> {
@Override
protected void handleClick(@Nullable View view) {
- mHandler.post(() -> mInternetDialogFactory.create(true,
+ mHandler.post(() -> mInternetDialogManager.create(true,
mAccessPointController.canConfigMobileData(),
mAccessPointController.canConfigWifi(), view));
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
index 13271c32c52f..357743bc2bd7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
@@ -33,7 +33,7 @@ import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
-import com.android.systemui.qs.tiles.dialog.InternetDialogFactory
+import com.android.systemui.qs.tiles.dialog.InternetDialogManager
import com.android.systemui.res.R
import com.android.systemui.statusbar.connectivity.AccessPointController
import com.android.systemui.statusbar.pipeline.shared.ui.binder.InternetTileBinder
@@ -44,18 +44,18 @@ import javax.inject.Inject
class InternetTileNewImpl
@Inject
constructor(
- host: QSHost,
- uiEventLogger: QsEventLogger,
- @Background backgroundLooper: Looper,
- @Main private val mainHandler: Handler,
- falsingManager: FalsingManager,
- metricsLogger: MetricsLogger,
- statusBarStateController: StatusBarStateController,
- activityStarter: ActivityStarter,
- qsLogger: QSLogger,
- viewModel: InternetTileViewModel,
- private val internetDialogFactory: InternetDialogFactory,
- private val accessPointController: AccessPointController,
+ host: QSHost,
+ uiEventLogger: QsEventLogger,
+ @Background backgroundLooper: Looper,
+ @Main private val mainHandler: Handler,
+ falsingManager: FalsingManager,
+ metricsLogger: MetricsLogger,
+ statusBarStateController: StatusBarStateController,
+ activityStarter: ActivityStarter,
+ qsLogger: QSLogger,
+ viewModel: InternetTileViewModel,
+ private val internetDialogManager: InternetDialogManager,
+ private val accessPointController: AccessPointController,
) :
QSTileImpl<QSTile.BooleanState>(
host,
@@ -86,7 +86,7 @@ constructor(
override fun handleClick(view: View?) {
mainHandler.post {
- internetDialogFactory.create(
+ internetDialogManager.create(
aboveStatusBar = true,
accessPointController.canConfigMobileData(),
accessPointController.canConfigWifi(),
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
index 03e0c1eaf3e2..0dd0a60b128a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
@@ -61,7 +61,6 @@ import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils;
import com.android.systemui.Prefs;
import com.android.systemui.accessibility.floatingmenu.AnnotationLinkSpan;
import com.android.systemui.animation.DialogTransitionAnimator;
-import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.res.R;
@@ -72,23 +71,30 @@ import com.android.wifitrackerlib.WifiEntry;
import java.util.List;
import java.util.concurrent.Executor;
-import javax.inject.Inject;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
/**
* Dialog for showing mobile network, connected Wi-Fi network and Wi-Fi networks.
*/
-@SysUISingleton
-public class InternetDialog extends SystemUIDialog implements
- InternetDialogController.InternetDialogCallback, Window.Callback {
+public class InternetDialogDelegate implements
+ SystemUIDialog.Delegate,
+ InternetDialogController.InternetDialogCallback {
private static final String TAG = "InternetDialog";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- static final long PROGRESS_DELAY_MS = 1500L;
+ private static final String ABOVE_STATUS_BAR = "above_status_bar";
+ private static final String CAN_CONFIG_MOBILE_DATA = "can_config_mobile_data";
+ private static final String CAN_CONFIG_WIFI = "can_config_wifi";
+
static final int MAX_NETWORK_COUNT = 4;
private final Handler mHandler;
private final Executor mBackgroundExecutor;
private final DialogTransitionAnimator mDialogTransitionAnimator;
+ private final boolean mAboveStatusBar;
+ private final SystemUIDialog.Factory mSystemUIDialogFactory;
@VisibleForTesting
protected InternetAdapter mAdapter;
@@ -97,19 +103,16 @@ public class InternetDialog extends SystemUIDialog implements
@VisibleForTesting
protected boolean mCanConfigWifi;
- private InternetDialogFactory mInternetDialogFactory;
- private SubscriptionManager mSubscriptionManager;
+ private final InternetDialogManager mInternetDialogManager;
private TelephonyManager mTelephonyManager;
@Nullable
private AlertDialog mAlertDialog;
- private UiEventLogger mUiEventLogger;
- private Context mContext;
- private InternetDialogController mInternetDialogController;
+ private final UiEventLogger mUiEventLogger;
+ private final InternetDialogController mInternetDialogController;
private TextView mInternetDialogTitle;
private TextView mInternetDialogSubTitle;
private View mDivider;
private ProgressBar mProgressBar;
- private LinearLayout mInternetDialogLayout;
private LinearLayout mConnectedWifListLayout;
private LinearLayout mMobileNetworkLayout;
private LinearLayout mSecondaryMobileNetworkLayout;
@@ -127,8 +130,6 @@ public class InternetDialog extends SystemUIDialog implements
private ImageView mSignalIcon;
private TextView mMobileTitleText;
private TextView mMobileSummaryText;
- private TextView mSecondaryMobileTitleText;
- private TextView mSecondaryMobileSummaryText;
private TextView mAirplaneModeSummaryText;
private Switch mMobileDataToggle;
private View mMobileToggleDivider;
@@ -139,12 +140,12 @@ public class InternetDialog extends SystemUIDialog implements
protected Button mShareWifiButton;
private Button mAirplaneModeButton;
private Drawable mBackgroundOn;
- private KeyguardStateController mKeyguard;
+ private final KeyguardStateController mKeyguard;
@Nullable
private Drawable mBackgroundOff = null;
- private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- private boolean mCanConfigMobileData;
- private boolean mCanChangeWifiState;
+ private int mDefaultDataSubId;
+ private final boolean mCanConfigMobileData;
+ private final boolean mCanChangeWifiState;
// Wi-Fi entries
private int mWifiNetworkHeight;
@Nullable
@@ -157,26 +158,41 @@ public class InternetDialog extends SystemUIDialog implements
// Wi-Fi scanning progress bar
protected boolean mIsProgressBarVisible;
-
- @Inject
- public InternetDialog(Context context, InternetDialogFactory internetDialogFactory,
- InternetDialogController internetDialogController, boolean canConfigMobileData,
- boolean canConfigWifi, boolean aboveStatusBar, UiEventLogger uiEventLogger,
+ private SystemUIDialog mDialog;
+
+ @AssistedFactory
+ public interface Factory {
+ InternetDialogDelegate create(
+ @Assisted(ABOVE_STATUS_BAR) boolean aboveStatusBar,
+ @Assisted(CAN_CONFIG_MOBILE_DATA) boolean canConfigMobileData,
+ @Assisted(CAN_CONFIG_WIFI) boolean canConfigWifi);
+ }
+
+ @AssistedInject
+ public InternetDialogDelegate(
+ Context context,
+ InternetDialogManager internetDialogManager,
+ InternetDialogController internetDialogController,
+ @Assisted(ABOVE_STATUS_BAR) boolean canConfigMobileData,
+ @Assisted(CAN_CONFIG_MOBILE_DATA) boolean canConfigWifi,
+ @Assisted(CAN_CONFIG_WIFI) boolean aboveStatusBar,
+ UiEventLogger uiEventLogger,
DialogTransitionAnimator dialogTransitionAnimator,
- @Main Handler handler, @Background Executor executor,
- KeyguardStateController keyguardStateController) {
- super(context);
+ @Main Handler handler,
+ @Background Executor executor,
+ KeyguardStateController keyguardStateController,
+ SystemUIDialog.Factory systemUIDialogFactory) {
+ mAboveStatusBar = aboveStatusBar;
+ mSystemUIDialogFactory = systemUIDialogFactory;
if (DEBUG) {
Log.d(TAG, "Init InternetDialog");
}
// Save the context that is wrapped with our theme.
- mContext = getContext();
mHandler = handler;
mBackgroundExecutor = executor;
- mInternetDialogFactory = internetDialogFactory;
+ mInternetDialogManager = internetDialogManager;
mInternetDialogController = internetDialogController;
- mSubscriptionManager = mInternetDialogController.getSubscriptionManager();
mDefaultDataSubId = mInternetDialogController.getDefaultDataSubscriptionId();
mTelephonyManager = mInternetDialogController.getTelephonyManager();
mCanConfigMobileData = canConfigMobileData;
@@ -187,31 +203,42 @@ public class InternetDialog extends SystemUIDialog implements
mUiEventLogger = uiEventLogger;
mDialogTransitionAnimator = dialogTransitionAnimator;
mAdapter = new InternetAdapter(mInternetDialogController);
- if (!aboveStatusBar) {
- getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
+ }
+
+ @Override
+ public SystemUIDialog createDialog() {
+ SystemUIDialog dialog = mSystemUIDialogFactory.create(this);
+ if (!mAboveStatusBar) {
+ dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
+ }
+
+ if (mDialog != null) {
+ mDialog.dismiss();
}
+ mDialog = dialog;
+
+ return dialog;
}
@Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
+ public void onCreate(SystemUIDialog dialog, Bundle savedInstanceState) {
if (DEBUG) {
Log.d(TAG, "onCreate");
}
+ Context context = dialog.getContext();
mUiEventLogger.log(InternetDialogEvent.INTERNET_DIALOG_SHOW);
- mDialogView = LayoutInflater.from(mContext).inflate(R.layout.internet_connectivity_dialog,
- null);
+ mDialogView = LayoutInflater.from(context).inflate(
+ R.layout.internet_connectivity_dialog, null);
mDialogView.setAccessibilityPaneTitle(
- mContext.getText(R.string.accessibility_desc_quick_settings));
- final Window window = getWindow();
+ context.getText(R.string.accessibility_desc_quick_settings));
+ final Window window = dialog.getWindow();
window.setContentView(mDialogView);
window.setWindowAnimations(R.style.Animation_InternetDialog);
- mWifiNetworkHeight = mContext.getResources()
+ mWifiNetworkHeight = context.getResources()
.getDimensionPixelSize(R.dimen.internet_dialog_wifi_network_height);
- mInternetDialogLayout = mDialogView.requireViewById(R.id.internet_connectivity_dialog);
mInternetDialogTitle = mDialogView.requireViewById(R.id.internet_dialog_title);
mInternetDialogSubTitle = mDialogView.requireViewById(R.id.internet_dialog_subtitle);
mDivider = mDialogView.requireViewById(R.id.divider);
@@ -239,20 +266,20 @@ public class InternetDialog extends SystemUIDialog implements
mMobileToggleDivider = mDialogView.requireViewById(R.id.mobile_toggle_divider);
mMobileDataToggle = mDialogView.requireViewById(R.id.mobile_toggle);
mWiFiToggle = mDialogView.requireViewById(R.id.wifi_toggle);
- mBackgroundOn = mContext.getDrawable(R.drawable.settingslib_switch_bar_bg_on);
+ mBackgroundOn = context.getDrawable(R.drawable.settingslib_switch_bar_bg_on);
mInternetDialogTitle.setText(getDialogTitleText());
mInternetDialogTitle.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
- mBackgroundOff = mContext.getDrawable(R.drawable.internet_dialog_selected_effect);
- setOnClickListener();
+ mBackgroundOff = context.getDrawable(R.drawable.internet_dialog_selected_effect);
+ setOnClickListener(dialog);
mTurnWifiOnLayout.setBackground(null);
mAirplaneModeButton.setVisibility(
mInternetDialogController.isAirplaneModeEnabled() ? View.VISIBLE : View.GONE);
- mWifiRecyclerView.setLayoutManager(new LinearLayoutManager(mContext));
+ mWifiRecyclerView.setLayoutManager(new LinearLayoutManager(context));
mWifiRecyclerView.setAdapter(mAdapter);
}
@Override
- public void start() {
+ public void onStart(SystemUIDialog dialog) {
if (DEBUG) {
Log.d(TAG, "onStart");
}
@@ -273,7 +300,7 @@ public class InternetDialog extends SystemUIDialog implements
}
@Override
- public void stop() {
+ public void onStop(SystemUIDialog dialog) {
if (DEBUG) {
Log.d(TAG, "onStop");
}
@@ -288,7 +315,7 @@ public class InternetDialog extends SystemUIDialog implements
mShareWifiButton.setOnClickListener(null);
mAirplaneModeButton.setOnClickListener(null);
mInternetDialogController.onStop();
- mInternetDialogFactory.destroyDialog();
+ mInternetDialogManager.destroyDialog();
}
@Override
@@ -296,8 +323,11 @@ public class InternetDialog extends SystemUIDialog implements
if (DEBUG) {
Log.d(TAG, "dismissDialog");
}
- mInternetDialogFactory.destroyDialog();
- dismiss();
+ mInternetDialogManager.destroyDialog();
+ if (mDialog != null) {
+ mDialog.dismiss();
+ mDialog = null;
+ }
}
/**
@@ -334,11 +364,11 @@ public class InternetDialog extends SystemUIDialog implements
updateWifiScanNotify(isWifiEnabled, isWifiScanEnabled, isDeviceLocked);
}
- private void setOnClickListener() {
+ private void setOnClickListener(SystemUIDialog dialog) {
mMobileNetworkLayout.setOnClickListener(v -> {
int autoSwitchNonDdsSubId = mInternetDialogController.getActiveAutoSwitchNonDdsSubId();
if (autoSwitchNonDdsSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- showTurnOffAutoDataSwitchDialog(autoSwitchNonDdsSubId);
+ showTurnOffAutoDataSwitchDialog(dialog, autoSwitchNonDdsSubId);
}
mInternetDialogController.connectCarrierNetwork();
});
@@ -346,10 +376,10 @@ public class InternetDialog extends SystemUIDialog implements
boolean isChecked = mMobileDataToggle.isChecked();
if (!isChecked && shouldShowMobileDialog()) {
mMobileDataToggle.setChecked(true);
- showTurnOffMobileDialog();
+ showTurnOffMobileDialog(dialog);
} else if (mInternetDialogController.isMobileDataEnabled() != isChecked) {
- mInternetDialogController.setMobileDataEnabled(mContext, mDefaultDataSubId,
- isChecked, false);
+ mInternetDialogController.setMobileDataEnabled(
+ dialog.getContext(), mDefaultDataSubId, isChecked, false);
}
});
mConnectedWifListLayout.setOnClickListener(this::onClickConnectedWifi);
@@ -359,7 +389,7 @@ public class InternetDialog extends SystemUIDialog implements
if (mInternetDialogController.isWifiEnabled() == isChecked) return;
mInternetDialogController.setWifiEnabled(isChecked);
});
- mDoneButton.setOnClickListener(v -> dismiss());
+ mDoneButton.setOnClickListener(v -> dialog.dismiss());
mShareWifiButton.setOnClickListener(v -> {
if (mInternetDialogController.mayLaunchShareWifiSettings(mConnectedWifiEntry)) {
mUiEventLogger.log(InternetDialogEvent.SHARE_WIFI_QS_BUTTON_CLICKED);
@@ -378,6 +408,14 @@ public class InternetDialog extends SystemUIDialog implements
private void setMobileDataLayout(boolean activeNetworkIsCellular,
boolean isCarrierNetworkActive) {
+
+ if (mDialog != null) {
+ setMobileDataLayout(mDialog, activeNetworkIsCellular, isCarrierNetworkActive);
+ }
+ }
+
+ private void setMobileDataLayout(SystemUIDialog dialog, boolean activeNetworkIsCellular,
+ boolean isCarrierNetworkActive) {
boolean isNetworkConnected = activeNetworkIsCellular || isCarrierNetworkActive;
// 1. Mobile network should be gone if airplane mode ON or the list of active
// subscriptionId is null.
@@ -431,19 +469,19 @@ public class InternetDialog extends SystemUIDialog implements
if (stub != null) {
stub.inflate();
}
- mSecondaryMobileNetworkLayout = findViewById(R.id.secondary_mobile_network_layout);
+ mSecondaryMobileNetworkLayout = dialog.findViewById(
+ R.id.secondary_mobile_network_layout);
mSecondaryMobileNetworkLayout.setOnClickListener(
this::onClickConnectedSecondarySub);
mSecondaryMobileNetworkLayout.setBackground(mBackgroundOn);
- mSecondaryMobileTitleText = mDialogView.requireViewById(
+ TextView mSecondaryMobileTitleText = mDialogView.requireViewById(
R.id.secondary_mobile_title);
mSecondaryMobileTitleText.setText(getMobileNetworkTitle(autoSwitchNonDdsSubId));
mSecondaryMobileTitleText.setTextAppearance(
R.style.TextAppearance_InternetDialog_Active);
- mSecondaryMobileSummaryText =
- mDialogView.requireViewById(R.id.secondary_mobile_summary);
+ TextView mSecondaryMobileSummaryText = mDialogView.requireViewById(R.id.secondary_mobile_summary);
summary = getMobileNetworkSummary(autoSwitchNonDdsSubId);
if (!TextUtils.isEmpty(summary)) {
mSecondaryMobileSummaryText.setText(
@@ -465,7 +503,7 @@ public class InternetDialog extends SystemUIDialog implements
ImageView mSecondaryMobileSettingsIcon =
mDialogView.requireViewById(R.id.secondary_settings_icon);
mSecondaryMobileSettingsIcon.setColorFilter(
- mContext.getColor(R.color.connected_network_primary_color));
+ dialog.getContext().getColor(R.color.connected_network_primary_color));
// set secondary visual for default data sub
mMobileNetworkLayout.setBackground(mBackgroundOff);
@@ -473,7 +511,7 @@ public class InternetDialog extends SystemUIDialog implements
mMobileSummaryText.setTextAppearance(
R.style.TextAppearance_InternetDialog_Secondary);
mSignalIcon.setColorFilter(
- mContext.getColor(R.color.connected_network_secondary_color));
+ dialog.getContext().getColor(R.color.connected_network_secondary_color));
} else {
mMobileNetworkLayout.setBackground(
isNetworkConnected ? mBackgroundOn : mBackgroundOff);
@@ -491,7 +529,8 @@ public class InternetDialog extends SystemUIDialog implements
// Set airplane mode to the summary for carrier network
if (mInternetDialogController.isAirplaneModeEnabled()) {
mAirplaneModeSummaryText.setVisibility(View.VISIBLE);
- mAirplaneModeSummaryText.setText(mContext.getText(R.string.airplane_mode));
+ mAirplaneModeSummaryText.setText(
+ dialog.getContext().getText(R.string.airplane_mode));
mAirplaneModeSummaryText.setTextAppearance(secondaryRes);
} else {
mAirplaneModeSummaryText.setVisibility(View.GONE);
@@ -523,7 +562,7 @@ public class InternetDialog extends SystemUIDialog implements
@MainThread
private void updateConnectedWifi(boolean isWifiEnabled, boolean isDeviceLocked) {
- if (!isWifiEnabled || mConnectedWifiEntry == null || isDeviceLocked) {
+ if (mDialog == null || !isWifiEnabled || mConnectedWifiEntry == null || isDeviceLocked) {
mConnectedWifListLayout.setVisibility(View.GONE);
mShareWifiButton.setVisibility(View.GONE);
return;
@@ -534,7 +573,7 @@ public class InternetDialog extends SystemUIDialog implements
mConnectedWifiIcon.setImageDrawable(
mInternetDialogController.getInternetWifiDrawable(mConnectedWifiEntry));
mWifiSettingsIcon.setColorFilter(
- mContext.getColor(R.color.connected_network_primary_color));
+ mDialog.getContext().getColor(R.color.connected_network_primary_color));
if (mInternetDialogController.getConfiguratorQrCodeGeneratorIntentOrNull(
mConnectedWifiEntry) != null) {
mShareWifiButton.setVisibility(View.VISIBLE);
@@ -593,7 +632,7 @@ public class InternetDialog extends SystemUIDialog implements
@MainThread
private void updateWifiScanNotify(boolean isWifiEnabled, boolean isWifiScanEnabled,
boolean isDeviceLocked) {
- if (isWifiEnabled || !isWifiScanEnabled || isDeviceLocked) {
+ if (mDialog == null || isWifiEnabled || !isWifiScanEnabled || isDeviceLocked) {
mWifiScanNotifyLayout.setVisibility(View.GONE);
return;
}
@@ -602,7 +641,7 @@ public class InternetDialog extends SystemUIDialog implements
AnnotationLinkSpan.LinkInfo.DEFAULT_ANNOTATION,
mInternetDialogController::launchWifiScanningSetting);
mWifiScanNotifyText.setText(AnnotationLinkSpan.linkify(
- getContext().getText(R.string.wifi_scan_notify_message), linkInfo));
+ mDialog.getContext().getText(R.string.wifi_scan_notify_message), linkInfo));
mWifiScanNotifyText.setMovementMethod(LinkMovementMethod.getInstance());
}
mWifiScanNotifyLayout.setVisibility(View.VISIBLE);
@@ -657,7 +696,10 @@ public class InternetDialog extends SystemUIDialog implements
}
private boolean shouldShowMobileDialog() {
- boolean flag = Prefs.getBoolean(mContext, QS_HAS_TURNED_OFF_MOBILE_DATA,
+ if (mDialog == null) {
+ return false;
+ }
+ boolean flag = Prefs.getBoolean(mDialog.getContext(), QS_HAS_TURNED_OFF_MOBILE_DATA,
false);
if (mInternetDialogController.isMobileDataEnabled() && !flag) {
return true;
@@ -665,40 +707,42 @@ public class InternetDialog extends SystemUIDialog implements
return false;
}
- private void showTurnOffMobileDialog() {
+ private void showTurnOffMobileDialog(SystemUIDialog dialog) {
+ Context context = dialog.getContext();
CharSequence carrierName = getMobileNetworkTitle(mDefaultDataSubId);
boolean isInService = mInternetDialogController.isVoiceStateInService(mDefaultDataSubId);
if (TextUtils.isEmpty(carrierName) || !isInService) {
- carrierName = mContext.getString(R.string.mobile_data_disable_message_default_carrier);
+ carrierName = context.getString(R.string.mobile_data_disable_message_default_carrier);
}
- mAlertDialog = new Builder(mContext)
+ mAlertDialog = new AlertDialog.Builder(context)
.setTitle(R.string.mobile_data_disable_title)
- .setMessage(mContext.getString(R.string.mobile_data_disable_message, carrierName))
+ .setMessage(context.getString(R.string.mobile_data_disable_message, carrierName))
.setNegativeButton(android.R.string.cancel, (d, w) -> {
})
.setPositiveButton(
com.android.internal.R.string.alert_windows_notification_turn_off_action,
(d, w) -> {
- mInternetDialogController.setMobileDataEnabled(mContext,
+ mInternetDialogController.setMobileDataEnabled(context,
mDefaultDataSubId, false, false);
mMobileDataToggle.setChecked(false);
- Prefs.putBoolean(mContext, QS_HAS_TURNED_OFF_MOBILE_DATA, true);
+ Prefs.putBoolean(context, QS_HAS_TURNED_OFF_MOBILE_DATA, true);
})
.create();
mAlertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
SystemUIDialog.setShowForAllUsers(mAlertDialog, true);
SystemUIDialog.registerDismissListener(mAlertDialog);
SystemUIDialog.setWindowOnTop(mAlertDialog, mKeyguard.isShowing());
- mDialogTransitionAnimator.showFromDialog(mAlertDialog, this, null, false);
+ mDialogTransitionAnimator.showFromDialog(mAlertDialog, dialog, null, false);
}
- private void showTurnOffAutoDataSwitchDialog(int subId) {
+ private void showTurnOffAutoDataSwitchDialog(SystemUIDialog dialog, int subId) {
+ Context context = dialog.getContext();
CharSequence carrierName = getMobileNetworkTitle(mDefaultDataSubId);
if (TextUtils.isEmpty(carrierName)) {
- carrierName = mContext.getString(R.string.mobile_data_disable_message_default_carrier);
+ carrierName = context.getString(R.string.mobile_data_disable_message_default_carrier);
}
- mAlertDialog = new Builder(mContext)
- .setTitle(mContext.getString(R.string.auto_data_switch_disable_title, carrierName))
+ mAlertDialog = new AlertDialog.Builder(context)
+ .setTitle(context.getString(R.string.auto_data_switch_disable_title, carrierName))
.setMessage(R.string.auto_data_switch_disable_message)
.setNegativeButton(R.string.auto_data_switch_dialog_negative_button,
(d, w) -> {
@@ -716,7 +760,7 @@ public class InternetDialog extends SystemUIDialog implements
SystemUIDialog.setShowForAllUsers(mAlertDialog, true);
SystemUIDialog.registerDismissListener(mAlertDialog);
SystemUIDialog.setWindowOnTop(mAlertDialog, mKeyguard.isShowing());
- mDialogTransitionAnimator.showFromDialog(mAlertDialog, this, null, false);
+ mDialogTransitionAnimator.showFromDialog(mAlertDialog, dialog, null, false);
}
@Override
@@ -802,11 +846,10 @@ public class InternetDialog extends SystemUIDialog implements
}
@Override
- public void onWindowFocusChanged(boolean hasFocus) {
- super.onWindowFocusChanged(hasFocus);
+ public void onWindowFocusChanged(SystemUIDialog dialog, boolean hasFocus) {
if (mAlertDialog != null && !mAlertDialog.isShowing()) {
- if (!hasFocus && isShowing()) {
- dismiss();
+ if (!hasFocus && dialog.isShowing()) {
+ dialog.dismiss();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt
index c5f89834d5ce..2a177c791a03 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt
@@ -15,64 +15,49 @@
*/
package com.android.systemui.qs.tiles.dialog
-import android.content.Context
-import android.os.Handler
import android.util.Log
import android.view.View
import com.android.internal.jank.InteractionJankMonitor
-import com.android.internal.logging.UiEventLogger
import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.statusbar.policy.KeyguardStateController
-import java.util.concurrent.Executor
+import com.android.systemui.statusbar.phone.SystemUIDialog
import javax.inject.Inject
private const val TAG = "InternetDialogFactory"
private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
/**
- * Factory to create [InternetDialog] objects.
+ * Factory to create [InternetDialogDelegate] objects.
*/
@SysUISingleton
-class InternetDialogFactory @Inject constructor(
- @Main private val handler: Handler,
- @Background private val executor: Executor,
- private val internetDialogController: InternetDialogController,
- private val context: Context,
- private val uiEventLogger: UiEventLogger,
+class InternetDialogManager @Inject constructor(
private val dialogTransitionAnimator: DialogTransitionAnimator,
- private val keyguardStateController: KeyguardStateController
+ private val dialogFactory: InternetDialogDelegate.Factory
) {
companion object {
private const val INTERACTION_JANK_TAG = "internet"
- var internetDialog: InternetDialog? = null
+ var dialog: SystemUIDialog? = null
}
- /** Creates a [InternetDialog]. The dialog will be animated from [view] if it is not null. */
+ /** Creates a [InternetDialogDelegate]. The dialog will be animated from [view] if it is not null. */
fun create(
aboveStatusBar: Boolean,
canConfigMobileData: Boolean,
canConfigWifi: Boolean,
view: View?
) {
- if (internetDialog != null) {
+ if (dialog != null) {
if (DEBUG) {
Log.d(TAG, "InternetDialog is showing, do not create it twice.")
}
return
} else {
- internetDialog = InternetDialog(
- context, this, internetDialogController,
- canConfigMobileData, canConfigWifi, aboveStatusBar, uiEventLogger,
- dialogTransitionAnimator, handler,
- executor, keyguardStateController
- )
+ dialog = dialogFactory.create(
+ aboveStatusBar, canConfigMobileData, canConfigWifi).createDialog()
if (view != null) {
dialogTransitionAnimator.showFromView(
- internetDialog!!, view,
+ dialog!!, view,
animateBackgroundBoundsChange = true,
cuj = DialogCuj(
InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
@@ -80,7 +65,7 @@ class InternetDialogFactory @Inject constructor(
)
)
} else {
- internetDialog?.show()
+ dialog!!.show()
}
}
}
@@ -89,6 +74,6 @@ class InternetDialogFactory @Inject constructor(
if (DEBUG) {
Log.d(TAG, "destroyDialog")
}
- internetDialog = null
+ dialog = null
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt
index cff95d8368a2..1b3e58524815 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt
@@ -68,8 +68,7 @@ constructor(
serviceInteractor.setUser(user)
// Wait for the CustomTileInteractor to become initialized first, because
- // binding
- // the service might access it
+ // binding the service might access it
customTileInteractor.initForUser(user)
// Bind the TileService for not active tile
serviceInteractor.bindOnStart()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt
index fd96fc5b6693..3e507cda4805 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt
@@ -77,6 +77,13 @@ constructor(
suspend fun isTileToggleable(): Boolean = customTileRepository.isTileToggleable()
/**
+ * True if the tile is active and false the otherwise. This effectively is a value of the
+ * [android.service.quicksettings.TileService.META_DATA_ACTIVE_TILE]. This is not the same as
+ * [Tile.STATE_ACTIVE].
+ */
+ suspend fun isTileActive(): Boolean = customTileRepository.isTileActive()
+
+ /**
* Initializes the repository for the current user. Suspends until it's safe to call [getTile]
* which needs at least one of the following:
* - defaults are loaded;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileServiceInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileServiceInteractor.kt
index acff40f816a9..79e903c7bce9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileServiceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileServiceInteractor.kt
@@ -58,7 +58,7 @@ constructor(
private val activityStarter: ActivityStarter,
private val userActionInteractor: Lazy<CustomTileUserActionInteractor>,
private val customTileInteractor: CustomTileInteractor,
- private val userRepository: UserRepository,
+ userRepository: UserRepository,
private val qsTileLogger: QSTileLogger,
private val tileServices: TileServices,
@QSTileScope private val tileScope: CoroutineScope,
@@ -78,10 +78,10 @@ constructor(
get() = tileReceivingInterface.mutableRefreshEvents
/** Clears all pending binding for an active tile and binds not active one. */
- fun bindOnStart() {
+ suspend fun bindOnStart() {
try {
with(getTileServiceManager()) {
- if (isActiveTile) {
+ if (customTileInteractor.isTileActive()) {
clearPendingBind()
} else {
setBindRequested(true)
@@ -94,10 +94,10 @@ constructor(
}
/** Binds active tile WITHOUT CLEARING pending binds. */
- fun bindOnClick() {
+ suspend fun bindOnClick() {
try {
with(getTileServiceManager()) {
- if (isActiveTile) {
+ if (customTileInteractor.isTileActive()) {
setBindRequested(true)
tileServiceInterface.onStartListening()
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt
index c3e1feaa6dfe..a16ac360e7e4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt
@@ -77,7 +77,7 @@ constructor(
qsTileLogger.logCustomTileUserActionDelivered(tileSpec)
}
- private fun click(
+ private suspend fun click(
view: View?,
activityLaunchForClick: PendingIntent?,
) {
@@ -114,9 +114,6 @@ constructor(
}
fun startActivityAndCollapse(pendingIntent: PendingIntent) {
- if (!pendingIntent.isActivity) {
- return
- }
if (!isTokenGranted) {
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
index 6e4f72daaf9c..c1b20374dbac 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
@@ -84,18 +84,40 @@ interface QSSceneAdapter {
*/
val qsHeight: Int
- sealed class State(
- val isVisible: Boolean,
- val expansion: Float,
- ) {
- data object CLOSED : State(false, 0f)
- data class Expanding(val progress: Float) : State(true, progress)
+ /** Compatibility for use by LockscreenShadeTransitionController. Matches default from [QS] */
+ val isQsFullyCollapsed: Boolean
+ get() = true
+
+ sealed interface State {
+
+ val isVisible: Boolean
+ val expansion: Float
+ val squishiness: Float
+
+ data object CLOSED : State {
+ override val isVisible = false
+ override val expansion = 0f
+ override val squishiness = 1f
+ }
+
+ /** State for expanding between QQS and QS */
+ data class Expanding(override val expansion: Float) : State {
+ override val isVisible = true
+ override val squishiness = 1f
+ }
+
+ /** State for appearing QQS from Lockscreen or Gone */
+ data class Unsquishing(override val squishiness: Float) : State {
+ override val isVisible = true
+ override val expansion = 0f
+ }
companion object {
// These are special cases of the expansion.
val QQS = Expanding(0f)
val QS = Expanding(1f)
+ /** Collapsing from QS to QQS. [progress] is 0f in QS and 1f in QQS. */
fun Collapsing(progress: Float) = Expanding(1f - progress)
}
}
@@ -147,6 +169,10 @@ constructor(
override val qsHeight: Int
get() = qsImpl.value?.qsHeight ?: 0
+ // If value is null, there's no QS and therefore it's fully collapsed.
+ override val isQsFullyCollapsed: Boolean
+ get() = qsImpl.value?.isFullyCollapsed ?: true
+
// Same config changes as in FragmentHostManager
private val interestingChanges =
InterestingConfigChanges(
@@ -232,7 +258,7 @@ constructor(
setQsVisible(state.isVisible)
setExpanded(state.isVisible)
setListening(state.isVisible)
- setQsExpansion(state.expansion, 1f, 0f, 1f)
- setTransitionToFullShadeProgress(false, 1f, 1f)
+ setQsExpansion(state.expansion, 1f, 0f, state.squishiness)
+ setTransitionToFullShadeProgress(false, 1f, state.squishiness)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
index 494c86c7b8c8..0add4443fa7a 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
@@ -19,7 +19,6 @@ package com.android.systemui.scene.domain.interactor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor
-import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.data.repository.SceneContainerRepository
import com.android.systemui.scene.shared.logger.SceneLogger
import com.android.systemui.scene.shared.model.ObservableTransitionState
@@ -50,7 +49,6 @@ class SceneInteractor
constructor(
@Application private val applicationScope: CoroutineScope,
private val repository: SceneContainerRepository,
- private val powerInteractor: PowerInteractor,
private val logger: SceneLogger,
private val deviceUnlockedInteractor: DeviceUnlockedInteractor,
) {
@@ -189,9 +187,4 @@ constructor(
fun setTransitionState(transitionState: Flow<ObservableTransitionState>?) {
repository.setTransitionState(transitionState)
}
-
- /** Handles a user input event. */
- fun onUserInput() {
- powerInteractor.onUserTouch()
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 605a5d9b6772..b642d38289fe 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -41,6 +41,7 @@ import com.android.systemui.scene.shared.logger.SceneLogger
import com.android.systemui.scene.shared.model.ObservableTransitionState
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.statusbar.NotificationShadeWindowController
+import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor
import com.android.systemui.statusbar.notification.stack.shared.flexiNotifsEnabled
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor
@@ -87,6 +88,7 @@ constructor(
private val windowController: NotificationShadeWindowController,
private val deviceProvisioningInteractor: DeviceProvisioningInteractor,
private val centralSurfaces: CentralSurfaces,
+ private val headsUpInteractor: HeadsUpNotificationInteractor,
) : CoreStartable {
override fun start() {
@@ -147,6 +149,15 @@ constructor(
}
}
}
+ .combine(headsUpInteractor.isHeadsUpOrAnimatingAway) {
+ visibilityForTransitionState,
+ isHeadsUpOrAnimatingAway ->
+ if (isHeadsUpOrAnimatingAway) {
+ true to "showing a HUN"
+ } else {
+ visibilityForTransitionState
+ }
+ }
.distinctUntilChanged()
} else {
flowOf(false to "Device not provisioned or Factory Reset Protection active")
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
index a755805d1872..8408c51c86dc 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
@@ -19,15 +19,17 @@
package com.android.systemui.scene.shared.flag
import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR
+import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
import com.android.systemui.Flags.keyguardBottomAreaRefactor
+import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.Flags.sceneContainer
import com.android.systemui.compose.ComposeFacade
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FlagToken
import com.android.systemui.flags.Flags.SCENE_CONTAINER_ENABLED
import com.android.systemui.flags.RefactorFlagUtils
-import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
+import com.android.systemui.keyguard.shared.ComposeLockscreen
import com.android.systemui.media.controls.util.MediaInSceneContainerFlag
import dagger.Module
import dagger.Provides
@@ -43,7 +45,8 @@ object SceneContainerFlag {
SCENE_CONTAINER_ENABLED && // mainStaticFlag
sceneContainer() && // mainAconfigFlag
keyguardBottomAreaRefactor() &&
- KeyguardShadeMigrationNssl.isEnabled &&
+ migrateClocksToBlueprint() &&
+ ComposeLockscreen.isEnabled &&
MediaInSceneContainerFlag.isEnabled &&
// NOTE: Changes should also be made in getSecondaryFlags and @EnableSceneContainer
ComposeFacade.isComposeAvailable()
@@ -63,7 +66,8 @@ object SceneContainerFlag {
inline fun getSecondaryFlags(): Sequence<FlagToken> =
sequenceOf(
FlagToken(FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR, keyguardBottomAreaRefactor()),
- KeyguardShadeMigrationNssl.token,
+ FlagToken(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT, migrateClocksToBlueprint()),
+ ComposeLockscreen.token,
MediaInSceneContainerFlag.token,
// NOTE: Changes should also be made in isEnabled and @EnableSceneContainer
)
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/UserActionResult.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/UserActionResult.kt
index e1b96e4db938..c6ae21505c68 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/UserActionResult.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/UserActionResult.kt
@@ -22,13 +22,6 @@ data class UserActionResult(
val toScene: SceneKey,
/**
- * The distance the action takes to animate from 0% to 100%.
- *
- * If `null`, a default distance will be used depending on the [UserAction] performed.
- */
- val distance: UserActionDistance? = null,
-
- /**
* The key of the transition that should be used, if a specific one should be used.
*
* If `null`, the transition used will be the corresponding transition from the collection
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt
index 7cff7ff1fd71..4c2c97981702 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt
@@ -25,8 +25,8 @@ import android.view.View
import android.view.WindowInsets
import android.widget.FrameLayout
import androidx.core.view.updateMargins
-import com.android.systemui.res.R
import com.android.systemui.compose.ComposeFacade
+import com.android.systemui.res.R
/** A view that can serve as the root of the main SysUI window. */
open class WindowRootView(
@@ -71,6 +71,7 @@ open class WindowRootView(
override fun onApplyWindowInsets(windowInsets: WindowInsets): WindowInsets? {
val insets = windowInsets.getInsetsIgnoringVisibility(WindowInsets.Type.systemBars())
+ val displayCutout = rootWindowInsets.displayCutout
if (fitsSystemWindows) {
val paddingChanged = insets.top != paddingTop || insets.bottom != paddingBottom
@@ -78,22 +79,23 @@ open class WindowRootView(
if (paddingChanged) {
setPadding(0, 0, 0, 0)
}
+
+ val pairInsets: Pair<Int, Int> =
+ layoutInsetsController.getinsets(windowInsets, displayCutout)
+ leftInset = pairInsets.first
+ rightInset = pairInsets.second
+ applyMargins()
} else {
val changed =
paddingLeft != 0 || paddingRight != 0 || paddingTop != 0 || paddingBottom != 0
if (changed) {
setPadding(0, 0, 0, 0)
}
+
+ leftInset = 0
+ rightInset = 0
}
- leftInset = 0
- rightInset = 0
- val displayCutout = rootWindowInsets.displayCutout
- val pairInsets: Pair<Int, Int> =
- layoutInsetsController.getinsets(windowInsets, displayCutout)
- leftInset = pairInsets.first
- rightInset = pairInsets.second
- applyMargins()
return windowInsets
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
index 5d290cefcd55..4cd3baa7a52e 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
@@ -17,8 +17,10 @@
package com.android.systemui.scene.ui.viewmodel
import android.view.MotionEvent
+import com.android.systemui.classifier.Classifier
import com.android.systemui.classifier.domain.interactor.FalsingInteractor
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.ObservableTransitionState
import com.android.systemui.scene.shared.model.SceneKey
@@ -33,6 +35,7 @@ class SceneContainerViewModel
constructor(
private val sceneInteractor: SceneInteractor,
private val falsingInteractor: FalsingInteractor,
+ private val powerInteractor: PowerInteractor,
) {
/**
* Keys of all scenes in the container.
@@ -63,7 +66,7 @@ constructor(
* Call this before the [MotionEvent] starts to propagate through the UI hierarchy.
*/
fun onMotionEvent(event: MotionEvent) {
- sceneInteractor.onUserInput()
+ powerInteractor.onUserTouch()
falsingInteractor.onTouchEvent(event)
}
@@ -77,7 +80,33 @@ constructor(
falsingInteractor.onMotionEventComplete()
}
- companion object {
- private const val SCENE_TRANSITION_LOGGING_REASON = "user input"
+ /**
+ * Returns `true` if a change to [toScene] is currently allowed; `false` otherwise.
+ *
+ * This is invoked only for user-initiated transitions. The goal is to check with the falsing
+ * system whether the change from the current scene to the given scene should be rejected due to
+ * it being a false touch.
+ */
+ fun canChangeScene(toScene: SceneKey): Boolean {
+ val interactionTypeOrNull =
+ when (toScene) {
+ SceneKey.Bouncer -> Classifier.BOUNCER_UNLOCK
+ SceneKey.Gone -> Classifier.UNLOCK
+ SceneKey.Shade -> Classifier.NOTIFICATION_DRAG_DOWN
+ SceneKey.QuickSettings -> Classifier.QUICK_SETTINGS
+ else -> null
+ }
+
+ return interactionTypeOrNull?.let { interactionType ->
+ // It's important that the falsing system is always queried, even if no enforcement will
+ // occur. This helps build up the right signal in the system.
+ val isFalseTouch = falsingInteractor.isFalseTouch(interactionType)
+
+ // Only enforce falsing if moving from the lockscreen scene to a new scene.
+ val fromLockscreenScene = currentScene.value == SceneKey.Lockscreen
+
+ !fromLockscreenScene || !isFalseTouch
+ }
+ ?: true
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
index bee315261f89..fb5339df7212 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
@@ -31,10 +31,13 @@ import android.view.WindowManager
import android.view.WindowManagerGlobal
import com.android.app.tracing.coroutines.launch
import com.android.internal.infra.ServiceConnector
+import com.android.systemui.Flags.screenshotActionDismissSystemWindows
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.settings.DisplayTracker
+import com.android.systemui.shared.system.ActivityManagerWrapper
+import com.android.systemui.statusbar.phone.CentralSurfaces
import javax.inject.Inject
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineDispatcher
@@ -46,9 +49,11 @@ class ActionIntentExecutor
@Inject
constructor(
private val context: Context,
+ private val activityManagerWrapper: ActivityManagerWrapper,
@Application private val applicationScope: CoroutineScope,
@Main private val mainDispatcher: CoroutineDispatcher,
private val displayTracker: DisplayTracker,
+ private val keyguardController: ScreenshotKeyguardController,
) {
/**
* Execute the given intent with startActivity while performing operations for screenshot action
@@ -74,7 +79,14 @@ constructor(
user: UserHandle,
overrideTransition: Boolean,
) {
- dismissKeyguard()
+ if (screenshotActionDismissSystemWindows()) {
+ keyguardController.dismiss()
+ activityManagerWrapper.closeSystemWindows(
+ CentralSurfaces.SYSTEM_DIALOG_REASON_SCREENSHOT
+ )
+ } else {
+ dismissKeyguard()
+ }
if (user == myUserHandle()) {
withContext(mainDispatcher) { context.startActivity(intent, options) }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotKeyguardController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotKeyguardController.kt
new file mode 100644
index 000000000000..7696bbe3763e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotKeyguardController.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.systemui.screenshot
+
+import android.content.Context
+import android.content.Intent
+import com.android.internal.infra.ServiceConnector
+import javax.inject.Inject
+import kotlinx.coroutines.CompletableDeferred
+
+open class ScreenshotKeyguardController @Inject constructor(context: Context) {
+ private val proxyConnector: ServiceConnector<IScreenshotProxy> =
+ ServiceConnector.Impl(
+ context,
+ Intent(context, ScreenshotProxyService::class.java),
+ Context.BIND_AUTO_CREATE or Context.BIND_WAIVE_PRIORITY or Context.BIND_NOT_VISIBLE,
+ context.userId,
+ IScreenshotProxy.Stub::asInterface
+ )
+
+ suspend fun dismiss() {
+ val completion = CompletableDeferred<Unit>()
+ val onDoneBinder =
+ object : IOnDoneCallback.Stub() {
+ override fun onDone(success: Boolean) {
+ completion.complete(Unit)
+ }
+ }
+ proxyConnector.post { it.dismissKeyguard(onDoneBinder) }
+ completion.await()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
index 86f652389b42..d5ab3066bfde 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
@@ -17,15 +17,16 @@ package com.android.systemui.screenshot
import android.content.Intent
import android.os.IBinder
+import android.os.RemoteException
import android.util.Log
import androidx.lifecycle.LifecycleService
import androidx.lifecycle.lifecycleScope
+import com.android.app.tracing.coroutines.launch
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.shade.ShadeExpansionStateManager
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
-import com.android.app.tracing.coroutines.launch
import kotlinx.coroutines.withContext
/** Provides state from the main SystemUI process on behalf of the Screenshot process. */
@@ -56,7 +57,13 @@ constructor(
private suspend fun executeAfterDismissing(callback: IOnDoneCallback) =
withContext(mMainDispatcher) {
activityStarter.executeRunnableDismissingKeyguard(
- Runnable { callback.onDone(true) },
+ {
+ try {
+ callback.onDone(true)
+ } catch (e: RemoteException) {
+ Log.w(TAG, "Failed to complete callback transaction", e)
+ }
+ },
null,
true /* dismissShade */,
true /* afterKeyguardGone */,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
index 238a552604ca..2c0bddecc58e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
@@ -58,8 +58,16 @@ constructor(
}
override fun playCameraSound(): Deferred<Unit> {
- return coroutineScope.async("playCameraSound", bgDispatcher) { player.await()?.start() }
+ return coroutineScope.async("playCameraSound", bgDispatcher) {
+ try {
+ player.await()?.start()
+ } catch (e: IllegalStateException) {
+ Log.w(TAG, "Screenshot sound failed to play", e)
+ releaseScreenshotSound()
+ }
+ }
}
+
override fun releaseScreenshotSound(): Deferred<Unit> {
return coroutineScope.async("releaseScreenshotSound", bgDispatcher) {
try {
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
index 2f0fc5127009..ee602e5f1c04 100644
--- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
@@ -23,6 +23,7 @@ import android.content.DialogInterface.BUTTON_NEGATIVE
import android.content.DialogInterface.BUTTON_POSITIVE
import android.content.Intent
import android.content.Intent.EXTRA_PACKAGE_NAME
+import android.content.pm.PackageManager
import android.hardware.SensorPrivacyManager
import android.hardware.SensorPrivacyManager.EXTRA_ALL_SENSORS
import android.hardware.SensorPrivacyManager.EXTRA_SENSOR
@@ -31,6 +32,7 @@ import android.os.Bundle
import android.os.Handler
import android.window.OnBackInvokedDispatcher
import androidx.annotation.OpenForTesting
+import com.android.internal.camera.flags.Flags
import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION
import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__CANCEL
import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__ENABLE
@@ -90,14 +92,14 @@ open class SensorUseStartedActivity @Inject constructor(
sensor = ALL_SENSORS
val callback = IndividualSensorPrivacyController.Callback { _, _ ->
if (!sensorPrivacyController.isSensorBlocked(MICROPHONE) &&
- !sensorPrivacyController.isSensorBlocked(CAMERA)) {
+ !isCameraBlocked(sensorUsePackageName)) {
finish()
}
}
sensorPrivacyListener = callback
sensorPrivacyController.addCallback(callback)
if (!sensorPrivacyController.isSensorBlocked(MICROPHONE) &&
- !sensorPrivacyController.isSensorBlocked(CAMERA)) {
+ !isCameraBlocked(sensorUsePackageName)) {
finish()
return
}
@@ -110,14 +112,22 @@ open class SensorUseStartedActivity @Inject constructor(
}
val callback = IndividualSensorPrivacyController.Callback {
whichSensor: Int, isBlocked: Boolean ->
- if (whichSensor == sensor && !isBlocked) {
+ if (whichSensor != sensor) {
+ // Ignore a callback; we're not interested in.
+ } else if ((whichSensor == CAMERA) && !isCameraBlocked(sensorUsePackageName)) {
+ finish()
+ } else if ((whichSensor == MICROPHONE) && !isBlocked) {
finish()
}
}
sensorPrivacyListener = callback
sensorPrivacyController.addCallback(callback)
- if (!sensorPrivacyController.isSensorBlocked(sensor)) {
+ if ((sensor == CAMERA) && !isCameraBlocked(sensorUsePackageName)) {
+ finish()
+ return
+ } else if ((sensor == MICROPHONE) &&
+ !sensorPrivacyController.isSensorBlocked(MICROPHONE)) {
finish()
return
}
@@ -204,6 +214,22 @@ open class SensorUseStartedActivity @Inject constructor(
recreate()
}
+ private fun isAutomotive(): Boolean {
+ return getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ }
+
+ private fun isCameraBlocked(packageName: String): Boolean {
+ if (Flags.cameraPrivacyAllowlist()) {
+ if (isAutomotive()) {
+ return sensorPrivacyController.isCameraPrivacyEnabled(packageName)
+ } else {
+ return sensorPrivacyController.isSensorBlocked(CAMERA)
+ }
+ } else {
+ return sensorPrivacyController.isSensorBlocked(CAMERA)
+ }
+ }
+
private fun disableSensorPrivacy() {
if (sensor == ALL_SENSORS) {
sensorPrivacyController.setSensorBlocked(DIALOG, MICROPHONE, false)
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
index d6f1ed9c3334..1a997a764055 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
@@ -50,6 +50,9 @@ interface UserTracker : UserContentResolverProvider, UserContextProvider {
* List of profiles associated with the current user.
*
* Quiet work profiles will still appear here, but will have the `QUIET_MODE` flag.
+ *
+ * Disabled work profiles will also appear here. Listeners will be notified when profiles go
+ * from disabled to enabled (as UserInfo are immutable) with the updated list.
*/
val userProfiles: List<UserInfo>
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index f2fa0ef3f30f..0a1f649691a1 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -121,7 +121,6 @@ open class UserTrackerImpl internal constructor(
@GuardedBy("callbacks")
private val callbacks: MutableList<DataItem> = ArrayList()
- private var beforeUserSwitchingJob: Job? = null
private var userSwitchingJob: Job? = null
private var afterUserSwitchingJob: Job? = null
@@ -194,14 +193,7 @@ open class UserTrackerImpl internal constructor(
private fun registerUserSwitchObserver() {
iActivityManager.registerUserSwitchObserver(object : UserSwitchObserver() {
override fun onBeforeUserSwitching(newUserId: Int) {
- if (isBackgroundUserSwitchEnabled) {
- beforeUserSwitchingJob?.cancel()
- beforeUserSwitchingJob = appScope.launch(backgroundContext) {
- handleBeforeUserSwitching(newUserId)
- }
- } else {
- handleBeforeUserSwitching(newUserId)
- }
+ handleBeforeUserSwitching(newUserId)
}
override fun onUserSwitching(newUserId: Int, reply: IRemoteCallback?) {
@@ -233,15 +225,12 @@ open class UserTrackerImpl internal constructor(
@WorkerThread
protected open fun handleBeforeUserSwitching(newUserId: Int) {
- Assert.isNotMainThread()
setUserIdInternal(newUserId)
- val list = synchronized(callbacks) {
- callbacks.toList()
- }
- list.forEach {
- it.callback.get()?.onBeforeUserSwitching(newUserId)
- }
+ notifySubscribers { callback, resultCallback ->
+ callback.onBeforeUserSwitching(newUserId)
+ resultCallback.run()
+ }.await()
}
@WorkerThread
@@ -249,21 +238,9 @@ open class UserTrackerImpl internal constructor(
Assert.isNotMainThread()
Log.i(TAG, "Switching to user $newUserId")
- val list = synchronized(callbacks) {
- callbacks.toList()
- }
- val latch = CountDownLatch(list.size)
- list.forEach {
- val callback = it.callback.get()
- if (callback != null) {
- it.executor.execute {
- callback.onUserChanging(userId, userContext) { latch.countDown() }
- }
- } else {
- latch.countDown()
- }
- }
- latch.await()
+ notifySubscribers { callback, resultCallback ->
+ callback.onUserChanging(newUserId, userContext, resultCallback)
+ }.await()
}
@WorkerThread
@@ -293,9 +270,9 @@ open class UserTrackerImpl internal constructor(
Assert.isNotMainThread()
Log.i(TAG, "Switched to user $newUserId")
- notifySubscribers {
- onUserChanged(newUserId, userContext)
- onProfilesChanged(userProfiles)
+ notifySubscribers { callback, _ ->
+ callback.onUserChanged(newUserId, userContext)
+ callback.onProfilesChanged(userProfiles)
}
}
@@ -307,8 +284,8 @@ open class UserTrackerImpl internal constructor(
synchronized(mutex) {
userProfiles = profiles.map { UserInfo(it) } // save a "deep" copy
}
- notifySubscribers {
- onProfilesChanged(profiles)
+ notifySubscribers { callback, _ ->
+ callback.onProfilesChanged(profiles)
}
}
@@ -324,18 +301,24 @@ open class UserTrackerImpl internal constructor(
}
}
- private inline fun notifySubscribers(crossinline action: UserTracker.Callback.() -> Unit) {
+ private inline fun notifySubscribers(
+ crossinline action: (UserTracker.Callback, resultCallback: Runnable) -> Unit
+ ): CountDownLatch {
val list = synchronized(callbacks) {
callbacks.toList()
}
-
+ val latch = CountDownLatch(list.size)
list.forEach {
- if (it.callback.get() != null) {
+ val callback = it.callback.get()
+ if (callback != null) {
it.executor.execute {
- it.callback.get()?.action()
+ action(callback) { latch.countDown() }
}
+ } else {
+ latch.countDown()
}
}
+ return latch
}
override fun dump(pw: PrintWriter, args: Array<out String>) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 0e359b7f0eec..7068f5fcc32b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -71,7 +71,6 @@ import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.MathUtils;
import android.view.HapticFeedbackConstants;
-import android.view.InputDevice;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.VelocityTracker;
@@ -123,6 +122,7 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.dump.DumpsysTableLogger;
@@ -137,7 +137,6 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.domain.interactor.NaturalScrollingSettingObserver;
import com.android.systemui.keyguard.shared.ComposeLockscreen;
-import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl;
import com.android.systemui.keyguard.shared.model.TransitionState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.keyguard.ui.binder.KeyguardLongPressViewBinder;
@@ -150,9 +149,9 @@ import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransition
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
-import com.android.systemui.media.controls.pipeline.MediaDataManager;
-import com.android.systemui.media.controls.ui.KeyguardMediaController;
-import com.android.systemui.media.controls.ui.MediaHierarchyManager;
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.ui.controller.KeyguardMediaController;
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarView;
@@ -166,6 +165,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController.StateList
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.power.shared.model.WakefulnessModel;
import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.shade.data.repository.FlingInfo;
import com.android.systemui.shade.data.repository.ShadeRepository;
import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor;
@@ -908,7 +908,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
mKeyguardBypassController = bypassController;
mUpdateMonitor = keyguardUpdateMonitor;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
- lockscreenShadeTransitionController.setShadeViewController(this);
shadeTransitionController.setShadeViewController(this);
dynamicPrivacyController.addListener(this::onDynamicPrivacyChanged);
quickSettingsController.setExpansionHeightListener(this::onQsSetExpansionHeightCalled);
@@ -1022,7 +1021,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
instantCollapse();
} else {
mView.animate().cancel();
- if (!KeyguardShadeMigrationNssl.isEnabled()) {
+ if (!migrateClocksToBlueprint()) {
mView.animate()
.alpha(0f)
.setStartDelay(0)
@@ -1158,7 +1157,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
// Occluded->Lockscreen
collectFlow(mView, mKeyguardTransitionInteractor.getOccludedToLockscreenTransition(),
mOccludedToLockscreenTransition, mMainDispatcher);
- if (!KeyguardShadeMigrationNssl.isEnabled()) {
+ if (!migrateClocksToBlueprint()) {
collectFlow(mView, mOccludedToLockscreenTransitionViewModel.getLockscreenAlpha(),
setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher);
collectFlow(mView,
@@ -1169,7 +1168,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
// Lockscreen->Dreaming
collectFlow(mView, mKeyguardTransitionInteractor.getLockscreenToDreamingTransition(),
mLockscreenToDreamingTransition, mMainDispatcher);
- if (!KeyguardShadeMigrationNssl.isEnabled()) {
+ if (!migrateClocksToBlueprint()) {
collectFlow(mView, mLockscreenToDreamingTransitionViewModel.getLockscreenAlpha(),
setDreamLockscreenTransitionAlpha(mNotificationStackScrollLayoutController),
mMainDispatcher);
@@ -1181,7 +1180,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
// Gone->Dreaming
collectFlow(mView, mKeyguardTransitionInteractor.getGoneToDreamingTransition(),
mGoneToDreamingTransition, mMainDispatcher);
- if (!KeyguardShadeMigrationNssl.isEnabled()) {
+ if (!migrateClocksToBlueprint()) {
collectFlow(mView, mGoneToDreamingTransitionViewModel.getLockscreenAlpha(),
setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher);
}
@@ -1192,7 +1191,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
// Lockscreen->Occluded
collectFlow(mView, mKeyguardTransitionInteractor.getLockscreenToOccludedTransition(),
mLockscreenToOccludedTransition, mMainDispatcher);
- if (!KeyguardShadeMigrationNssl.isEnabled()) {
+ if (!migrateClocksToBlueprint()) {
collectFlow(mView, mLockscreenToOccludedTransitionViewModel.getLockscreenAlpha(),
setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher);
collectFlow(mView, mLockscreenToOccludedTransitionViewModel.getLockscreenTranslationY(),
@@ -1200,7 +1199,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
}
// Primary bouncer->Gone (ensures lockscreen content is not visible on successful auth)
- if (!KeyguardShadeMigrationNssl.isEnabled()) {
+ if (!migrateClocksToBlueprint()) {
collectFlow(mView, mPrimaryBouncerToGoneTransitionViewModel.getLockscreenAlpha(),
setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher);
}
@@ -1278,7 +1277,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
mKeyguardStatusViewController.onDestroy();
}
- if (KeyguardShadeMigrationNssl.isEnabled()) {
+ if (migrateClocksToBlueprint()) {
// Need a shared controller until mKeyguardStatusViewController can be removed from
// here, due to important state being set in that controller. Rebind in order to pick
// up config changes
@@ -1334,7 +1333,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
// Reset any left over overscroll state. It is a rare corner case but can happen.
mQsController.setOverScrollAmount(0);
mScrimController.setNotificationsOverScrollAmount(0);
- if (!KeyguardShadeMigrationNssl.isEnabled()) {
+ if (!migrateClocksToBlueprint()) {
mNotificationStackScrollLayoutController.setOverExpansion(0);
mNotificationStackScrollLayoutController.setOverScrollAmount(0);
}
@@ -1355,7 +1354,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
}
updateClockAppearance();
mQsController.updateQsState();
- if (!KeyguardShadeMigrationNssl.isEnabled() && !FooterViewRefactor.isEnabled()) {
+ if (!migrateClocksToBlueprint() && !FooterViewRefactor.isEnabled()) {
mNotificationStackScrollLayoutController.updateFooter();
}
}
@@ -1387,7 +1386,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
void reInflateViews() {
debugLog("reInflateViews");
// Re-inflate the status view group.
- if (!KeyguardShadeMigrationNssl.isEnabled()) {
+ if (!migrateClocksToBlueprint()) {
KeyguardStatusView keyguardStatusView =
mNotificationContainerParent.findViewById(R.id.keyguard_status_view);
int statusIndex = mNotificationContainerParent.indexOfChild(keyguardStatusView);
@@ -1507,7 +1506,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
}
private void updateMaxDisplayedNotifications(boolean recompute) {
- if (KeyguardShadeMigrationNssl.isEnabled()) {
+ if (migrateClocksToBlueprint()) {
return;
}
@@ -1664,7 +1663,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
mKeyguardStatusViewController.getClockBottom(mStatusBarHeaderHeightKeyguard),
mKeyguardStatusViewController.isClockTopAligned());
mClockPositionAlgorithm.run(mClockPositionResult);
- if (!KeyguardShadeMigrationNssl.isEnabled()) {
+ if (!migrateClocksToBlueprint()) {
mKeyguardStatusViewController.setLockscreenClockY(
mClockPositionAlgorithm.getExpandedPreferredClockY());
}
@@ -1678,7 +1677,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
boolean animateClock = (animate || mAnimateNextPositionUpdate) && shouldAnimateClockChange;
- if (!KeyguardShadeMigrationNssl.isEnabled()) {
+ if (!migrateClocksToBlueprint()) {
mKeyguardStatusViewController.updatePosition(
mClockPositionResult.clockX, mClockPositionResult.clockY,
mClockPositionResult.clockScale, animateClock);
@@ -1754,7 +1753,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
private void updateKeyguardStatusViewAlignment(boolean animate) {
boolean shouldBeCentered = shouldKeyguardStatusViewBeCentered();
ConstraintLayout layout;
- if (KeyguardShadeMigrationNssl.isEnabled()) {
+ if (migrateClocksToBlueprint()) {
layout = mKeyguardViewConfigurator.getKeyguardRootView();
} else {
layout = mNotificationContainerParent;
@@ -1828,7 +1827,14 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
/** Returns space between top of lock icon and bottom of NotificationStackScrollLayout. */
private float getLockIconPadding() {
float lockIconPadding = 0f;
- if (mLockIconViewController.getTop() != 0f) {
+ if (DeviceEntryUdfpsRefactor.isEnabled()) {
+ View deviceEntryIconView = mKeyguardViewConfigurator.getKeyguardRootView()
+ .findViewById(R.id.device_entry_icon_view);
+ if (deviceEntryIconView != null) {
+ lockIconPadding = mNotificationStackScrollLayoutController.getBottom()
+ - deviceEntryIconView.getTop();
+ }
+ } else if (mLockIconViewController.getTop() != 0f) {
lockIconPadding = mNotificationStackScrollLayoutController.getBottom()
- mLockIconViewController.getTop();
}
@@ -1930,7 +1936,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
}
float alpha = mClockPositionResult.clockAlpha * mKeyguardOnlyContentAlpha;
mKeyguardStatusViewController.setAlpha(alpha);
- if (KeyguardShadeMigrationNssl.isEnabled()) {
+ if (migrateClocksToBlueprint()) {
// TODO (b/296373478) This is for split shade media movement.
} else {
mKeyguardStatusViewController
@@ -2523,7 +2529,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
void requestScrollerTopPaddingUpdate(boolean animate) {
float padding = mQsController.calculateNotificationsTopPadding(mIsExpandingOrCollapsing,
getKeyguardNotificationStaticPadding(), mExpandedFraction);
- if (KeyguardShadeMigrationNssl.isEnabled()) {
+ if (migrateClocksToBlueprint()) {
mSharedNotificationContainerInteractor.setTopPosition(padding);
} else {
mNotificationStackScrollLayoutController.updateTopPadding(padding, animate);
@@ -2705,7 +2711,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
return;
}
- if (!KeyguardShadeMigrationNssl.isEnabled()) {
+ if (!migrateClocksToBlueprint()) {
float alpha = 1f;
if (mClosingWithAlphaFadeOut && !mExpandingFromHeadsUp
&& !mHeadsUpManager.hasPinnedHeadsUp()) {
@@ -2740,7 +2746,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
}
private void updateKeyguardBottomAreaAlpha() {
- if (KeyguardShadeMigrationNssl.isEnabled()) {
+ if (migrateClocksToBlueprint()) {
return;
}
if (mIsOcclusionTransitionRunning) {
@@ -2981,7 +2987,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
@Override
public void onScreenTurningOn() {
- if (!KeyguardShadeMigrationNssl.isEnabled()) {
+ if (!migrateClocksToBlueprint()) {
mKeyguardStatusViewController.dozeTimeTick();
}
}
@@ -3233,7 +3239,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
public void dozeTimeTick() {
mLockIconViewController.dozeTimeTick();
- if (!KeyguardShadeMigrationNssl.isEnabled()) {
+ if (!migrateClocksToBlueprint()) {
mKeyguardStatusViewController.dozeTimeTick();
}
if (mInterpolatedDarkAmount > 0) {
@@ -4158,8 +4164,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
mFixedDuration = NO_FIXED_DURATION;
}
- @Override
- public boolean postToView(Runnable action) {
+ boolean postToView(Runnable action) {
return mView.post(action);
}
@@ -4449,7 +4454,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
&& statusBarState == KEYGUARD) {
// This means we're doing the screen off animation - position the keyguard status
// view where it'll be on AOD, so we can animate it in.
- if (!KeyguardShadeMigrationNssl.isEnabled()) {
+ if (!migrateClocksToBlueprint()) {
mKeyguardStatusViewController.updatePosition(
mClockPositionResult.clockX,
mClockPositionResult.clockYFullyDozing,
@@ -4569,7 +4574,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
setDozing(true /* dozing */, false /* animate */);
mStatusBarStateController.setUpcomingState(KEYGUARD);
- if (KeyguardShadeMigrationNssl.isEnabled()) {
+ if (migrateClocksToBlueprint()) {
mStatusBarStateController.setState(KEYGUARD);
} else {
mStatusBarStateListener.onStateChanged(KEYGUARD);
@@ -4591,8 +4596,10 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
public void onViewAttachedToWindow(View v) {
mFragmentService.getFragmentHostManager(mView)
.addTagListener(QS.TAG, mQsController.getQsFragmentListener());
- mStatusBarStateController.addCallback(mStatusBarStateListener);
- mStatusBarStateListener.onStateChanged(mStatusBarStateController.getState());
+ if (!SceneContainerFlag.isEnabled()) {
+ mStatusBarStateController.addCallback(mStatusBarStateListener);
+ mStatusBarStateListener.onStateChanged(mStatusBarStateController.getState());
+ }
mConfigurationController.addCallback(mConfigurationListener);
// Theme might have changed between inflating this view and attaching it to the
// window, so
@@ -4630,7 +4637,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
setIsFullWidth(mNotificationStackScrollLayoutController.getWidth() == mView.getWidth());
// Update Clock Pivot (used by anti-burnin transformations)
- if (!KeyguardShadeMigrationNssl.isEnabled()) {
+ if (!migrateClocksToBlueprint()) {
mKeyguardStatusViewController.updatePivot(mView.getWidth(), mView.getHeight());
}
@@ -4740,7 +4747,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
private Consumer<Float> setTransitionY(
NotificationStackScrollLayoutController stackScroller) {
return (Float translationY) -> {
- if (!KeyguardShadeMigrationNssl.isEnabled()) {
+ if (!migrateClocksToBlueprint()) {
mKeyguardStatusViewController.setTranslationY(translationY,
/* excludeMedia= */false);
stackScroller.setTranslationY(translationY);
@@ -4782,7 +4789,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
- if (KeyguardShadeMigrationNssl.isEnabled() && !mUseExternalTouch) {
+ if (migrateClocksToBlueprint() && !mUseExternalTouch) {
return false;
}
@@ -4853,7 +4860,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
- if (!KeyguardShadeMigrationNssl.isEnabled()) {
+ if (!migrateClocksToBlueprint()) {
mCentralSurfaces.userActivity();
}
mAnimatingOnDown = mHeightAnimator != null && !mIsSpringBackAnimation;
@@ -4954,7 +4961,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
- if (KeyguardShadeMigrationNssl.isEnabled() && !mUseExternalTouch) {
+ if (migrateClocksToBlueprint() && !mUseExternalTouch) {
return false;
}
@@ -5066,19 +5073,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
return false;
}
- final boolean isTrackpadTwoOrThreeFingerSwipe = isTrackpadScroll(
- mTrackpadGestureFeaturesEnabled, event) || isTrackpadThreeFingerSwipe(
- mTrackpadGestureFeaturesEnabled, event);
-
- // On expanding, single mouse click expands the panel instead of dragging.
- if (isFullyCollapsed() && (event.isFromSource(InputDevice.SOURCE_MOUSE)
- && !isTrackpadTwoOrThreeFingerSwipe)) {
- if (event.getAction() == MotionEvent.ACTION_UP) {
- expand(true /* animate */);
- }
- return true;
- }
-
/*
* We capture touch events here and update the expand height here in case according to
* the users fingers. This also handles multi-touch.
@@ -5099,6 +5093,10 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
mIgnoreXTouchSlop = true;
}
+ final boolean isTrackpadTwoOrThreeFingerSwipe = isTrackpadScroll(
+ mTrackpadGestureFeaturesEnabled, event) || isTrackpadThreeFingerSwipe(
+ mTrackpadGestureFeaturesEnabled, event);
+
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
if (QuickStepContract.ALLOW_BACK_GESTURE_IN_SHADE && mAnimateBack) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index f7fed537a167..8f9cef37dac7 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -60,6 +60,7 @@ import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.scene.shared.flag.SceneContainerFlags;
import com.android.systemui.scene.ui.view.WindowRootViewComponent;
import com.android.systemui.settings.UserTracker;
@@ -506,7 +507,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
private void applyFitsSystemWindows(NotificationShadeWindowState state) {
boolean fitsSystemWindows = !state.isKeyguardShowingAndNotOccluded();
- if (mWindowRootView != null
+ if (!SceneContainerFlag.isEnabled() && mWindowRootView != null
&& mWindowRootView.getFitsSystemWindows() != fitsSystemWindows) {
mWindowRootView.setFitsSystemWindows(fitsSystemWindows);
mWindowRootView.requestApplyInsets();
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index aa2d606c5126..e5771785409f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -16,6 +16,7 @@
package com.android.systemui.shade;
+import static com.android.systemui.Flags.migrateClocksToBlueprint;
import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
import static com.android.systemui.flags.Flags.TRACKPAD_GESTURE_COMMON;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
@@ -48,7 +49,6 @@ import com.android.systemui.flags.Flags;
import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
-import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl;
import com.android.systemui.keyguard.shared.model.TransitionState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.keyguard.ui.binder.AlternateBouncerViewBinder;
@@ -320,7 +320,7 @@ public class NotificationShadeWindowViewController implements Dumpable {
mTouchActive = true;
mTouchCancelled = false;
mDownEvent = ev;
- if (KeyguardShadeMigrationNssl.isEnabled()) {
+ if (migrateClocksToBlueprint()) {
mService.userActivity();
}
} else if (ev.getActionMasked() == MotionEvent.ACTION_UP
@@ -475,7 +475,7 @@ public class NotificationShadeWindowViewController implements Dumpable {
&& !bouncerShowing
&& !mStatusBarStateController.isDozing()) {
if (mDragDownHelper.isDragDownEnabled()) {
- if (KeyguardShadeMigrationNssl.isEnabled()) {
+ if (migrateClocksToBlueprint()) {
// When on lockscreen, if the touch originates at the top of the screen
// go directly to QS and not the shade
if (mStatusBarStateController.getState() == KEYGUARD
@@ -488,7 +488,7 @@ public class NotificationShadeWindowViewController implements Dumpable {
// This handles drag down over lockscreen
boolean result = mDragDownHelper.onInterceptTouchEvent(ev);
- if (KeyguardShadeMigrationNssl.isEnabled()) {
+ if (migrateClocksToBlueprint()) {
if (result) {
mLastInterceptWasDragDownHelper = true;
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
@@ -511,6 +511,12 @@ public class NotificationShadeWindowViewController implements Dumpable {
return true;
}
}
+ } else if (migrateClocksToBlueprint()) {
+ // This final check handles swipes on HUNs and when Pulsing
+ if (!bouncerShowing && didNotificationPanelInterceptEvent(ev)) {
+ mShadeLogger.d("NSWVC: intercepted for HUN/PULSING");
+ return true;
+ }
}
return false;
}
@@ -520,7 +526,7 @@ public class NotificationShadeWindowViewController implements Dumpable {
MotionEvent cancellation = MotionEvent.obtain(ev);
cancellation.setAction(MotionEvent.ACTION_CANCEL);
mStackScrollLayout.onInterceptTouchEvent(cancellation);
- if (!KeyguardShadeMigrationNssl.isEnabled()) {
+ if (!migrateClocksToBlueprint()) {
mNotificationPanelViewController.handleExternalInterceptTouch(cancellation);
}
cancellation.recycle();
@@ -535,7 +541,7 @@ public class NotificationShadeWindowViewController implements Dumpable {
if (mStatusBarKeyguardViewManager.onTouch(ev)) {
return true;
}
- if (KeyguardShadeMigrationNssl.isEnabled()) {
+ if (migrateClocksToBlueprint()) {
if (mLastInterceptWasDragDownHelper && (mDragDownHelper.isDraggingDown())) {
// we still want to finish our drag down gesture when locking the screen
handled |= mDragDownHelper.onTouchEvent(ev) || handled;
@@ -625,7 +631,7 @@ public class NotificationShadeWindowViewController implements Dumpable {
}
private boolean didNotificationPanelInterceptEvent(MotionEvent ev) {
- if (KeyguardShadeMigrationNssl.isEnabled()) {
+ if (migrateClocksToBlueprint()) {
// 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/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
index c0afa32571e7..29de688fa7bf 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
@@ -27,11 +27,11 @@ import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
import androidx.lifecycle.lifecycleScope
-import com.android.systemui.Flags.centralizedStatusBarDimensRefactor
+import com.android.systemui.Flags.centralizedStatusBarHeightFix
+import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.fragments.FragmentService
-import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.navigationbar.NavigationModeController
import com.android.systemui.plugins.qs.QS
@@ -183,7 +183,7 @@ class NotificationsQSContainerController @Inject constructor(
}
private fun calculateLargeShadeHeaderHeight(): Int {
- return if (centralizedStatusBarDimensRefactor()) {
+ return if (centralizedStatusBarHeightFix()) {
largeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight()
} else {
resources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height)
@@ -284,7 +284,7 @@ class NotificationsQSContainerController @Inject constructor(
}
private fun setNotificationsConstraints(constraintSet: ConstraintSet) {
- if (KeyguardShadeMigrationNssl.isEnabled) {
+ if (migrateClocksToBlueprint()) {
return
}
val startConstraintId = if (splitShadeEnabled) R.id.qs_edge_guideline else PARENT_ID
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
index 25e558ee42dd..e82f2d3cbd30 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
@@ -18,6 +18,8 @@ package com.android.systemui.shade;
import static androidx.constraintlayout.core.widgets.Optimizer.OPTIMIZATION_GRAPH;
+import static com.android.systemui.Flags.migrateClocksToBlueprint;
+
import android.app.Fragment;
import android.content.Context;
import android.content.res.Configuration;
@@ -33,7 +35,6 @@ import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
-import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.AboveShelfObserver;
@@ -189,7 +190,7 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
- if (KeyguardShadeMigrationNssl.isEnabled()) {
+ if (migrateClocksToBlueprint()) {
return super.drawChild(canvas, child, drawingTime);
}
int layoutIndex = mLayoutDrawingOrder.indexOf(child);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index f3e9c7503626..e7f970050033 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -20,7 +20,8 @@ package com.android.systemui.shade;
import static android.view.WindowInsets.Type.ime;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
-import static com.android.systemui.Flags.centralizedStatusBarDimensRefactor;
+import static com.android.systemui.Flags.centralizedStatusBarHeightFix;
+import static com.android.systemui.Flags.migrateClocksToBlueprint;
import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
import static com.android.systemui.shade.NotificationPanelViewController.COUNTER_PANEL_OPEN_QS;
import static com.android.systemui.shade.NotificationPanelViewController.FLING_COLLAPSE;
@@ -70,9 +71,8 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.fragments.FragmentHostManager;
-import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl;
-import com.android.systemui.media.controls.pipeline.MediaDataManager;
-import com.android.systemui.media.controls.ui.MediaHierarchyManager;
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.res.R;
@@ -453,7 +453,7 @@ public class QuickSettingsController implements Dumpable {
mUseLargeScreenShadeHeader =
LargeScreenUtils.shouldUseLargeScreenShadeHeader(mPanelView.getResources());
mLargeScreenShadeHeaderHeight =
- centralizedStatusBarDimensRefactor()
+ centralizedStatusBarHeightFix()
? mLargeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight()
: mResources.getDimensionPixelSize(
R.dimen.large_screen_shade_header_height);
@@ -1782,7 +1782,7 @@ public class QuickSettingsController implements Dumpable {
// Dragging down on the lockscreen statusbar should prohibit other interactions
// immediately, otherwise we'll wait on the touchslop. This is to allow
// dragging down to expanded quick settings directly on the lockscreen.
- if (!KeyguardShadeMigrationNssl.isEnabled()) {
+ if (!migrateClocksToBlueprint()) {
mPanelView.getParent().requestDisallowInterceptTouchEvent(true);
}
}
@@ -1827,7 +1827,7 @@ public class QuickSettingsController implements Dumpable {
&& Math.abs(h) > Math.abs(x - mInitialTouchX)
&& shouldQuickSettingsIntercept(
mInitialTouchX, mInitialTouchY, h)) {
- if (!KeyguardShadeMigrationNssl.isEnabled()) {
+ if (!migrateClocksToBlueprint()) {
mPanelView.getParent().requestDisallowInterceptTouchEvent(true);
}
mShadeLog.onQsInterceptMoveQsTrackingEnabled(h);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
index e8d9c35a893b..ea419127d7c1 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
@@ -66,7 +66,7 @@ public final class ShadeControllerImpl extends BaseShadeControllerImpl {
private final StatusBarWindowController mStatusBarWindowController;
private final DeviceProvisionedController mDeviceProvisionedController;
- private final Lazy<ShadeViewController> mShadeViewControllerLazy;
+ private final Lazy<NotificationPanelViewController> mNpvc;
private final Lazy<AssistManager> mAssistManagerLazy;
private final Lazy<NotificationGutsManager> mGutsManager;
@@ -89,7 +89,7 @@ public final class ShadeControllerImpl extends BaseShadeControllerImpl {
DeviceProvisionedController deviceProvisionedController,
NotificationShadeWindowController notificationShadeWindowController,
WindowManager windowManager,
- Lazy<ShadeViewController> shadeViewControllerLazy,
+ Lazy<NotificationPanelViewController> shadeViewControllerLazy,
Lazy<AssistManager> assistManagerLazy,
Lazy<NotificationGutsManager> gutsManager
) {
@@ -101,7 +101,7 @@ public final class ShadeControllerImpl extends BaseShadeControllerImpl {
mCommandQueue = commandQueue;
mMainExecutor = mainExecutor;
mWindowRootViewVisibilityInteractor = windowRootViewVisibilityInteractor;
- mShadeViewControllerLazy = shadeViewControllerLazy;
+ mNpvc = shadeViewControllerLazy;
mStatusBarStateController = statusBarStateController;
mStatusBarWindowController = statusBarWindowController;
mDeviceProvisionedController = deviceProvisionedController;
@@ -122,7 +122,7 @@ public final class ShadeControllerImpl extends BaseShadeControllerImpl {
public void instantExpandShade() {
// Make our window larger and the panel expanded.
makeExpandedVisible(true /* force */);
- getShadeViewController().expand(false /* animate */);
+ getNpvc().expand(false /* animate */);
getCommandQueue().recomputeDisableFlags(mDisplayId, false /* animate */);
}
@@ -134,29 +134,29 @@ public final class ShadeControllerImpl extends BaseShadeControllerImpl {
return;
}
if (getNotificationShadeWindowView() != null
- && getShadeViewController().canBeCollapsed()
+ && getNpvc().canBeCollapsed()
&& (flags & CommandQueue.FLAG_EXCLUDE_NOTIFICATION_PANEL) == 0) {
// release focus immediately to kick off focus change transition
mNotificationShadeWindowController.setNotificationShadeFocusable(false);
mNotificationShadeWindowViewController.cancelExpandHelper();
- getShadeViewController().collapse(true, delayed, speedUpFactor);
+ getNpvc().collapse(true, delayed, speedUpFactor);
}
}
@Override
protected void expandToNotifications() {
- getShadeViewController().expandToNotifications();
+ getNpvc().expandToNotifications();
}
@Override
protected void expandToQs() {
- getShadeViewController().expandToQs();
+ getNpvc().expandToQs();
}
@Override
public boolean closeShadeIfOpen() {
- if (!getShadeViewController().isFullyCollapsed()) {
+ if (!getNpvc().isFullyCollapsed()) {
getCommandQueue().animateCollapsePanels(
CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
notifyVisibilityChanged(false);
@@ -167,12 +167,12 @@ public final class ShadeControllerImpl extends BaseShadeControllerImpl {
@Override
public boolean isShadeFullyOpen() {
- return getShadeViewController().isShadeFullyExpanded();
+ return getNpvc().isShadeFullyExpanded();
}
@Override
public boolean isExpandingOrCollapsing() {
- return getShadeViewController().isExpandingOrCollapsing();
+ return getNpvc().isExpandingOrCollapsing();
}
@Override
public void postAnimateCollapseShade() {
@@ -191,13 +191,13 @@ public final class ShadeControllerImpl extends BaseShadeControllerImpl {
@Override
public void postOnShadeExpanded(Runnable executable) {
- getShadeViewController().addOnGlobalLayoutListener(
+ getNpvc().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (getNotificationShadeWindowView().isVisibleToUser()) {
- getShadeViewController().removeOnGlobalLayoutListener(this);
- getShadeViewController().postToView(executable);
+ getNpvc().removeOnGlobalLayoutListener(this);
+ getNpvc().postToView(executable);
}
}
});
@@ -209,7 +209,7 @@ public final class ShadeControllerImpl extends BaseShadeControllerImpl {
}
private boolean collapseShadeInternal() {
- if (!getShadeViewController().isFullyCollapsed()) {
+ if (!getNpvc().isFullyCollapsed()) {
// close the shade if it was open
animateCollapseShadeForcedDelayed();
notifyVisibilityChanged(false);
@@ -237,10 +237,10 @@ public final class ShadeControllerImpl extends BaseShadeControllerImpl {
@Override
public void cancelExpansionAndCollapseShade() {
- if (getShadeViewController().isTracking()) {
+ if (getNpvc().isTracking()) {
mNotificationShadeWindowViewController.cancelCurrentTouch();
}
- if (getShadeViewController().isPanelExpanded()
+ if (getNpvc().isPanelExpanded()
&& mStatusBarStateController.getState() == StatusBarState.SHADE) {
animateCollapseShade();
}
@@ -266,7 +266,7 @@ public final class ShadeControllerImpl extends BaseShadeControllerImpl {
@Override
public void instantCollapseShade() {
- getShadeViewController().instantCollapse();
+ getNpvc().instantCollapse();
runPostCollapseActions();
}
@@ -297,7 +297,7 @@ public final class ShadeControllerImpl extends BaseShadeControllerImpl {
}
// Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868)
- getShadeViewController().collapse(false, false, 1.0f);
+ getNpvc().collapse(false, false, 1.0f);
mExpandedVisible = false;
notifyVisibilityChanged(false);
@@ -319,7 +319,7 @@ public final class ShadeControllerImpl extends BaseShadeControllerImpl {
notifyExpandedVisibleChanged(false);
getCommandQueue().recomputeDisableFlags(
mDisplayId,
- getShadeViewController().shouldHideStatusBarIconsWhenExpanded());
+ getNpvc().shouldHideStatusBarIconsWhenExpanded());
// Trimming will happen later if Keyguard is showing - doing it here might cause a jank in
// the bouncer appear animation.
@@ -368,15 +368,15 @@ public final class ShadeControllerImpl extends BaseShadeControllerImpl {
return mNotificationShadeWindowViewController.getView();
}
- private ShadeViewController getShadeViewController() {
- return mShadeViewControllerLazy.get();
+ private NotificationPanelViewController getNpvc() {
+ return mNpvc.get();
}
@Override
public void start() {
super.start();
- getShadeViewController().setTrackingStartedListener(this::runPostCollapseActions);
- getShadeViewController().setOpenCloseListener(
+ getNpvc().setTrackingStartedListener(this::runPostCollapseActions);
+ getNpvc().setOpenCloseListener(
new OpenCloseListener() {
@Override
public void onClosingFinished() {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
index 7cb3be7f159d..6a2a6a417f5a 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
@@ -241,7 +241,7 @@ constructor(
}
override fun onStatusBarTouch(event: MotionEvent) {
- // The only call to this doesn't happen with KeyguardShadeMigrationNssl enabled
+ // The only call to this doesn't happen with migrateClocksToBlueprint() enabled
throw UnsupportedOperationException()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt
index f89a9c701047..4054a86960d3 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt
@@ -17,6 +17,8 @@
package com.android.systemui.shade
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.shade.data.repository.PrivacyChipRepository
+import com.android.systemui.shade.data.repository.PrivacyChipRepositoryImpl
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.shade.data.repository.ShadeRepositoryImpl
import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor
@@ -40,6 +42,12 @@ abstract class ShadeEmptyImplModule {
@Binds
@SysUISingleton
+ abstract fun bindsShadeLockscreenInteractor(
+ slsi: ShadeViewControllerEmptyImpl
+ ): ShadeLockscreenInteractor
+
+ @Binds
+ @SysUISingleton
abstract fun bindsShadeController(sc: ShadeControllerEmptyImpl): ShadeController
@Binds
@@ -55,4 +63,8 @@ abstract class ShadeEmptyImplModule {
abstract fun bindsShadeAnimationInteractor(
sai: ShadeAnimationInteractorEmptyImpl
): ShadeAnimationInteractor
+
+ @Binds
+ @SysUISingleton
+ abstract fun bindsPrivacyChipRepository(impl: PrivacyChipRepositoryImpl): PrivacyChipRepository
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
index a66bacd237be..df9c57c13732 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
@@ -38,7 +38,7 @@ import androidx.core.view.doOnLayout
import com.android.app.animation.Interpolators
import com.android.settingslib.Utils
import com.android.systemui.Dumpable
-import com.android.systemui.Flags.centralizedStatusBarDimensRefactor
+import com.android.systemui.Flags.centralizedStatusBarHeightFix
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
@@ -433,7 +433,7 @@ constructor(
changes += combinedShadeHeadersConstraintManager.emptyCutoutConstraints()
}
- if (centralizedStatusBarDimensRefactor()) {
+ if (centralizedStatusBarHeightFix()) {
view.setPadding(view.paddingLeft, sbInsets.top, view.paddingRight, view.paddingBottom)
}
view.updateAllConstraints(changes)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeLockscreenInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeLockscreenInteractor.kt
new file mode 100644
index 000000000000..a9ba6f96b7d2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeLockscreenInteractor.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2024 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.shade
+
+/** Allows the lockscreen to control the shade. */
+interface ShadeLockscreenInteractor {
+
+ /**
+ * Expand shade so that notifications are visible. Non-split shade: just expanding shade or
+ * collapsing QS when they're expanded. Split shade: only expanding shade, notifications are
+ * always visible
+ *
+ * Called when `adb shell cmd statusbar expand-notifications` is executed.
+ */
+ @Deprecated("Use ShadeInteractor instead") fun expandToNotifications()
+
+ /** Returns whether the shade is expanding or collapsing itself or quick settings. */
+ val isExpandingOrCollapsing: Boolean
+
+ /**
+ * Returns whether the shade height is greater than zero (i.e. partially or fully expanded),
+ * there is a HUN, the shade is animating, or the shade is instantly expanding.
+ */
+ @Deprecated("Use ShadeInteractor instead") val isExpanded: Boolean
+
+ /** Called before animating Keyguard dismissal, i.e. the animation dismissing the bouncer. */
+ fun startBouncerPreHideAnimation()
+
+ /** Called once every minute while dozing. */
+ fun dozeTimeTick()
+
+ /**
+ * Do not let the user drag the shade up and down for the current touch session. This is
+ * necessary to avoid shade expansion while/after the bouncer is dismissed.
+ */
+ @Deprecated("Not supported by scenes") fun blockExpansionForCurrentTouch()
+
+ /** Close guts, notification menus, and QS. Set scroll and overscroll to 0. */
+ fun resetViews(animate: Boolean)
+
+ /** Sets whether the screen has temporarily woken up to display notifications. */
+ @Deprecated("Not supported by scenes") fun setPulsing(pulsing: Boolean)
+
+ /** Animate to expanded shade after a delay in ms. Used for lockscreen to shade transition. */
+ fun transitionToExpandedShade(delay: Long)
+
+ /** @see ViewGroupFadeHelper.reset */
+ @Deprecated("Not supported by scenes") fun resetViewGroupFade()
+
+ /**
+ * Set the alpha and translationY of the keyguard elements which only show on the lockscreen,
+ * but not in shade locked / shade. This is used when dragging down to the full shade.
+ */
+ @Deprecated("Not supported by scenes")
+ fun setKeyguardTransitionProgress(keyguardAlpha: Float, keyguardTranslationY: Int)
+
+ /** Sets the overstretch amount in raw pixels when dragging down. */
+ @Deprecated("Not supported by scenes") fun setOverStretchAmount(amount: Float)
+
+ /**
+ * Sets the alpha value to be set on the keyguard status bar.
+ *
+ * @param alpha value between 0 and 1. -1 if the value is to be reset.
+ */
+ @Deprecated("TODO(b/325072511) delete this") fun setKeyguardStatusBarAlpha(alpha: Float)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
index e4d5d22c602c..5632766f2633 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
@@ -18,6 +18,8 @@ package com.android.systemui.shade
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.shade.data.repository.PrivacyChipRepository
+import com.android.systemui.shade.data.repository.PrivacyChipRepositoryImpl
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.shade.data.repository.ShadeRepositoryImpl
import com.android.systemui.shade.domain.interactor.BaseShadeInteractor
@@ -30,6 +32,7 @@ import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.domain.interactor.ShadeInteractorImpl
import com.android.systemui.shade.domain.interactor.ShadeInteractorLegacyImpl
import com.android.systemui.shade.domain.interactor.ShadeInteractorSceneContainerImpl
+import com.android.systemui.shade.domain.interactor.ShadeLockscreenInteractorImpl
import dagger.Binds
import dagger.Module
import dagger.Provides
@@ -94,6 +97,20 @@ abstract class ShadeModule {
sceneContainerOff.get()
}
}
+
+ @Provides
+ @SysUISingleton
+ fun provideShadeLockscreenInteractor(
+ sceneContainerFlags: SceneContainerFlags,
+ sceneContainerOn: Provider<ShadeLockscreenInteractorImpl>,
+ sceneContainerOff: Provider<NotificationPanelViewController>
+ ): ShadeLockscreenInteractor {
+ return if (sceneContainerFlags.isEnabled()) {
+ sceneContainerOn.get()
+ } else {
+ sceneContainerOff.get()
+ }
+ }
}
@Binds
@@ -109,4 +126,8 @@ abstract class ShadeModule {
abstract fun bindsShadeViewController(
notificationPanelViewController: NotificationPanelViewController
): ShadeViewController
+
+ @Binds
+ @SysUISingleton
+ abstract fun bindsPrivacyChipRepository(impl: PrivacyChipRepositoryImpl): PrivacyChipRepository
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt
index 0befb61ff814..941c6f33bc16 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt
@@ -26,7 +26,7 @@ import com.android.systemui.statusbar.policy.HeadsUpManager
* this class. If any method in this class is needed outside of CentralSurfacesImpl, it must be
* pulled up into ShadeViewController.
*/
-interface ShadeSurface : ShadeViewController, ShadeBackActionInteractor {
+interface ShadeSurface : ShadeViewController, ShadeBackActionInteractor, ShadeLockscreenInteractor {
/** Initialize objects instead of injecting to avoid circular dependencies. */
fun initDependencies(
centralSurfaces: CentralSurfaces,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
index 74035bd442db..44c6a82d93ca 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
@@ -37,25 +37,10 @@ interface ShadeViewController {
/** Animates to an expanded shade with QS expanded. If the shade starts expanded, expands QS. */
fun expandToQs()
- /**
- * Expand shade so that notifications are visible. Non-split shade: just expanding shade or
- * collapsing QS when they're expanded. Split shade: only expanding shade, notifications are
- * always visible
- *
- * Called when `adb shell cmd statusbar expand-notifications` is executed.
- */
- fun expandToNotifications()
-
/** Returns whether the shade is expanding or collapsing itself or quick settings. */
val isExpandingOrCollapsing: Boolean
/**
- * Returns whether the shade height is greater than zero (i.e. partially or fully expanded),
- * there is a HUN, the shade is animating, or the shade is instantly expanding.
- */
- val isExpanded: Boolean
-
- /**
* Returns whether the shade height is greater than zero or the shade is expecting a synthesized
* down event.
*/
@@ -101,12 +86,6 @@ interface ShadeViewController {
/** Returns whether status bar icons should be hidden when the shade is expanded. */
fun shouldHideStatusBarIconsWhenExpanded(): Boolean
- /**
- * Do not let the user drag the shade up and down for the current touch session. This is
- * necessary to avoid shade expansion while/after the bouncer is dismissed.
- */
- fun blockExpansionForCurrentTouch()
-
/** Sets a listener to be notified when touch tracking begins. */
fun setTrackingStartedListener(trackingStartedListener: TrackingStartedListener)
@@ -120,15 +99,6 @@ interface ShadeViewController {
/** If the latency tracker is enabled, begins tracking expand latency. */
fun startExpandLatencyTracking()
- /** Called before animating Keyguard dismissal, i.e. the animation dismissing the bouncer. */
- fun startBouncerPreHideAnimation()
-
- /** Called once every minute while dozing. */
- fun dozeTimeTick()
-
- /** Close guts, notification menus, and QS. Set scroll and overscroll to 0. */
- fun resetViews(animate: Boolean)
-
/** Returns the StatusBarState. */
val barState: Int
@@ -145,9 +115,6 @@ interface ShadeViewController {
*/
fun setAlphaChangeAnimationEndAction(r: Runnable)
- /** Sets whether the screen has temporarily woken up to display notifications. */
- fun setPulsing(pulsing: Boolean)
-
/** Sets Qs ScrimEnabled and updates QS state. */
fun setQsScrimEnabled(qsScrimEnabled: Boolean)
@@ -166,32 +133,6 @@ interface ShadeViewController {
/** Removes a global layout listener. */
fun removeOnGlobalLayoutListener(listener: ViewTreeObserver.OnGlobalLayoutListener)
- /** Posts the given runnable to the view. */
- fun postToView(action: Runnable): Boolean
-
- // ******* Begin Keyguard Section *********
- /** Animate to expanded shade after a delay in ms. Used for lockscreen to shade transition. */
- fun transitionToExpandedShade(delay: Long)
-
- /** @see ViewGroupFadeHelper.reset */
- fun resetViewGroupFade()
-
- /**
- * Set the alpha and translationY of the keyguard elements which only show on the lockscreen,
- * but not in shade locked / shade. This is used when dragging down to the full shade.
- */
- fun setKeyguardTransitionProgress(keyguardAlpha: Float, keyguardTranslationY: Int)
-
- /** Sets the overstretch amount in raw pixels when dragging down. */
- fun setOverStretchAmount(amount: Float)
-
- /**
- * Sets the alpha value to be set on the keyguard status bar.
- *
- * @param alpha value between 0 and 1. -1 if the value is to be reset.
- */
- fun setKeyguardStatusBarAlpha(alpha: Float)
-
/**
* Reconfigures the shade to show the AOD UI (clock, smartspace, etc). This is called by the
* screen off animation controller in order to animate in AOD without "actually" fully switching
@@ -251,8 +192,6 @@ interface ShadeViewController {
*/
fun performHapticFeedback(constant: Int)
- // ******* End Keyguard Section *********
-
/** Returns the ShadeHeadsUpTracker. */
val shadeHeadsUpTracker: ShadeHeadsUpTracker
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
index 5d966ac51bc8..7a181f106514 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
@@ -27,7 +27,7 @@ import javax.inject.Inject
/** Empty implementation of ShadeViewController for variants with no shade. */
class ShadeViewControllerEmptyImpl @Inject constructor() :
- ShadeViewController, ShadeBackActionInteractor {
+ ShadeViewController, ShadeBackActionInteractor, ShadeLockscreenInteractor {
override fun expand(animate: Boolean) {}
override fun expandToQs() {}
override fun expandToNotifications() {}
@@ -70,9 +70,6 @@ class ShadeViewControllerEmptyImpl @Inject constructor() :
override fun updateTouchableRegion() {}
override fun addOnGlobalLayoutListener(listener: ViewTreeObserver.OnGlobalLayoutListener) {}
override fun removeOnGlobalLayoutListener(listener: ViewTreeObserver.OnGlobalLayoutListener) {}
- override fun postToView(action: Runnable): Boolean {
- return false
- }
override fun transitionToExpandedShade(delay: Long) {}
override fun resetViewGroupFade() {}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/PrivacyChipRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/PrivacyChipRepository.kt
new file mode 100644
index 000000000000..91c92cc8cd2f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/PrivacyChipRepository.kt
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2024 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.shade.data.repository
+
+import android.content.IntentFilter
+import android.os.UserHandle
+import android.safetycenter.SafetyCenterManager
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.privacy.PrivacyConfig
+import com.android.systemui.privacy.PrivacyItem
+import com.android.systemui.privacy.PrivacyItemController
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.stateIn
+
+interface PrivacyChipRepository {
+ /** Whether or not the Safety Center is enabled. */
+ val isSafetyCenterEnabled: StateFlow<Boolean>
+
+ /** The list of PrivacyItems to be displayed by the privacy chip. */
+ val privacyItems: StateFlow<List<PrivacyItem>>
+
+ /** Whether or not mic & camera indicators are enabled in the device privacy config. */
+ val isMicCameraIndicationEnabled: StateFlow<Boolean>
+
+ /** Whether or not location indicators are enabled in the device privacy config. */
+ val isLocationIndicationEnabled: StateFlow<Boolean>
+}
+
+@SysUISingleton
+class PrivacyChipRepositoryImpl
+@Inject
+constructor(
+ @Application applicationScope: CoroutineScope,
+ private val privacyConfig: PrivacyConfig,
+ private val privacyItemController: PrivacyItemController,
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+ broadcastDispatcher: BroadcastDispatcher,
+ private val safetyCenterManager: SafetyCenterManager,
+) : PrivacyChipRepository {
+ override val isSafetyCenterEnabled: StateFlow<Boolean> =
+ broadcastDispatcher
+ .broadcastFlow(
+ filter =
+ IntentFilter().apply {
+ addAction(SafetyCenterManager.ACTION_SAFETY_CENTER_ENABLED_CHANGED)
+ },
+ user = UserHandle.SYSTEM,
+ map = { _, _ -> safetyCenterManager.isSafetyCenterEnabled }
+ )
+ .onStart { emit(safetyCenterManager.isSafetyCenterEnabled) }
+ .flowOn(backgroundDispatcher)
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = false,
+ )
+
+ override val privacyItems: StateFlow<List<PrivacyItem>> =
+ conflatedCallbackFlow {
+ val callback =
+ object : PrivacyItemController.Callback {
+ override fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>) {
+ trySend(privacyItems)
+ }
+ }
+ privacyItemController.addCallback(callback)
+ awaitClose { privacyItemController.removeCallback(callback) }
+ }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.Eagerly,
+ initialValue = emptyList(),
+ )
+
+ override val isMicCameraIndicationEnabled: StateFlow<Boolean> =
+ conflatedCallbackFlow {
+ val callback =
+ object : PrivacyConfig.Callback {
+ override fun onFlagMicCameraChanged(flag: Boolean) {
+ trySend(flag)
+ }
+ }
+ privacyConfig.addCallback(callback)
+ awaitClose { privacyConfig.removeCallback(callback) }
+ }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.Eagerly,
+ initialValue = privacyItemController.micCameraAvailable,
+ )
+
+ override val isLocationIndicationEnabled: StateFlow<Boolean> =
+ conflatedCallbackFlow {
+ val callback =
+ object : PrivacyConfig.Callback {
+ override fun onFlagLocationChanged(flag: Boolean) {
+ trySend(flag)
+ }
+ }
+ privacyConfig.addCallback(callback)
+ awaitClose { privacyConfig.removeCallback(callback) }
+ }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.Eagerly,
+ initialValue = privacyItemController.locationAvailable,
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeHeaderClockRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeHeaderClockRepository.kt
new file mode 100644
index 000000000000..a9bf2dfdef9e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeHeaderClockRepository.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2024 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.shade.data.repository
+
+import android.app.PendingIntent
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.policy.NextAlarmController
+import javax.inject.Inject
+
+@SysUISingleton
+class ShadeHeaderClockRepository
+@Inject
+constructor(
+ nextAlarmController: NextAlarmController,
+) {
+ private val nextAlarmCallback =
+ NextAlarmController.NextAlarmChangeCallback { nextAlarm ->
+ nextAlarmIntent = nextAlarm?.showIntent
+ }
+
+ init {
+ nextAlarmController.addCallback(nextAlarmCallback)
+ }
+
+ /** Intent to show the next active alarm. */
+ var nextAlarmIntent: PendingIntent? = null
+ private set
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PrivacyChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PrivacyChipInteractor.kt
new file mode 100644
index 000000000000..4c6c31809275
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PrivacyChipInteractor.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2024 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.shade.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.privacy.OngoingPrivacyChip
+import com.android.systemui.privacy.PrivacyDialogController
+import com.android.systemui.privacy.PrivacyDialogControllerV2
+import com.android.systemui.privacy.PrivacyItem
+import com.android.systemui.shade.data.repository.PrivacyChipRepository
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+@SysUISingleton
+class PrivacyChipInteractor
+@Inject
+constructor(
+ @Application applicationScope: CoroutineScope,
+ private val repository: PrivacyChipRepository,
+ private val privacyDialogController: PrivacyDialogController,
+ private val privacyDialogControllerV2: PrivacyDialogControllerV2,
+ private val deviceProvisionedController: DeviceProvisionedController,
+) {
+ /** The list of PrivacyItems to be displayed by the privacy chip. */
+ val privacyItems: StateFlow<List<PrivacyItem>> = repository.privacyItems
+
+ /** Whether or not mic & camera indicators are enabled in the device privacy config. */
+ val isMicCameraIndicationEnabled: StateFlow<Boolean> = repository.isMicCameraIndicationEnabled
+
+ /** Whether or not location indicators are enabled in the device privacy config. */
+ val isLocationIndicationEnabled: StateFlow<Boolean> = repository.isLocationIndicationEnabled
+
+ /** Whether or not the privacy chip should be visible. */
+ val isChipVisible: StateFlow<Boolean> =
+ privacyItems
+ .map { it.isNotEmpty() }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = false,
+ )
+
+ /** Whether or not the privacy chip is enabled in the device privacy config. */
+ val isChipEnabled: StateFlow<Boolean> =
+ combine(
+ isMicCameraIndicationEnabled,
+ isLocationIndicationEnabled,
+ ) { micCamera, location ->
+ micCamera || location
+ }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = false,
+ )
+
+ /** Notifies that the privacy chip was clicked. */
+ fun onPrivacyChipClicked(privacyChip: OngoingPrivacyChip) {
+ if (!deviceProvisionedController.isDeviceProvisioned) return
+
+ if (repository.isSafetyCenterEnabled.value) {
+ privacyDialogControllerV2.showDialog(privacyChip.context, privacyChip)
+ } else {
+ privacyDialogController.showDialog(privacyChip.context)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeHeaderClockInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeHeaderClockInteractor.kt
new file mode 100644
index 000000000000..186bfcbbc8e2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeHeaderClockInteractor.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 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.shade.domain.interactor
+
+import android.content.Intent
+import android.provider.AlarmClock
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.shade.data.repository.ShadeHeaderClockRepository
+import javax.inject.Inject
+
+@SysUISingleton
+class ShadeHeaderClockInteractor
+@Inject
+constructor(
+ private val repository: ShadeHeaderClockRepository,
+ private val activityStarter: ActivityStarter,
+) {
+ /** Launch the clock activity. */
+ fun launchClockActivity() {
+ val nextAlarmIntent = repository.nextAlarmIntent
+ if (nextAlarmIntent != null) {
+ activityStarter.postStartActivityDismissingKeyguard(nextAlarmIntent)
+ } else {
+ activityStarter.postStartActivityDismissingKeyguard(
+ Intent(AlarmClock.ACTION_SHOW_ALARMS),
+ 0
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt
new file mode 100644
index 000000000000..21a782e43b78
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2024 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.shade.domain.interactor
+
+import com.android.keyguard.LockIconViewController
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.shade.ShadeLockscreenInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+
+class ShadeLockscreenInteractorImpl
+@Inject
+constructor(
+ @Background private val scope: CoroutineScope,
+ shadeInteractor: ShadeInteractor,
+ private val sceneInteractor: SceneInteractor,
+ private val lockIconViewController: LockIconViewController,
+) : ShadeLockscreenInteractor {
+ override fun expandToNotifications() {
+ changeToShadeScene()
+ }
+
+ override val isExpandingOrCollapsing = shadeInteractor.isUserInteracting.value
+
+ override val isExpanded = shadeInteractor.isAnyExpanded.value
+
+ override fun startBouncerPreHideAnimation() {
+ // TODO("b/324280998") Implement replacement or delete
+ }
+
+ override fun dozeTimeTick() {
+ lockIconViewController.dozeTimeTick()
+ }
+
+ override fun blockExpansionForCurrentTouch() {
+ // TODO("b/324280998") Implement replacement or delete
+ }
+
+ override fun resetViews(animate: Boolean) {
+ // The existing comment to the only call to this claims it only calls it to collapse QS
+ changeToShadeScene()
+ }
+
+ override fun setPulsing(pulsing: Boolean) {
+ // Now handled elsewhere. Do nothing.
+ }
+ override fun transitionToExpandedShade(delay: Long) {
+ scope.launch {
+ delay(delay)
+ changeToShadeScene()
+ }
+ }
+
+ override fun resetViewGroupFade() {
+ // Now handled elsewhere. Do nothing.
+ }
+
+ override fun setKeyguardTransitionProgress(keyguardAlpha: Float, keyguardTranslationY: Int) {
+ // Now handled elsewhere. Do nothing.
+ }
+
+ override fun setOverStretchAmount(amount: Float) {
+ // Now handled elsewhere. Do nothing.
+ }
+
+ override fun setKeyguardStatusBarAlpha(alpha: Float) {
+ // TODO(b/325072511) delete this
+ }
+
+ private fun changeToShadeScene() {
+ sceneInteractor.changeScene(
+ SceneKey.Shade,
+ "ShadeLockscreenInteractorImpl.expandToNotifications",
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
index 314637e4b27e..1191c0f247f9 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
@@ -25,8 +25,11 @@ import android.os.UserHandle
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.privacy.OngoingPrivacyChip
+import com.android.systemui.privacy.PrivacyItem
import com.android.systemui.res.R
-import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.shade.domain.interactor.PrivacyChipInteractor
+import com.android.systemui.shade.domain.interactor.ShadeHeaderClockInteractor
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel
import java.util.Date
@@ -50,9 +53,10 @@ class ShadeHeaderViewModel
constructor(
@Application private val applicationScope: CoroutineScope,
context: Context,
- sceneInteractor: SceneInteractor,
mobileIconsInteractor: MobileIconsInteractor,
val mobileIconsViewModel: MobileIconsViewModel,
+ private val privacyChipInteractor: PrivacyChipInteractor,
+ private val clockInteractor: ShadeHeaderClockInteractor,
broadcastDispatcher: BroadcastDispatcher,
) {
/** True if there is exactly one mobile connection. */
@@ -64,6 +68,23 @@ constructor(
.map { list -> list.map { it.subscriptionId } }
.stateIn(applicationScope, SharingStarted.WhileSubscribed(), emptyList())
+ /** The list of PrivacyItems to be displayed by the privacy chip. */
+ val privacyItems: StateFlow<List<PrivacyItem>> = privacyChipInteractor.privacyItems
+
+ /** Whether or not mic & camera indicators are enabled in the device privacy config. */
+ val isMicCameraIndicationEnabled: StateFlow<Boolean> =
+ privacyChipInteractor.isMicCameraIndicationEnabled
+
+ /** Whether or not location indicators are enabled in the device privacy config. */
+ val isLocationIndicationEnabled: StateFlow<Boolean> =
+ privacyChipInteractor.isLocationIndicationEnabled
+
+ /** Whether or not the privacy chip should be visible. */
+ val isPrivacyChipVisible: StateFlow<Boolean> = privacyChipInteractor.isChipVisible
+
+ /** Whether or not the privacy chip is enabled in the device privacy config. */
+ val isPrivacyChipEnabled: StateFlow<Boolean> = privacyChipInteractor.isChipEnabled
+
private val longerPattern = context.getString(R.string.abbrev_wday_month_day_no_year_alarm)
private val shorterPattern = context.getString(R.string.abbrev_month_day_no_year)
private val longerDateFormat = MutableStateFlow(getFormatFromPattern(longerPattern))
@@ -97,6 +118,16 @@ constructor(
applicationScope.launch { updateDateTexts(false) }
}
+ /** Notifies that the privacy chip was clicked. */
+ fun onPrivacyChipClicked(privacyChip: OngoingPrivacyChip) {
+ privacyChipInteractor.onPrivacyChipClicked(privacyChip)
+ }
+
+ /** Notifies that the clock was clicked. */
+ fun onClockClicked() {
+ clockInteractor.launchClockActivity()
+ }
+
private fun updateDateTexts(invalidateFormats: Boolean) {
if (invalidateFormats) {
longerDateFormat.value = getFormatFromPattern(longerPattern)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
index 9af2d58910b6..38358a8f244e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
@@ -19,7 +19,7 @@ package com.android.systemui.shade.ui.viewmodel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
-import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
import com.android.systemui.qs.ui.adapter.QSSceneAdapter
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
@@ -28,6 +28,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
/** Models UI state and handles user input for the shade scene. */
@@ -63,6 +64,16 @@ constructor(
),
)
+ /** Whether or not the shade container should be clickable. */
+ val isClickable: StateFlow<Boolean> =
+ upDestinationSceneKey
+ .map { it == SceneKey.Lockscreen }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = false
+ )
+
/** Notifies that some content in the shade was clicked. */
fun onContentClicked() = deviceEntryInteractor.attemptDeviceEntry()
diff --git a/packages/SystemUI/src/com/android/systemui/slice/SliceViewManagerExt.kt b/packages/SystemUI/src/com/android/systemui/slice/SliceViewManagerExt.kt
new file mode 100644
index 000000000000..384acc493c4a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/slice/SliceViewManagerExt.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 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.slice
+
+import android.net.Uri
+import androidx.slice.Slice
+import androidx.slice.SliceViewManager
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.launch
+
+/**
+ * Returns updating [Slice] for a [sliceUri]. It's null when there is no slice available for the
+ * provided Uri. This can change overtime because of external changes (like device being
+ * connected/disconnected).
+ */
+fun SliceViewManager.sliceForUri(sliceUri: Uri): Flow<Slice?> =
+ ConflatedCallbackFlow.conflatedCallbackFlow {
+ val callback = SliceViewManager.SliceCallback { launch { send(it) } }
+
+ val slice = bindSlice(sliceUri)
+ send(slice)
+ registerSliceCallback(sliceUri, callback)
+ awaitClose { unregisterSliceCallback(sliceUri, callback) }
+ }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index ffb11dd3cf92..3908edec7da5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -177,6 +177,7 @@ public class CommandQueue extends IStatusBar.Stub implements
private static final int MSG_CONFIRM_IMMERSIVE_PROMPT = 77 << MSG_SHIFT;
private static final int MSG_IMMERSIVE_CHANGED = 78 << MSG_SHIFT;
private static final int MSG_SET_QS_TILES = 79 << MSG_SHIFT;
+ private static final int MSG_ENTER_DESKTOP = 80 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
public static final int FLAG_EXCLUDE_RECENTS_PANEL = 1 << 1;
@@ -520,6 +521,11 @@ public class CommandQueue extends IStatusBar.Stub implements
* @see IStatusBar#immersiveModeChanged
*/
default void immersiveModeChanged(int rootDisplayAreaId, boolean isImmersiveMode) {}
+
+ /**
+ * @see IStatusBar#enterDesktop(int)
+ */
+ default void enterDesktop(int displayId) {}
}
@VisibleForTesting
@@ -609,14 +615,7 @@ public class CommandQueue extends IStatusBar.Stub implements
args.argi2 = state1;
args.argi3 = state2;
args.argi4 = animate ? 1 : 0;
- Message msg = mHandler.obtainMessage(MSG_DISABLE, args);
- if (Looper.myLooper() == mHandler.getLooper()) {
- // If its the right looper execute immediately so hides can be handled quickly.
- mHandler.handleMessage(msg);
- msg.recycle();
- } else {
- msg.sendToTarget();
- }
+ mHandler.obtainMessage(MSG_DISABLE, args).sendToTarget();
}
}
@@ -1420,6 +1419,13 @@ public class CommandQueue extends IStatusBar.Stub implements
mHandler.obtainMessage(MSG_GO_TO_FULLSCREEN_FROM_SPLIT).sendToTarget();
}
+ @Override
+ public void enterDesktop(int displayId) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = displayId;
+ mHandler.obtainMessage(MSG_ENTER_DESKTOP, args).sendToTarget();
+ }
+
private final class H extends Handler {
private H(Looper l) {
super(l);
@@ -1914,6 +1920,13 @@ public class CommandQueue extends IStatusBar.Stub implements
mCallbacks.get(i).immersiveModeChanged(rootDisplayAreaId, isImmersiveMode);
}
break;
+ case MSG_ENTER_DESKTOP:
+ args = (SomeArgs) msg.obj;
+ int displayId = args.argi1;
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).enterDesktop(displayId);
+ }
+ break;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
index e5982428fe14..a4741a509d72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
@@ -25,8 +25,11 @@ import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.graphics.Matrix;
@@ -71,6 +74,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settingslib.Utils;
import com.android.systemui.res.R;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.bottomsheet.BottomSheetDialog;
@@ -114,6 +118,7 @@ public final class KeyboardShortcutListSearch {
private Button mButtonInput;
private Button mButtonOpenApps;
private Button mButtonSpecificApp;
+ private CharSequence mCurrentAppPackageName;
private TextView mNoSearchResults;
private final SparseArray<String> mSpecialCharacterNames = new SparseArray<>();
@@ -412,8 +417,10 @@ public final class KeyboardShortcutListSearch {
mWindowManager.requestAppKeyboardShortcuts(result -> {
// Add specific app shortcuts
if (result.isEmpty()) {
+ mCurrentAppPackageName = null;
mKeySearchResultMap.put(SHORTCUT_SPECIFICAPP_INDEX, false);
} else {
+ mCurrentAppPackageName = result.get(0).getPackageName();
mSpecificAppGroup.addAll(reMapToKeyboardShortcutMultiMappingGroup(result));
mKeySearchResultMap.put(SHORTCUT_SPECIFICAPP_INDEX, true);
}
@@ -823,6 +830,7 @@ public final class KeyboardShortcutListSearch {
mNoSearchResults = keyboardShortcutsView.findViewById(R.id.shortcut_search_no_result);
mKeyboardShortcutsBottomSheetDialog.setContentView(keyboardShortcutsView);
setButtonsDefaultStatus(keyboardShortcutsView);
+ populateCurrentAppButton();
populateKeyboardShortcutSearchList(shortcutsContainer);
// Workaround for solve issue about dialog not full expanded when landscape.
@@ -1272,6 +1280,41 @@ public final class KeyboardShortcutListSearch {
mFullButtonList.add(mButtonSpecificApp);
}
+ private void resetCurrentAppButton() {
+ if (mButtonSpecificApp == null) {
+ return;
+ }
+ mButtonSpecificApp.setText(
+ mContext.getString(R.string.keyboard_shortcut_search_category_current_app));
+ // TODO(b/325252986): Reset icon once the icon is implemented
+ }
+
+ private void populateCurrentAppButton() {
+ if (mButtonSpecificApp == null) {
+ return;
+ }
+ if (mCurrentAppPackageName != null) {
+ final int userId = mContext.getUserId();
+ try {
+ PackageManager pmUser = CentralSurfaces.getPackageManagerForUser(
+ mContext,
+ userId);
+ ApplicationInfo appInfo = pmUser.getApplicationInfoAsUser(
+ mCurrentAppPackageName.toString(),
+ 0,
+ userId);
+ // According to the API, we will always get a label
+ mButtonSpecificApp.setText(pmUser.getApplicationLabel(appInfo));
+ // TODO(b/325252986): Show icon once it has been defined
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Package name not found", e);
+ resetCurrentAppButton();
+ }
+ } else {
+ resetCurrentAppButton();
+ }
+ }
+
private void setButtonFocusColor(int i, boolean isFocused) {
if (isFocused) {
mFullButtonList.get(i).setTextColor(getColorOfTextColorOnAccent());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 14230ba43f59..19fe60a60bf5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar;
+import static android.adaptiveauth.Flags.enableAdaptiveAuth;
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
import static android.app.admin.DevicePolicyResources.Strings.SystemUi.KEYGUARD_MANAGEMENT_DISCLOSURE;
import static android.app.admin.DevicePolicyResources.Strings.SystemUi.KEYGUARD_NAMED_MANAGEMENT_DISCLOSURE;
@@ -32,6 +33,7 @@ import static com.android.systemui.DejankUtils.whitelistIpcs;
import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.IMPORTANT_MSG_MIN_DURATION;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_IS_DISMISSIBLE;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ADAPTIVE_AUTH;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE;
@@ -454,6 +456,9 @@ public class KeyguardIndicationController {
updateLockScreenAlignmentMsg();
updateLockScreenLogoutView();
updateLockScreenPersistentUnlockMsg();
+ if (enableAdaptiveAuth()) {
+ updateLockScreenAdaptiveAuthMsg(userId);
+ }
}
private void updateOrganizedOwnedDevice() {
@@ -740,6 +745,22 @@ public class KeyguardIndicationController {
}
}
+ private void updateLockScreenAdaptiveAuthMsg(int userId) {
+ final boolean deviceLocked = mKeyguardUpdateMonitor.isDeviceLockedByAdaptiveAuth(userId);
+ if (deviceLocked) {
+ mRotateTextViewController.updateIndication(
+ INDICATION_TYPE_ADAPTIVE_AUTH,
+ new KeyguardIndication.Builder()
+ .setMessage(mContext
+ .getString(R.string.kg_prompt_after_adaptive_auth_lock))
+ .setTextColor(mInitialTextColorState)
+ .build(),
+ true);
+ } else {
+ mRotateTextViewController.hideIndication(INDICATION_TYPE_ADAPTIVE_AUTH);
+ }
+ }
+
private boolean isOrganizationOwnedDevice() {
return mDevicePolicyManager.isDeviceManaged()
|| mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt
index 62c9980c336c..9f098e79f759 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt
@@ -3,10 +3,10 @@ package com.android.systemui.statusbar
import android.content.Context
import android.util.IndentingPrintWriter
import android.util.MathUtils
-import com.android.systemui.res.R
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.controls.ui.MediaHierarchyManager
-import com.android.systemui.shade.ShadeViewController
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
+import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeLockscreenInteractor
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.SplitShadeStateController
import dagger.assisted.Assisted
@@ -18,7 +18,7 @@ class LockscreenShadeKeyguardTransitionController
@AssistedInject
constructor(
private val mediaHierarchyManager: MediaHierarchyManager,
- @Assisted private val notificationPanelController: ShadeViewController,
+ @Assisted private val shadeLockscreenInteractor: ShadeLockscreenInteractor,
context: Context,
configurationController: ConfigurationController,
dumpManager: DumpManager,
@@ -72,10 +72,10 @@ constructor(
alphaProgress = MathUtils.saturate(dragDownAmount / alphaTransitionDistance)
alpha = 1f - alphaProgress
translationY = calculateKeyguardTranslationY(dragDownAmount)
- notificationPanelController.setKeyguardTransitionProgress(alpha, translationY)
+ shadeLockscreenInteractor.setKeyguardTransitionProgress(alpha, translationY)
statusBarAlpha = if (useSplitShade) alpha else -1f
- notificationPanelController.setKeyguardStatusBarAlpha(statusBarAlpha)
+ shadeLockscreenInteractor.setKeyguardStatusBarAlpha(statusBarAlpha)
}
private fun calculateKeyguardTranslationY(dragDownAmount: Float): Int {
@@ -117,7 +117,7 @@ constructor(
@AssistedFactory
fun interface Factory {
fun create(
- notificationPanelController: ShadeViewController
+ shadeLockscreenInteractor: ShadeLockscreenInteractor
): LockscreenShadeKeyguardTransitionController
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionController.kt
index 4d0552e7cb31..adca3f2d25d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionController.kt
@@ -21,9 +21,9 @@ import android.util.IndentingPrintWriter
import android.util.MathUtils
import androidx.annotation.FloatRange
import androidx.annotation.Px
-import com.android.systemui.res.R
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.qs.QS
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.SplitShadeStateController
import dagger.assisted.Assisted
@@ -38,7 +38,7 @@ constructor(
context: Context,
configurationController: ConfigurationController,
dumpManager: DumpManager,
- @Assisted private val qsProvider: () -> QS,
+ @Assisted private val qsProvider: () -> QS?,
splitShadeStateController: SplitShadeStateController
) :
AbstractLockscreenShadeTransitionController(
@@ -48,7 +48,7 @@ constructor(
splitShadeStateController
) {
- private val qs: QS
+ private val qs: QS?
get() = qsProvider()
/**
@@ -135,7 +135,7 @@ constructor(
/* amount= */ MathUtils.saturate(qsDragDownAmount / qsSquishTransitionDistance)
)
isTransitioningToFullShade = dragDownAmount > 0.0f
- qs.setTransitionToFullShadeProgress(
+ qs?.setTransitionToFullShadeProgress(
isTransitioningToFullShade,
qsTransitionFraction,
qsSquishTransitionFraction
@@ -163,6 +163,6 @@ constructor(
@AssistedFactory
fun interface Factory {
- fun create(qsProvider: () -> QS): LockscreenShadeQsTransitionController
+ fun create(qsProvider: () -> QS?): LockscreenShadeQsTransitionController
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index ef5026538216..4ee83497b368 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -15,6 +15,7 @@ import androidx.annotation.VisibleForTesting
import com.android.systemui.Dumpable
import com.android.systemui.ExpandHelper
import com.android.systemui.Flags.nsslFalsingFix
+import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.Gefingerpoken
import com.android.systemui.biometrics.UdfpsKeyguardViewControllerLegacy
import com.android.systemui.classifier.Classifier
@@ -23,17 +24,16 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.domain.interactor.NaturalScrollingSettingObserver
-import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
-import com.android.systemui.media.controls.ui.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
import com.android.systemui.navigationbar.gestural.Utilities.isTrackpadScroll
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.ActivityStarter.OnDismissAction
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.qs.QS
import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.qs.ui.adapter.QSSceneAdapter
import com.android.systemui.res.R
-import com.android.systemui.shade.ShadeViewController
+import com.android.systemui.shade.ShadeLockscreenInteractor
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -47,6 +47,7 @@ import com.android.systemui.statusbar.phone.LSShadeTransitionLogger
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.SplitShadeStateController
import com.android.wm.shell.animation.Interpolators
+import dagger.Lazy
import java.io.PrintWriter
import javax.inject.Inject
@@ -68,7 +69,7 @@ constructor(
private val mediaHierarchyManager: MediaHierarchyManager,
private val scrimTransitionController: LockscreenShadeScrimTransitionController,
private val keyguardTransitionControllerFactory:
- LockscreenShadeKeyguardTransitionController.Factory,
+ LockscreenShadeKeyguardTransitionController.Factory,
private val depthController: NotificationShadeDepthController,
private val context: Context,
private val splitShadeOverScrollerFactory: SplitShadeLockScreenOverScroller.Factory,
@@ -81,9 +82,10 @@ constructor(
qsTransitionControllerFactory: LockscreenShadeQsTransitionController.Factory,
private val shadeRepository: ShadeRepository,
private val shadeInteractor: ShadeInteractor,
- private val powerInteractor: PowerInteractor,
private val splitShadeStateController: SplitShadeStateController,
- private val naturalScrollingSettingObserver: NaturalScrollingSettingObserver,
+ private val shadeLockscreenInteractorLazy: Lazy<ShadeLockscreenInteractor>,
+ naturalScrollingSettingObserver: NaturalScrollingSettingObserver,
+ private val lazyQSSceneAdapter: Lazy<QSSceneAdapter>,
) : Dumpable {
private var pulseHeight: Float = 0f
@@ -92,9 +94,12 @@ constructor(
private set
private var useSplitShade: Boolean = false
private lateinit var nsslController: NotificationStackScrollLayoutController
- lateinit var shadeViewController: ShadeViewController
lateinit var centralSurfaces: CentralSurfaces
- lateinit var qS: QS
+
+ // When in scene container mode, this will be null. In that case, we use the adapter if needed
+ var qS: QS? = null
+ private val isQsFullyCollapsed: Boolean
+ get() = qS?.isFullyCollapsed ?: lazyQSSceneAdapter.get().isQsFullyCollapsed
/** A handler that handles the next keyguard dismiss animation. */
private var animationHandlerOnKeyguardDismiss: ((Long) -> Unit)? = null
@@ -165,7 +170,6 @@ constructor(
val touchHelper =
DragDownHelper(
falsingManager,
- falsingCollector,
this,
naturalScrollingSettingObserver,
shadeRepository,
@@ -181,7 +185,7 @@ constructor(
}
private val keyguardTransitionController by lazy {
- keyguardTransitionControllerFactory.create(shadeViewController)
+ keyguardTransitionControllerFactory.create(shadeLockscreenInteractorLazy.get())
}
private val qsTransitionController = qsTransitionControllerFactory.create { qS }
@@ -288,7 +292,8 @@ constructor(
/** @return true if the interaction is accepted, false if it should be cancelled */
internal fun canDragDown(): Boolean {
return (statusBarStateController.state == StatusBarState.KEYGUARD ||
- nsslController.isInLockedDownShade()) && (qS.isFullyCollapsed || useSplitShade)
+ nsslController.isInLockedDownShade()) &&
+ (isQsFullyCollapsed || useSplitShade)
}
/** Called by the touch helper when when a gesture has completed all the way and released. */
@@ -320,7 +325,7 @@ constructor(
true /* drag down is always an open */
)
}
- shadeViewController.transitionToExpandedShade(delay)
+ shadeLockscreenInteractorLazy.get().transitionToExpandedShade(delay)
callbacks.forEach {
it.setTransitionToFullShadeAmount(0f, /* animated= */ true, delay)
}
@@ -412,7 +417,7 @@ constructor(
get() =
(statusBarStateController.getState() == StatusBarState.KEYGUARD &&
!keyguardBypassController.bypassEnabled &&
- (qS.isFullyCollapsed || useSplitShade))
+ (isQsFullyCollapsed || useSplitShade))
/** The amount in pixels that the user has dragged down. */
internal var dragDownAmount = 0f
@@ -538,7 +543,7 @@ constructor(
} else {
// Let's only animate notifications
animationHandler = { delay: Long ->
- shadeViewController.transitionToExpandedShade(delay)
+ shadeLockscreenInteractorLazy.get().transitionToExpandedShade(delay)
}
}
goToLockedShadeInternal(expandedView, animationHandler, cancelAction = null)
@@ -661,7 +666,7 @@ constructor(
*/
private fun performDefaultGoToFullShadeAnimation(delay: Long) {
logger.logDefaultGoToFullShadeAnimation(delay)
- shadeViewController.transitionToExpandedShade(delay)
+ shadeLockscreenInteractorLazy.get().transitionToExpandedShade(delay)
animateAppear(delay)
}
@@ -686,7 +691,7 @@ constructor(
} else {
pulseHeight = height
val overflow = nsslController.setPulseHeight(height)
- shadeViewController.setOverStretchAmount(overflow)
+ shadeLockscreenInteractorLazy.get().setOverStretchAmount(overflow)
val transitionHeight = if (keyguardBypassController.bypassEnabled) height else 0.0f
transitionToShadeAmountCommon(transitionHeight)
}
@@ -760,7 +765,6 @@ constructor(
*/
class DragDownHelper(
private val falsingManager: FalsingManager,
- private val falsingCollector: FalsingCollector,
private val dragDownCallback: LockscreenShadeTransitionController,
private val naturalScrollingSettingObserver: NaturalScrollingSettingObserver,
private val shadeRepository: ShadeRepository,
@@ -852,7 +856,6 @@ class DragDownHelper(
if (!isDraggingDown) {
return false
}
- val x = event.x
val y = event.y
when (event.actionMasked) {
MotionEvent.ACTION_MOVE -> {
@@ -890,7 +893,7 @@ class DragDownHelper(
isDraggingDown = false
isTrackpadReverseScroll = false
shadeRepository.setLegacyLockscreenShadeTracking(false)
- if (nsslFalsingFix() || KeyguardShadeMigrationNssl.isEnabled) {
+ if (nsslFalsingFix() || migrateClocksToBlueprint()) {
return true
}
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationClickNotifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationClickNotifier.kt
index 692a9977c364..4ab78aab372d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationClickNotifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationClickNotifier.kt
@@ -1,8 +1,8 @@
package com.android.systemui.statusbar
+import android.app.Flags.lifetimeExtensionRefactor
import android.app.Notification
import android.os.RemoteException
-import android.util.Log
import com.android.internal.statusbar.IStatusBarService
import com.android.internal.statusbar.NotificationVisibility
import com.android.systemui.dagger.SysUISingleton
@@ -58,9 +58,14 @@ public class NotificationClickNotifier @Inject constructor(
} catch (e: RemoteException) {
// nothing
}
+ if (lifetimeExtensionRefactor()) {
+ notifyListenersAboutInteraction(key)
+ }
}
- mainExecutor.execute {
- notifyListenersAboutInteraction(key)
+ if (!lifetimeExtensionRefactor()) {
+ mainExecutor.execute {
+ notifyListenersAboutInteraction(key)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 2a4753def463..1a06eec1cb4d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -156,14 +156,12 @@ public class NotificationLockscreenUserManagerImpl implements
final String action = intent.getAction();
if (ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED.equals(action)) {
- if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
- mKeyguardAllowingNotifications =
- intent.getBooleanExtra(EXTRA_KM_PRIVATE_NOTIFS_ALLOWED, false);
- if (mCurrentUserId == getSendingUserId()) {
- boolean changed = updateLockscreenNotificationSetting();
- if (changed) {
- notifyNotificationStateChanged();
- }
+ mKeyguardAllowingNotifications =
+ intent.getBooleanExtra(EXTRA_KM_PRIVATE_NOTIFS_ALLOWED, false);
+ if (mCurrentUserId == getSendingUserId()) {
+ boolean changed = updateLockscreenNotificationSetting();
+ if (changed) {
+ notifyNotificationStateChanged();
}
}
}
@@ -176,36 +174,26 @@ public class NotificationLockscreenUserManagerImpl implements
final String action = intent.getAction();
if (ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action)) {
- if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
- boolean changed = false;
- int sendingUserId = getSendingUserId();
- if (sendingUserId == USER_ALL) {
- // When a Device Owner triggers changes it's sent as USER_ALL. Normalize
- // the user before calling into DPM
- sendingUserId = mCurrentUserId;
- @SuppressLint("MissingPermission")
- List<UserInfo> users = mUserManager.getUsers();
- for (int i = users.size() - 1; i >= 0; i--) {
- changed |= updateDpcSettings(users.get(i).id);
- }
- } else {
- changed |= updateDpcSettings(sendingUserId);
- }
-
- if (mCurrentUserId == sendingUserId) {
- changed |= updateLockscreenNotificationSetting();
- }
- if (changed) {
- notifyNotificationStateChanged();
+ boolean changed = false;
+ int sendingUserId = getSendingUserId();
+ if (sendingUserId == USER_ALL) {
+ // When a Device Owner triggers changes it's sent as USER_ALL. Normalize
+ // the user before calling into DPM
+ sendingUserId = mCurrentUserId;
+ @SuppressLint("MissingPermission")
+ List<UserInfo> users = mUserManager.getUsers();
+ for (int i = users.size() - 1; i >= 0; i--) {
+ changed |= updateDpcSettings(users.get(i).id);
}
} else {
- if (isCurrentProfile(getSendingUserId())) {
- mUsersAllowingPrivateNotifications.clear();
- updateLockscreenNotificationSetting();
- // TODO(b/231976036): Consolidate pipeline invalidations related to this
- // event
- // notifyNotificationStateChanged();
- }
+ changed |= updateDpcSettings(sendingUserId);
+ }
+
+ if (mCurrentUserId == sendingUserId) {
+ changed |= updateLockscreenNotificationSetting();
+ }
+ if (changed) {
+ notifyNotificationStateChanged();
}
}
}
@@ -225,12 +213,10 @@ public class NotificationLockscreenUserManagerImpl implements
updateCurrentProfilesCache();
} else if (Objects.equals(action, Intent.ACTION_USER_ADDED)){
updateCurrentProfilesCache();
- if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
- final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
- mBackgroundExecutor.execute(() -> {
- initValuesForUser(userId);
- });
- }
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
+ mBackgroundExecutor.execute(() -> {
+ initValuesForUser(userId);
+ });
} else if (profileAvailabilityActions(action)) {
updateCurrentProfilesCache();
} else if (Objects.equals(action, Intent.ACTION_USER_UNLOCKED)) {
@@ -360,28 +346,16 @@ public class NotificationLockscreenUserManagerImpl implements
}
private void init() {
- mLockscreenSettingsObserver = new ExecutorContentObserver(
- mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)
- ? mBackgroundExecutor
- : mMainExecutor) {
+ mLockscreenSettingsObserver = new ExecutorContentObserver(mBackgroundExecutor) {
@Override
public void onChange(boolean selfChange, Collection<Uri> uris, int flags) {
- if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
- @SuppressLint("MissingPermission")
- List<UserInfo> users = mUserManager.getUsers();
- for (int i = users.size() - 1; i >= 0; i--) {
- onChange(selfChange, uris, flags,users.get(i).getUserHandle());
- }
- } else {
- // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS or
- // LOCK_SCREEN_SHOW_NOTIFICATIONS, so we just dump our cache ...
- mUsersAllowingPrivateNotifications.clear();
- mUsersAllowingNotifications.clear();
- // ... and refresh all the notifications
- updateLockscreenNotificationSetting();
- notifyNotificationStateChanged();
+ @SuppressLint("MissingPermission")
+ List<UserInfo> users = mUserManager.getUsers();
+ for (int i = users.size() - 1; i >= 0; i--) {
+ onChange(selfChange, uris, flags,users.get(i).getUserHandle());
}
+
}
// Note: even though this is an override, this method is not called by the OS
@@ -390,22 +364,20 @@ public class NotificationLockscreenUserManagerImpl implements
@Override
public void onChange(boolean selfChange, Collection<Uri> uris,
int flags, UserHandle user) {
- if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
- boolean changed = false;
- for (Uri uri: uris) {
- if (SHOW_LOCKSCREEN.equals(uri)) {
- changed |= updateUserShowSettings(user.getIdentifier());
- } else if (SHOW_PRIVATE_LOCKSCREEN.equals(uri)) {
- changed |= updateUserShowPrivateSettings(user.getIdentifier());
- }
+ boolean changed = false;
+ for (Uri uri: uris) {
+ if (SHOW_LOCKSCREEN.equals(uri)) {
+ changed |= updateUserShowSettings(user.getIdentifier());
+ } else if (SHOW_PRIVATE_LOCKSCREEN.equals(uri)) {
+ changed |= updateUserShowPrivateSettings(user.getIdentifier());
}
+ }
- if (mCurrentUserId == user.getIdentifier()) {
- changed |= updateLockscreenNotificationSetting();
- }
- if (changed) {
- notifyNotificationStateChanged();
- }
+ if (mCurrentUserId == user.getIdentifier()) {
+ changed |= updateLockscreenNotificationSetting();
+ }
+ if (changed) {
+ notifyNotificationStateChanged();
}
}
};
@@ -432,16 +404,10 @@ public class NotificationLockscreenUserManagerImpl implements
mLockscreenSettingsObserver,
USER_ALL);
- if (!mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
- mContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
- mSettingsObserver);
- }
mBroadcastDispatcher.registerReceiver(mAllUsersReceiver,
new IntentFilter(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
- mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)
- ? mBackgroundExecutor : null, UserHandle.ALL);
+ mBackgroundExecutor, UserHandle.ALL);
if (keyguardPrivateNotifications()) {
mBroadcastDispatcher.registerReceiver(mKeyguardReceiver,
new IntentFilter(ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED),
@@ -471,17 +437,13 @@ public class NotificationLockscreenUserManagerImpl implements
mCurrentUserId = mUserTracker.getUserId(); // in case we reg'd receiver too late
updateCurrentProfilesCache();
- if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
- // Set up
- mBackgroundExecutor.execute(() -> {
- @SuppressLint("MissingPermission") List<UserInfo> users = mUserManager.getUsers();
- for (int i = users.size() - 1; i >= 0; i--) {
- initValuesForUser(users.get(i).id);
- }
- });
- } else {
- mSettingsObserver.onChange(false); // set up
- }
+ // Set up
+ mBackgroundExecutor.execute(() -> {
+ @SuppressLint("MissingPermission") List<UserInfo> users = mUserManager.getUsers();
+ for (int i = users.size() - 1; i >= 0; i--) {
+ initValuesForUser(users.get(i).id);
+ }
+ });
}
private void initValuesForUser(@UserIdInt int userId) {
@@ -519,26 +481,15 @@ public class NotificationLockscreenUserManagerImpl implements
boolean show;
boolean allowedByDpm;
- if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
- if (keyguardPrivateNotifications()) {
- show = mUsersUsersAllowingNotifications.get(mCurrentUserId);
- } else {
- show = mUsersUsersAllowingNotifications.get(mCurrentUserId)
- && mKeyguardAllowingNotifications;
- }
- // If DPC never notified us about a user, that means they have no policy for the user,
- // and they allow the behavior
- allowedByDpm = mUsersDpcAllowingNotifications.get(mCurrentUserId, true);
+ if (keyguardPrivateNotifications()) {
+ show = mUsersUsersAllowingNotifications.get(mCurrentUserId);
} else {
- show = mSecureSettings.getIntForUser(
- LOCK_SCREEN_SHOW_NOTIFICATIONS,
- 1,
- mCurrentUserId) != 0;
- final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(
- null /* admin */, mCurrentUserId);
- allowedByDpm = (dpmFlags
- & KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0;
+ show = mUsersUsersAllowingNotifications.get(mCurrentUserId)
+ && mKeyguardAllowingNotifications;
}
+ // If DPC never notified us about a user, that means they have no policy for the user,
+ // and they allow the behavior
+ allowedByDpm = mUsersDpcAllowingNotifications.get(mCurrentUserId, true);
final boolean oldValue = mShowLockscreenNotifications;
setShowLockscreenNotifications(show && allowedByDpm);
@@ -600,42 +551,24 @@ public class NotificationLockscreenUserManagerImpl implements
* when the lockscreen is in "public" (secure & locked) mode?
*/
public boolean userAllowsPrivateNotificationsInPublic(int userHandle) {
- if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
- if (userHandle == USER_ALL) {
- userHandle = mCurrentUserId;
- }
- if (mUsersUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
- Log.i(TAG, "Asking for redact notifs setting too early", new Throwable());
- return false;
- }
- if (mUsersDpcAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
- Log.i(TAG, "Asking for redact notifs dpm override too early", new Throwable());
- return false;
- }
- if (keyguardPrivateNotifications()) {
- return mUsersUsersAllowingPrivateNotifications.get(userHandle)
- && mUsersDpcAllowingPrivateNotifications.get(userHandle)
- && mKeyguardAllowingNotifications;
- } else {
- return mUsersUsersAllowingPrivateNotifications.get(userHandle)
- && mUsersDpcAllowingPrivateNotifications.get(userHandle);
- }
+ if (userHandle == USER_ALL) {
+ userHandle = mCurrentUserId;
+ }
+ if (mUsersUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
+ Log.i(TAG, "Asking for redact notifs setting too early", new Throwable());
+ return false;
+ }
+ if (mUsersDpcAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
+ Log.i(TAG, "Asking for redact notifs dpm override too early", new Throwable());
+ return false;
+ }
+ if (keyguardPrivateNotifications()) {
+ return mUsersUsersAllowingPrivateNotifications.get(userHandle)
+ && mUsersDpcAllowingPrivateNotifications.get(userHandle)
+ && mKeyguardAllowingNotifications;
} else {
- if (userHandle == USER_ALL) {
- return true;
- }
-
- if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
- final boolean allowedByUser = 0 != mSecureSettings.getIntForUser(
- LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle);
- final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle,
- KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
- final boolean allowed = allowedByUser && allowedByDpm;
- mUsersAllowingPrivateNotifications.append(userHandle, allowed);
- return allowed;
- }
-
- return mUsersAllowingPrivateNotifications.get(userHandle);
+ return mUsersUsersAllowingPrivateNotifications.get(userHandle)
+ && mUsersDpcAllowingPrivateNotifications.get(userHandle);
}
}
@@ -688,48 +621,30 @@ public class NotificationLockscreenUserManagerImpl implements
* "public" (secure & locked) mode?
*/
public boolean userAllowsNotificationsInPublic(int userHandle) {
- if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
- // Unlike 'show private', settings does not show a copy of this setting for each
- // profile, so it inherits from the parent user.
- if (userHandle == USER_ALL || mCurrentManagedProfiles.contains(userHandle)) {
- userHandle = mCurrentUserId;
- }
- if (mUsersUsersAllowingNotifications.indexOfKey(userHandle) < 0) {
- // TODO(b/301955929): STOP_SHIP (stop flag flip): remove this read and use a safe
- // default value before moving to 'released'
- Log.wtf(TAG, "Asking for show notifs setting too early", new Throwable());
- updateUserShowSettings(userHandle);
- }
- if (mUsersDpcAllowingNotifications.indexOfKey(userHandle) < 0) {
- // TODO(b/301955929): STOP_SHIP (stop flag flip): remove this read and use a safe
- // default value before moving to 'released'
- Log.wtf(TAG, "Asking for show notifs dpm override too early", new Throwable());
- updateDpcSettings(userHandle);
- }
- if (keyguardPrivateNotifications()) {
- return mUsersUsersAllowingNotifications.get(userHandle)
- && mUsersDpcAllowingNotifications.get(userHandle);
- } else {
- return mUsersUsersAllowingNotifications.get(userHandle)
- && mUsersDpcAllowingNotifications.get(userHandle)
- && mKeyguardAllowingNotifications;
- }
+ // Unlike 'show private', settings does not show a copy of this setting for each
+ // profile, so it inherits from the parent user.
+ if (userHandle == USER_ALL || mCurrentManagedProfiles.contains(userHandle)) {
+ userHandle = mCurrentUserId;
+ }
+ if (mUsersUsersAllowingNotifications.indexOfKey(userHandle) < 0) {
+ // TODO(b/301955929): STOP_SHIP (stop flag flip): remove this read and use a safe
+ // default value before moving to 'released'
+ Log.wtf(TAG, "Asking for show notifs setting too early", new Throwable());
+ updateUserShowSettings(userHandle);
+ }
+ if (mUsersDpcAllowingNotifications.indexOfKey(userHandle) < 0) {
+ // TODO(b/301955929): STOP_SHIP (stop flag flip): remove this read and use a safe
+ // default value before moving to 'released'
+ Log.wtf(TAG, "Asking for show notifs dpm override too early", new Throwable());
+ updateDpcSettings(userHandle);
+ }
+ if (keyguardPrivateNotifications()) {
+ return mUsersUsersAllowingNotifications.get(userHandle)
+ && mUsersDpcAllowingNotifications.get(userHandle);
} else {
- if (isCurrentProfile(userHandle) && userHandle != mCurrentUserId) {
- return true;
- }
-
- if (mUsersAllowingNotifications.indexOfKey(userHandle) < 0) {
- final boolean allowedByUser = 0 != mSecureSettings.getIntForUser(
- LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, userHandle);
- final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle,
- KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
- final boolean allowedBySystem = mKeyguardManager.getPrivateNotificationsAllowed();
- final boolean allowed = allowedByUser && allowedByDpm && allowedBySystem;
- mUsersAllowingNotifications.append(userHandle, allowed);
- return allowed;
- }
- return mUsersAllowingNotifications.get(userHandle);
+ return mUsersUsersAllowingNotifications.get(userHandle)
+ && mUsersDpcAllowingNotifications.get(userHandle)
+ && mKeyguardAllowingNotifications;
}
}
@@ -749,7 +664,7 @@ public class NotificationLockscreenUserManagerImpl implements
|| isNotifUserRedacted;
boolean notificationRequestsRedaction =
- ent.getSbn().getNotification().visibility == Notification.VISIBILITY_PRIVATE;
+ ent.isNotificationVisibilityPrivate();
boolean userForcesRedaction = packageHasVisibilityOverride(ent.getSbn().getKey());
if (keyguardPrivateNotifications()) {
@@ -766,15 +681,7 @@ public class NotificationLockscreenUserManagerImpl implements
return true;
}
NotificationEntry entry = mCommonNotifCollectionLazy.get().getEntry(key);
- if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
- return entry != null && entry.getRanking().getChannel() != null
- && entry.getRanking().getChannel().getLockscreenVisibility()
- == Notification.VISIBILITY_PRIVATE;
- } else {
- return entry != null
- && entry.getRanking().getLockscreenVisibilityOverride()
- == Notification.VISIBILITY_PRIVATE;
- }
+ return entry != null && entry.isChannelVisibilityPrivate();
}
@SuppressLint("MissingPermission")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 9c4625e91110..d465973a5d12 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -30,9 +30,9 @@ import android.util.Log;
import com.android.systemui.Dumpable;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.media.controls.models.player.MediaData;
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData;
-import com.android.systemui.media.controls.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.shared.model.MediaData;
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData;
import com.android.systemui.statusbar.dagger.CentralSurfacesModule;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 0e0f15237185..615534809c97 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar;
import static com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress;
+import static com.android.systemui.util.ColorUtilKt.hexColorString;
import android.content.Context;
import android.content.res.Configuration;
@@ -42,6 +43,7 @@ import com.android.systemui.flags.Flags;
import com.android.systemui.flags.RefactorFlag;
import com.android.systemui.res.R;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
+import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.SourceType;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
@@ -187,8 +189,8 @@ public class NotificationShelf extends ActivatableNotificationView {
@Override
public String toString() {
- return "NotificationShelf"
- + "(hideBackground=" + mHideBackground
+ return super.toString()
+ + " (hideBackground=" + mHideBackground
+ " notGoneIndex=" + mNotGoneIndex
+ " hasItemsInStableShelf=" + mHasItemsInStableShelf
+ " interactive=" + mInteractive
@@ -368,6 +370,17 @@ public class NotificationShelf extends ActivatableNotificationView {
&& isYInView(localY, slop, top, bottom);
}
+ @Override
+ public void updateBackgroundColors() {
+ super.updateBackgroundColors();
+ ColorUpdateLogger colorUpdateLogger = ColorUpdateLogger.getInstance();
+ if (colorUpdateLogger != null) {
+ colorUpdateLogger.logEvent("Shelf.updateBackgroundColors()",
+ "normalBgColor=" + hexColorString(getNormalBgColor())
+ + " background=" + mBackgroundNormal.toDumpString());
+ }
+ }
+
/**
* Update the shelf appearance based on the other notifications around it. This transforms
* the icons from the notification area into the shelf.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt b/packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt
index e47c914341a6..612a365dbe8b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt
@@ -27,7 +27,7 @@ constructor(
private val context: Context,
private val scrimController: ScrimController,
private val statusBarStateController: SysuiStatusBarStateController,
- @Assisted private val qSProvider: () -> QS,
+ @Assisted private val qSProvider: () -> QS?,
@Assisted private val nsslControllerProvider: () -> NotificationStackScrollLayoutController
) : LockScreenShadeOverScroller {
@@ -37,7 +37,7 @@ constructor(
private var maxOverScrollAmount = 0
private var previousOverscrollAmount = 0
- private val qS: QS
+ private val qS: QS?
get() = qSProvider()
private val nsslController: NotificationStackScrollLayoutController
@@ -90,7 +90,7 @@ constructor(
}
private fun applyOverscroll(overscrollAmount: Int) {
- qS.setOverScrollAmount(overscrollAmount)
+ qS?.setOverScrollAmount(overscrollAmount)
scrimController.setNotificationsOverScrollAmount(overscrollAmount)
nsslController.setOverScrollAmount(overscrollAmount)
}
@@ -109,7 +109,7 @@ constructor(
val animator = ValueAnimator.ofInt(previousOverscrollAmount, 0)
animator.addUpdateListener {
val overScrollAmount = it.animatedValue as Int
- qS.setOverScrollAmount(overScrollAmount)
+ qS?.setOverScrollAmount(overScrollAmount)
scrimController.setNotificationsOverScrollAmount(overScrollAmount)
nsslController.setOverScrollAmount(overScrollAmount)
}
@@ -143,7 +143,7 @@ constructor(
@AssistedFactory
fun interface Factory {
fun create(
- qSProvider: () -> QS,
+ qSProvider: () -> QS?,
nsslControllerProvider: () -> NotificationStackScrollLayoutController
): SplitShadeLockScreenOverScroller
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 537f8a866fed..36fc9bb3a2da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_FROM_AOD;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_TO_AOD;
+import static com.android.systemui.util.kotlin.JavaAdapterKt.combineFlows;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -43,19 +44,26 @@ import com.android.internal.logging.UiEventLogger;
import com.android.keyguard.KeyguardClockSwitch;
import com.android.systemui.DejankUtils;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.res.R;
+import com.android.systemui.scene.domain.interactor.SceneInteractor;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
+import com.android.systemui.scene.shared.model.SceneKey;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.policy.CallbackController;
import com.android.systemui.util.Compile;
import com.android.systemui.util.kotlin.JavaAdapter;
+import com.google.common.base.Preconditions;
+
import dagger.Lazy;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Comparator;
+import java.util.Map;
import javax.inject.Inject;
@@ -97,6 +105,8 @@ public class StatusBarStateControllerImpl implements
private final InteractionJankMonitor mInteractionJankMonitor;
private final JavaAdapter mJavaAdapter;
private final Lazy<ShadeInteractor> mShadeInteractorLazy;
+ private final Lazy<DeviceUnlockedInteractor> mDeviceUnlockedInteractorLazy;
+ private final Lazy<SceneInteractor> mSceneInteractorLazy;
private int mState;
private int mLastState;
private int mUpcomingState;
@@ -160,11 +170,15 @@ public class StatusBarStateControllerImpl implements
UiEventLogger uiEventLogger,
InteractionJankMonitor interactionJankMonitor,
JavaAdapter javaAdapter,
- Lazy<ShadeInteractor> shadeInteractorLazy) {
+ Lazy<ShadeInteractor> shadeInteractorLazy,
+ Lazy<DeviceUnlockedInteractor> deviceUnlockedInteractorLazy,
+ Lazy<SceneInteractor> sceneInteractorLazy) {
mUiEventLogger = uiEventLogger;
mInteractionJankMonitor = interactionJankMonitor;
mJavaAdapter = javaAdapter;
mShadeInteractorLazy = shadeInteractorLazy;
+ mDeviceUnlockedInteractorLazy = deviceUnlockedInteractorLazy;
+ mSceneInteractorLazy = sceneInteractorLazy;
for (int i = 0; i < HISTORY_SIZE; i++) {
mHistoricalRecords[i] = new HistoricalState();
}
@@ -174,6 +188,15 @@ public class StatusBarStateControllerImpl implements
public void start() {
mJavaAdapter.alwaysCollectFlow(mShadeInteractorLazy.get().isAnyExpanded(),
this::onShadeOrQsExpanded);
+
+ if (SceneContainerFlag.isEnabled()) {
+ mJavaAdapter.alwaysCollectFlow(
+ combineFlows(
+ mDeviceUnlockedInteractorLazy.get().isDeviceUnlocked(),
+ mSceneInteractorLazy.get().getCurrentScene(),
+ this::calculateStateFromSceneFramework),
+ this::onStatusBarStateChanged);
+ }
}
@Override
@@ -183,6 +206,10 @@ public class StatusBarStateControllerImpl implements
@Override
public boolean setState(int state, boolean force) {
+ if (SceneContainerFlag.isEnabled()) {
+ return false;
+ }
+
if (state > MAX_STATE || state < MIN_STATE) {
throw new IllegalArgumentException("Invalid state " + state);
}
@@ -194,6 +221,14 @@ public class StatusBarStateControllerImpl implements
return false;
}
+ updateStateAndNotifyListeners(state);
+ return true;
+ }
+
+ /**
+ * Updates the {@link StatusBarState} and notifies registered listeners, if needed.
+ */
+ private void updateStateAndNotifyListeners(int state) {
if (state != mUpcomingState) {
Log.d(TAG, "setState: requested state " + StatusBarState.toString(state)
+ "!= upcomingState: " + StatusBarState.toString(mUpcomingState) + ". "
@@ -229,15 +264,16 @@ public class StatusBarStateControllerImpl implements
}
DejankUtils.stopDetectingBlockingIpcs(tag);
}
-
- return true;
}
@Override
public void setUpcomingState(int nextState) {
+ if (SceneContainerFlag.isEnabled()) {
+ return;
+ }
+
recordHistoricalState(nextState /* newState */, mState /* lastState */, true);
updateUpcomingState(nextState);
-
}
private void updateUpcomingState(int upcomingState) {
@@ -468,7 +504,7 @@ public class StatusBarStateControllerImpl implements
@Override
public boolean goingToFullShade() {
- return mState == StatusBarState.SHADE && mLeaveOpenOnKeyguardHide;
+ return getState() == StatusBarState.SHADE && mLeaveOpenOnKeyguardHide;
}
@Override
@@ -599,6 +635,37 @@ public class StatusBarStateControllerImpl implements
state.mUpcoming = upcoming;
}
+ private int calculateStateFromSceneFramework(
+ boolean isDeviceUnlocked,
+ SceneKey currentScene) {
+ SceneContainerFlag.isUnexpectedlyInLegacyMode();
+
+ if (isDeviceUnlocked) {
+ return StatusBarState.SHADE;
+ } else {
+ return Preconditions.checkNotNull(sStatusBarStateByLockedSceneKey.get(currentScene));
+ }
+ }
+
+ /** Notifies that the {@link StatusBarState} has changed to the given new state. */
+ private void onStatusBarStateChanged(int newState) {
+ SceneContainerFlag.isUnexpectedlyInLegacyMode();
+
+ if (newState == mState) {
+ return;
+ }
+
+ updateStateAndNotifyListeners(newState);
+ }
+
+ private static final Map<SceneKey, Integer> sStatusBarStateByLockedSceneKey = Map.of(
+ SceneKey.Lockscreen.INSTANCE, StatusBarState.KEYGUARD,
+ SceneKey.Bouncer.INSTANCE, StatusBarState.KEYGUARD,
+ SceneKey.Communal.INSTANCE, StatusBarState.KEYGUARD,
+ SceneKey.Shade.INSTANCE, StatusBarState.SHADE_LOCKED,
+ SceneKey.QuickSettings.INSTANCE, StatusBarState.SHADE_LOCKED
+ );
+
/**
* For keeping track of our previous state to help with debugging
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
index fc84973c46bd..724b19c891aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
@@ -61,7 +61,6 @@ import com.android.settingslib.mobile.MobileStatusTracker.SubscriptionDefaults;
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.net.DataUsageController;
import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
@@ -72,7 +71,8 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.core.LogLevel;
import com.android.systemui.log.dagger.StatusBarNetworkControllerLog;
-import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
+import com.android.systemui.qs.tiles.dialog.InternetDialogManager;
+import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -83,8 +83,6 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceP
import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.util.CarrierConfigTracker;
-import dalvik.annotation.optimization.NeverCompile;
-
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -99,6 +97,7 @@ import java.util.stream.Collectors;
import javax.inject.Inject;
+import dalvik.annotation.optimization.NeverCompile;
import kotlin.Unit;
/** Platform implementation of the network controller. **/
@@ -201,7 +200,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
private boolean mUserSetup;
private boolean mSimDetected;
private boolean mForceCellularValidated;
- private InternetDialogFactory mInternetDialogFactory;
+ private InternetDialogManager mInternetDialogManager;
private Handler mMainHandler;
private ConfigurationController.ConfigurationListener mConfigurationListener =
@@ -245,7 +244,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
WifiStatusTrackerFactory trackerFactory,
MobileSignalControllerFactory mobileFactory,
@Main Handler handler,
- InternetDialogFactory internetDialogFactory,
+ InternetDialogManager internetDialogManager,
DumpManager dumpManager,
@StatusBarNetworkControllerLog LogBuffer logBuffer) {
this(context, connectivityManager,
@@ -272,7 +271,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
dumpManager,
logBuffer);
mReceiverHandler.post(mRegisterListeners);
- mInternetDialogFactory = internetDialogFactory;
+ mInternetDialogManager = internetDialogManager;
}
@VisibleForTesting
@@ -829,7 +828,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
mReceiverHandler.post(this::handleConfigurationChanged);
break;
case Settings.Panel.ACTION_INTERNET_CONNECTIVITY:
- mMainHandler.post(() -> mInternetDialogFactory.create(true,
+ mMainHandler.post(() -> mInternetDialogManager.create(true,
mAccessPoints.canConfigMobileData(), mAccessPoints.canConfigWifi(),
null /* view */));
break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index f6d99bdefb9f..f960fcafafc6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -33,7 +33,7 @@ import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpHandler;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.media.controls.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.shade.NotificationPanelViewController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
index 6429815bcb9f..11636bdf0f17 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
@@ -17,6 +17,8 @@
package com.android.systemui.statusbar.data.repository
import android.graphics.Rect
+import android.view.InsetsFlags
+import android.view.ViewDebug
import android.view.WindowInsets
import android.view.WindowInsetsController
import android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS
@@ -305,8 +307,8 @@ constructor(
letterboxDetails.isNotEmpty()
override fun dump(pw: PrintWriter, args: Array<out String>) {
- pw.println("originalStatusBarAttributes: ${_originalStatusBarAttributes.value}")
- pw.println("modifiedStatusBarAttributes: ${modifiedStatusBarAttributes.value}")
+ pw.println("${_originalStatusBarAttributes.value}")
+ pw.println("${modifiedStatusBarAttributes.value}")
pw.println("statusBarMode: ${statusBarMode.value}")
}
@@ -320,7 +322,20 @@ constructor(
val navbarColorManagedByIme: Boolean,
@WindowInsets.Type.InsetsType val requestedVisibleTypes: Int,
val letterboxDetails: List<LetterboxDetails>,
- )
+ ) {
+ override fun toString(): String {
+ return """
+ StatusBarAttributes(
+ appearance=${appearance.toAppearanceString()},
+ appearanceRegions=$appearanceRegions,
+ navbarColorManagedByIme=$navbarColorManagedByIme,
+ requestedVisibleTypes=${requestedVisibleTypes.toWindowInsetsString()},
+ letterboxDetails=$letterboxDetails
+ )
+ """
+ .trimIndent()
+ }
+ }
/**
* Internal class keeping track of how [StatusBarAttributes] were transformed into new
@@ -331,9 +346,31 @@ constructor(
val appearanceRegions: List<AppearanceRegion>,
val navbarColorManagedByIme: Boolean,
val statusBarBounds: BoundsPair,
- )
+ ) {
+ override fun toString(): String {
+ return """
+ ModifiedStatusBarAttributes(
+ appearance=${appearance.toAppearanceString()},
+ appearanceRegions=$appearanceRegions,
+ navbarColorManagedByIme=$navbarColorManagedByIme,
+ statusBarBounds=$statusBarBounds
+ )
+ """
+ .trimIndent()
+ }
+ }
}
+private fun @receiver:WindowInsets.Type.InsetsType Int.toWindowInsetsString() =
+ "[${WindowInsets.Type.toString(this).replace(" ", ", ")}]"
+
+private fun @receiver:Appearance Int.toAppearanceString() =
+ if (this == 0) {
+ "NONE"
+ } else {
+ ViewDebug.flagsToString(InsetsFlags::class.java, "appearance", this)
+ }
+
@AssistedFactory
interface StatusBarModePerDisplayRepositoryFactory {
fun create(@Assisted("displayId") displayId: Int): StatusBarModePerDisplayRepositoryImpl
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ColorUpdateLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ColorUpdateLogger.kt
new file mode 100644
index 000000000000..0f0ab2e36b8d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ColorUpdateLogger.kt
@@ -0,0 +1,153 @@
+/*
+ * 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.statusbar.notification
+
+import android.icu.text.SimpleDateFormat
+import android.util.IndentingPrintWriter
+import com.android.systemui.Dumpable
+import com.android.systemui.Flags.notificationColorUpdateLogger
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.util.Compile
+import com.android.systemui.util.asIndenting
+import com.android.systemui.util.printCollection
+import com.android.systemui.util.withIncreasedIndent
+import com.google.errorprone.annotations.CompileTimeConstant
+import java.io.PrintWriter
+import java.util.Locale
+import java.util.SortedSet
+import java.util.TreeSet
+import javax.inject.Inject
+
+@SysUISingleton
+class ColorUpdateLogger
+@Inject
+constructor(
+ val featureFlags: FeatureFlagsClassic,
+ dumpManager: DumpManager,
+) : Dumpable {
+
+ inline val isEnabled
+ get() = Compile.IS_DEBUG && notificationColorUpdateLogger()
+ private val frames: MutableList<Frame> = mutableListOf()
+
+ init {
+ dumpManager.registerDumpable(this)
+ if (isEnabled) {
+ instance = this
+ }
+ }
+
+ @JvmOverloads
+ fun logTriggerEvent(@CompileTimeConstant type: String, extra: String? = null) {
+ if (!isEnabled) return
+ val event = Event(type = type, extraValue = extra)
+ val didAppend = frames.lastOrNull()?.tryAddTrigger(event) == true
+ if (!didAppend) {
+ frames.add(Frame(event))
+ if (frames.size > maxFrames) frames.removeAt(0)
+ }
+ }
+
+ @JvmOverloads
+ fun logEvent(@CompileTimeConstant type: String, extra: String? = null) {
+ if (!isEnabled) return
+ val frame = frames.lastOrNull() ?: return
+ frame.events.add(Event(type = type, extraValue = extra))
+ frame.trim()
+ }
+
+ @JvmOverloads
+ fun logNotificationEvent(
+ @CompileTimeConstant type: String,
+ key: String,
+ extra: String? = null
+ ) {
+ if (!isEnabled) return
+ val frame = frames.lastOrNull() ?: return
+ frame.events.add(Event(type = type, extraValue = extra, notificationKey = key))
+ frame.trim()
+ }
+
+ override fun dump(pwOrig: PrintWriter, args: Array<out String>) {
+ val pw = pwOrig.asIndenting()
+ pw.println("enabled: $isEnabled")
+ pw.printCollection("frames", frames) { it.dump(pw) }
+ }
+
+ private class Frame(event: Event) {
+ val startTime: Long = event.time
+ val events: MutableList<Event> = mutableListOf(event)
+ val triggers: SortedSet<String> = TreeSet<String>().apply { add(event.type) }
+ var trimmedEvents: Int = 0
+
+ fun tryAddTrigger(newEvent: Event): Boolean {
+ if (newEvent.time < startTime) return false
+ if (newEvent.time - startTime > triggerStartsNewFrameAge) return false
+ if (newEvent.type in triggers) return false
+ triggers.add(newEvent.type)
+ events.add(newEvent)
+ trim()
+ return true
+ }
+
+ fun trim() {
+ if (events.size > maxEventsPerFrame) {
+ events.removeAt(0)
+ trimmedEvents++
+ }
+ }
+
+ fun dump(pw: IndentingPrintWriter) {
+ pw.println("Frame")
+ pw.withIncreasedIndent {
+ pw.println("startTime: ${timeString(startTime)}")
+ pw.printCollection("triggers", triggers)
+ pw.println("trimmedEvents: $trimmedEvents")
+ pw.printCollection("events", events) { it.dump(pw) }
+ }
+ }
+ }
+
+ private class Event(
+ @CompileTimeConstant val type: String,
+ val extraValue: String? = null,
+ val notificationKey: String? = null,
+ ) {
+ val time: Long = System.currentTimeMillis()
+
+ fun dump(pw: IndentingPrintWriter) {
+ pw.append(timeString(time)).append(": ").append(type)
+ extraValue?.let { pw.append(" ").append(it) }
+ notificationKey?.let { pw.append(" ---- ").append(logKey(it)) }
+ pw.println()
+ }
+ }
+
+ private companion object {
+ @JvmStatic
+ var instance: ColorUpdateLogger? = null
+ private set
+ private const val maxFrames = 5
+ private const val maxEventsPerFrame = 250
+ private const val triggerStartsNewFrameAge = 5000
+
+ private val dateFormat = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
+ private fun timeString(time: Long): String = dateFormat.format(time)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationContentDescription.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationContentDescription.kt
index 17fc5c60f74f..bdd9fd032800 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationContentDescription.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationContentDescription.kt
@@ -26,11 +26,11 @@ import com.android.systemui.res.R
/** Returns accessibility content description for a given notification. */
@MainThread
-fun contentDescForNotification(c: Context, n: Notification?): CharSequence {
- val appName = n?.loadHeaderAppName(c) ?: ""
- val title = n?.extras?.getCharSequence(Notification.EXTRA_TITLE)
- val text = n?.extras?.getCharSequence(Notification.EXTRA_TEXT)
- val ticker = n?.tickerText
+fun contentDescForNotification(c: Context, n: Notification): CharSequence {
+ val appName = n.loadHeaderAppName(c) ?: ""
+ val title = n.extras?.getCharSequence(Notification.EXTRA_TITLE)
+ val text = n.extras?.getCharSequence(Notification.EXTRA_TEXT)
+ val ticker = n.tickerText
// Some apps just put the app name into the title
val titleOrText = if (TextUtils.equals(title, appName)) text else title
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 8678f0aad181..e111525285e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -998,6 +998,23 @@ public final class NotificationEntry extends ListEntry {
return style == null ? "nostyle" : style.getSimpleName();
}
+ /**
+ * Return {@code true} if notification's visibility is {@link Notification.VISIBILITY_PRIVATE}
+ */
+ public boolean isNotificationVisibilityPrivate() {
+ return getSbn().getNotification().visibility == Notification.VISIBILITY_PRIVATE;
+ }
+
+ /**
+ * Return {@code true} if notification's channel lockscreen visibility is
+ * {@link Notification.VISIBILITY_PRIVATE}
+ */
+ public boolean isChannelVisibilityPrivate() {
+ return getRanking().getChannel() != null
+ && getRanking().getChannel().getLockscreenVisibility()
+ == Notification.VISIBILITY_PRIVATE;
+ }
+
/** Information about a suggestion that is being edited. */
public static class EditedSuggestionInfo {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
index 54b6ad71e734..fb67f7cc0b0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
@@ -104,7 +104,7 @@ class HeadsUpCoordinator @Inject constructor(
/**
* Once the pipeline starts running, we can look through posted entries and quickly process
- * any that don't have groups, and thus will never gave a group alert edge case.
+ * any that don't have groups, and thus will never gave a group heads up edge case.
*/
fun onBeforeTransformGroups(list: List<ListEntry>) {
mNow = mSystemClock.currentTimeMillis()
@@ -125,7 +125,7 @@ class HeadsUpCoordinator @Inject constructor(
/**
* Once we have a nearly final shade list (not including what's pruned for inflation reasons),
* we know that stability and [NotifPromoter]s have been applied, so we can use the location of
- * notifications in this list to determine what kind of group alert behavior should happen.
+ * notifications in this list to determine what kind of group heads up behavior should happen.
*/
fun onBeforeFinalizeFilter(list: List<ListEntry>) = mHeadsUpManager.modifyHuns { hunMutator ->
// Nothing to do if there are no other adds/updates
@@ -140,7 +140,7 @@ class HeadsUpCoordinator @Inject constructor(
.groupBy { it.sbn.groupKey }
val groupLocationsByKey: Map<String, GroupLocation> by lazy { getGroupLocationsByKey(list) }
mLogger.logEvaluatingGroups(postedEntriesByGroup.size)
- // For each group, determine which notification(s) for a group should alert.
+ // For each group, determine which notification(s) for a group should heads up.
postedEntriesByGroup.forEach { (groupKey, postedEntries) ->
// get and classify the logical members
val logicalMembers = logicalMembersByGroup[groupKey] ?: emptyList()
@@ -149,7 +149,7 @@ class HeadsUpCoordinator @Inject constructor(
// Report the start of this group's evaluation
mLogger.logEvaluatingGroup(groupKey, postedEntries.size, logicalMembers.size)
- // If there is no logical summary, then there is no alert to transfer
+ // If there is no logical summary, then there is no heads up to transfer
if (logicalSummary == null) {
postedEntries.forEach {
handlePostedEntry(it, hunMutator, scenario = "logical-summary-missing")
@@ -157,43 +157,43 @@ class HeadsUpCoordinator @Inject constructor(
return@forEach
}
- // If summary isn't wanted to be heads up, then there is no alert to transfer
+ // If summary isn't wanted to be heads up, then there is no heads up to transfer
if (!isGoingToShowHunStrict(logicalSummary)) {
postedEntries.forEach {
- handlePostedEntry(it, hunMutator, scenario = "logical-summary-not-alerting")
+ handlePostedEntry(it, hunMutator, scenario = "logical-summary-not-heads-up")
}
return@forEach
}
- // The group is alerting! Overall goals:
- // - Maybe transfer its alert to a child
- // - Also let any/all newly alerting children still alert
- var childToReceiveParentAlert: NotificationEntry?
+ // The group is heads up! Overall goals:
+ // - Maybe transfer its heads up to a child
+ // - Also let any/all newly heads up children still heads up
+ var childToReceiveParentHeadsUp: NotificationEntry?
var targetType = "undefined"
- // If the parent is alerting, always look at the posted notification with the newest
+ // If the parent is heads up, always look at the posted notification with the newest
// 'when', and if it is isolated with GROUP_ALERT_SUMMARY, then it should receive the
- // parent's alert.
- childToReceiveParentAlert =
- findAlertOverride(postedEntries, groupLocationsByKey::getLocation)
- if (childToReceiveParentAlert != null) {
- targetType = "alertOverride"
+ // parent's heads up.
+ childToReceiveParentHeadsUp =
+ findHeadsUpOverride(postedEntries, groupLocationsByKey::getLocation)
+ if (childToReceiveParentHeadsUp != null) {
+ targetType = "headsUpOverride"
}
- // If the summary is Detached and we have not picked a receiver of the alert, then we
- // need to look for the best child to alert in place of the summary.
+ // If the summary is Detached and we have not picked a receiver of the heads up, then we
+ // need to look for the best child to heads up in place of the summary.
val isSummaryAttached = groupLocationsByKey.contains(logicalSummary.key)
- if (!isSummaryAttached && childToReceiveParentAlert == null) {
- childToReceiveParentAlert =
+ if (!isSummaryAttached && childToReceiveParentHeadsUp == null) {
+ childToReceiveParentHeadsUp =
findBestTransferChild(logicalMembers, groupLocationsByKey::getLocation)
- if (childToReceiveParentAlert != null) {
+ if (childToReceiveParentHeadsUp != null) {
targetType = "bestChild"
}
}
- // If there is no child to receive the parent alert, then just handle the posted entries
- // and return.
- if (childToReceiveParentAlert == null) {
+ // If there is no child to receive the parent heads up, then just handle the posted
+ // entries and return.
+ if (childToReceiveParentHeadsUp == null) {
postedEntries.forEach {
handlePostedEntry(it, hunMutator, scenario = "no-transfer-target")
}
@@ -203,14 +203,14 @@ class HeadsUpCoordinator @Inject constructor(
// At this point we just need to initiate the transfer
val summaryUpdate = mPostedEntries[logicalSummary.key]
- // Because we now know for certain that some child is going to alert for this summary
- // (as we have found a child to transfer the alert to), mark the group as having
+ // Because we now know for certain that some child is going to heads up for this summary
+ // (as we have found a child to transfer the heads up to), mark the group as having
// interrupted. This will allow us to know in the future that the "should heads up"
// state of this group has already been handled, just not via the summary entry itself.
logicalSummary.setInterruption()
- mLogger.logSummaryMarkedInterrupted(logicalSummary.key, childToReceiveParentAlert.key)
+ mLogger.logSummaryMarkedInterrupted(logicalSummary.key, childToReceiveParentHeadsUp.key)
- // If the summary was not attached, then remove the alert from the detached summary.
+ // If the summary was not attached, then remove the heads up from the detached summary.
// Otherwise we can simply ignore its posted update.
if (!isSummaryAttached) {
val summaryUpdateForRemoval = summaryUpdate?.also {
@@ -221,60 +221,63 @@ class HeadsUpCoordinator @Inject constructor(
wasUpdated = false,
shouldHeadsUpEver = false,
shouldHeadsUpAgain = false,
- isAlerting = mHeadsUpManager.isHeadsUpEntry(logicalSummary.key),
+ isHeadsUpEntry = mHeadsUpManager.isHeadsUpEntry(logicalSummary.key),
isBinding = isEntryBinding(logicalSummary),
)
- // If we transfer the alert and the summary isn't even attached, that means we
- // should ensure the summary is no longer alerting, so we remove it here.
+ // If we transfer the heads up notification and the summary isn't even attached,
+ // that means we should ensure the summary is no longer a heads up notification,
+ // so we remove it here.
handlePostedEntry(
summaryUpdateForRemoval,
hunMutator,
- scenario = "detached-summary-remove-alert")
+ scenario = "detached-summary-remove-heads-up")
} else if (summaryUpdate != null) {
mLogger.logPostedEntryWillNotEvaluate(
summaryUpdate,
reason = "attached-summary-transferred")
}
- // Handle all posted entries -- if the child receiving the parent's alert is in the
- // list, then set its flags to ensure it alerts.
- var didAlertChildToReceiveParentAlert = false
+ // Handle all posted entries -- if the child receiving the parent's heads up is in the
+ // list, then set its flags to ensure it heads up.
+ var didHeadsUpChildToReceiveParentHeadsUp = false
postedEntries.asSequence()
.filter { it.key != logicalSummary.key }
.forEach { postedEntry ->
- if (childToReceiveParentAlert.key == postedEntry.key) {
+ if (childToReceiveParentHeadsUp.key == postedEntry.key) {
// Update the child's posted update so that it
postedEntry.shouldHeadsUpEver = true
postedEntry.shouldHeadsUpAgain = true
handlePostedEntry(
postedEntry,
hunMutator,
- scenario = "child-alert-transfer-target-$targetType")
- didAlertChildToReceiveParentAlert = true
+ scenario = "child-heads-up-transfer-target-$targetType")
+ didHeadsUpChildToReceiveParentHeadsUp = true
} else {
handlePostedEntry(
postedEntry,
hunMutator,
- scenario = "child-alert-non-target")
+ scenario = "child-heads-up-non-target")
}
}
- // If the child receiving the alert was not updated on this tick (which can happen in a
- // standard alert transfer scenario), then construct an update so that we can apply it.
- if (!didAlertChildToReceiveParentAlert) {
+ // If the child receiving the heads up notification was not updated on this tick
+ // (which can happen in a standard heads up transfer scenario), then construct an update
+ // so that we can apply it.
+ if (!didHeadsUpChildToReceiveParentHeadsUp) {
val posted = PostedEntry(
- childToReceiveParentAlert,
+ childToReceiveParentHeadsUp,
wasAdded = false,
wasUpdated = false,
shouldHeadsUpEver = true,
shouldHeadsUpAgain = true,
- isAlerting = mHeadsUpManager.isHeadsUpEntry(childToReceiveParentAlert.key),
- isBinding = isEntryBinding(childToReceiveParentAlert),
+ isHeadsUpEntry =
+ mHeadsUpManager.isHeadsUpEntry(childToReceiveParentHeadsUp.key),
+ isBinding = isEntryBinding(childToReceiveParentHeadsUp),
)
handlePostedEntry(
posted,
hunMutator,
- scenario = "non-posted-child-alert-transfer-target-$targetType")
+ scenario = "non-posted-child-heads-up-transfer-target-$targetType")
}
}
// After this method runs, all posted entries should have been handled (or skipped).
@@ -286,9 +289,9 @@ class HeadsUpCoordinator @Inject constructor(
/**
* Find the posted child with the newest when, and return it if it is isolated and has
- * GROUP_ALERT_SUMMARY so that it can be alerted.
+ * GROUP_ALERT_SUMMARY so that it can be heads uped.
*/
- private fun findAlertOverride(
+ private fun findHeadsUpOverride(
postedEntries: List<PostedEntry>,
locationLookupByKey: (String) -> GroupLocation,
): NotificationEntry? = postedEntries.asSequence()
@@ -344,16 +347,17 @@ class HeadsUpCoordinator @Inject constructor(
}
} else {
if (posted.isHeadsUpAlready) {
- // NOTE: This might be because we're alerting (i.e. tracked by HeadsUpManager) OR
- // it could be because we're binding, and that will affect the next step.
+ // NOTE: This might be because we're showing heads up (i.e. tracked by
+ // HeadsUpManager) OR it could be because we're binding, and that will affect the
+ // next step.
if (posted.shouldHeadsUpEver) {
- // If alerting, we need to post an update. Otherwise we're still binding,
- // and we can just let that finish.
- if (posted.isAlerting) {
+ // If showing heads up, we need to post an update. Otherwise we're still
+ // binding, and we can just let that finish.
+ if (posted.isHeadsUpEntry) {
hunMutator.updateNotification(posted.key, posted.shouldHeadsUpAgain)
}
} else {
- if (posted.isAlerting) {
+ if (posted.isHeadsUpEntry) {
// We don't want this to be interrupting anymore, let's remove it
hunMutator.removeNotification(posted.key, false /*removeImmediately*/)
} else {
@@ -408,7 +412,7 @@ class HeadsUpCoordinator @Inject constructor(
wasUpdated = false,
shouldHeadsUpEver = shouldHeadsUpEver,
shouldHeadsUpAgain = true,
- isAlerting = false,
+ isHeadsUpEntry = false,
isBinding = false,
)
@@ -418,21 +422,21 @@ class HeadsUpCoordinator @Inject constructor(
/**
* Notification could've updated to be heads up or not heads up. Even if it did update to
- * heads up, if the notification specified that it only wants to alert once, don't heads
+ * heads up, if the notification specified that it only wants to heads up once, don't heads
* up again.
*/
override fun onEntryUpdated(entry: NotificationEntry) {
val shouldHeadsUpEver =
mVisualInterruptionDecisionProvider.makeAndLogHeadsUpDecision(entry).shouldInterrupt
val shouldHeadsUpAgain = shouldHunAgain(entry)
- val isAlerting = mHeadsUpManager.isHeadsUpEntry(entry.key)
+ val isHeadsUpEntry = mHeadsUpManager.isHeadsUpEntry(entry.key)
val isBinding = isEntryBinding(entry)
val posted = mPostedEntries.compute(entry.key) { _, value ->
value?.also { update ->
update.wasUpdated = true
update.shouldHeadsUpEver = shouldHeadsUpEver
update.shouldHeadsUpAgain = update.shouldHeadsUpAgain || shouldHeadsUpAgain
- update.isAlerting = isAlerting
+ update.isHeadsUpEntry = isHeadsUpEntry
update.isBinding = isBinding
} ?: PostedEntry(
entry,
@@ -440,15 +444,15 @@ class HeadsUpCoordinator @Inject constructor(
wasUpdated = true,
shouldHeadsUpEver = shouldHeadsUpEver,
shouldHeadsUpAgain = shouldHeadsUpAgain,
- isAlerting = isAlerting,
+ isHeadsUpEntry = isHeadsUpEntry,
isBinding = isBinding,
)
}
- // Handle cancelling alerts here, rather than in the OnBeforeFinalizeFilter, so that
+ // Handle cancelling heads up here, rather than in the OnBeforeFinalizeFilter, so that
// work can be done before the ShadeListBuilder is run. This prevents re-entrant
// behavior between this Coordinator, HeadsUpManager, and VisualStabilityManager.
if (posted?.shouldHeadsUpEver == false) {
- if (posted.isAlerting) {
+ if (posted.isHeadsUpEntry) {
// We don't want this to be interrupting anymore, let's remove it
mHeadsUpManager.removeNotification(posted.key, false /*removeImmediately*/)
} else if (posted.isBinding) {
@@ -462,7 +466,7 @@ class HeadsUpCoordinator @Inject constructor(
}
/**
- * Stop alerting HUNs that are removed from the notification collection
+ * Stop showing as heads up once removed from the notification collection
*/
override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
mPostedEntries.remove(entry.key)
@@ -484,7 +488,7 @@ class HeadsUpCoordinator @Inject constructor(
/**
* Identify notifications whose heads-up state changes when the notification rankings are
- * updated, and have those changed notifications alert if necessary.
+ * updated, and have those changed notifications heads up if necessary.
*
* This method will occur after any operations in onEntryAdded or onEntryUpdated, so any
* handling of ranking changes needs to take into account that we may have just made a
@@ -492,7 +496,7 @@ class HeadsUpCoordinator @Inject constructor(
*/
override fun onRankingApplied() {
// Because a ranking update may cause some notifications that are no longer (or were
- // never) in mPostedEntries to need to alert, we need to check every notification
+ // never) in mPostedEntries to need to heads up, we need to check every notification
// known to the pipeline.
for (entry in mNotifPipeline.allNotifs) {
// Only consider entries that are recent enough, since we want to apply a fairly
@@ -500,9 +504,9 @@ class HeadsUpCoordinator @Inject constructor(
// app-provided notification update.
if (!isNewEnoughForRankingUpdate(entry)) continue
- // The only entries we consider alerting for here are entries that have never
- // interrupted and that now say they should heads up or FSI; if they've alerted in
- // the past, we don't want to incorrectly alert a second time if there wasn't an
+ // The only entries we consider heads up for here are entries that have never
+ // interrupted and that now say they should heads up or FSI; if they've heads uped in
+ // the past, we don't want to incorrectly heads up a second time if there wasn't an
// explicit notification update.
if (entry.hasInterrupted()) continue
@@ -561,7 +565,7 @@ class HeadsUpCoordinator @Inject constructor(
}
/**
- * Checks whether an update for a notification warrants an alert for the user.
+ * Checks whether an update for a notification warrants an heads up for the user.
*/
private fun shouldHunAgain(entry: NotificationEntry): Boolean {
return (!entry.hasInterrupted() ||
@@ -716,25 +720,25 @@ class HeadsUpCoordinator @Inject constructor(
}
/**
- * Whether the notification is already alerting or binding so that it can imminently alert
+ * Whether the notification is already heads up or binding so that it can imminently heads up
*/
private fun isAttemptingToShowHun(entry: ListEntry) =
mHeadsUpManager.isHeadsUpEntry(entry.key) || isEntryBinding(entry)
/**
- * Whether the notification is already alerting/binding per [isAttemptingToShowHun] OR if it
- * has been updated so that it should alert this update. This method is permissive because it
- * returns `true` even if the update would (in isolation of its group) cause the alert to be
- * retracted. This is important for not retracting transferred group alerts.
+ * Whether the notification is already heads up/binding per [isAttemptingToShowHun] OR if it
+ * has been updated so that it should heads up this update. This method is permissive because
+ * it returns `true` even if the update would (in isolation of its group) cause the heads up to
+ * be retracted. This is important for not retracting transferred group heads ups.
*/
private fun isGoingToShowHunNoRetract(entry: ListEntry) =
mPostedEntries[entry.key]?.calculateShouldBeHeadsUpNoRetract ?: isAttemptingToShowHun(entry)
/**
* If the notification has been updated, then whether it should HUN in isolation, otherwise
- * defers to the already alerting/binding state of [isAttemptingToShowHun]. This method is
- * strict because any update which would revoke the alert supersedes the current
- * alerting/binding state.
+ * defers to the already heads up/binding state of [isAttemptingToShowHun]. This method is
+ * strict because any update which would revoke the heads up supersedes the current
+ * heads up/binding state.
*/
private fun isGoingToShowHunStrict(entry: ListEntry) =
mPostedEntries[entry.key]?.calculateShouldBeHeadsUpStrict ?: isAttemptingToShowHun(entry)
@@ -760,12 +764,12 @@ class HeadsUpCoordinator @Inject constructor(
var wasUpdated: Boolean,
var shouldHeadsUpEver: Boolean,
var shouldHeadsUpAgain: Boolean,
- var isAlerting: Boolean,
+ var isHeadsUpEntry: Boolean,
var isBinding: Boolean,
) {
val key = entry.key
val isHeadsUpAlready: Boolean
- get() = isAlerting || isBinding
+ get() = isHeadsUpEntry || isBinding
val calculateShouldBeHeadsUpStrict: Boolean
get() = shouldHeadsUpEver && (wasAdded || shouldHeadsUpAgain || isHeadsUpAlready)
val calculateShouldBeHeadsUpNoRetract: Boolean
@@ -781,7 +785,7 @@ private fun Map<String, GroupLocation>.getLocation(key: String): GroupLocation =
/**
* Invokes the given block with a [HunMutator] that defers all HUN removals. This ensures that the
* HeadsUpManager is notified of additions before removals, which prevents a glitch where the
- * HeadsUpManager temporarily believes that nothing is alerting, causing bad re-entrant behavior.
+ * HeadsUpManager temporarily believes that nothing is heads up, causing bad re-entrant behavior.
*/
private fun <R> HeadsUpManager.modifyHuns(block: (HunMutator) -> R): R {
val mutator = HunMutatorImpl(this)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java
index 0be4bde749f3..072f56d2429d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java
@@ -16,13 +16,16 @@
package com.android.systemui.statusbar.notification.collection.coordinator;
-import static com.android.systemui.media.controls.pipeline.MediaDataManagerKt.isMediaNotification;
+import static com.android.systemui.media.controls.domain.pipeline.MediaDataManagerKt.isMediaNotification;
import android.os.RemoteException;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
+import androidx.annotation.NonNull;
+
import com.android.internal.statusbar.IStatusBarService;
+import com.android.systemui.Flags;
import com.android.systemui.media.controls.util.MediaFeatureFlag;
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -53,32 +56,13 @@ public class MediaCoordinator implements Coordinator {
private final NotifFilter mMediaFilter = new NotifFilter(TAG) {
@Override
- public boolean shouldFilterOut(NotificationEntry entry, long now) {
+ public boolean shouldFilterOut(@NonNull NotificationEntry entry, long now) {
if (!mIsMediaFeatureEnabled || !isMediaNotification(entry.getSbn())) {
return false;
}
- switch (mIconsState.getOrDefault(entry, STATE_ICONS_UNINFLATED)) {
- case STATE_ICONS_UNINFLATED:
- try {
- mIconManager.createIcons(entry);
- mIconsState.put(entry, STATE_ICONS_INFLATED);
- } catch (InflationException e) {
- reportInflationError(entry, e);
- mIconsState.put(entry, STATE_ICONS_ERROR);
- }
- break;
- case STATE_ICONS_INFLATED:
- try {
- mIconManager.updateIcons(entry);
- } catch (InflationException e) {
- reportInflationError(entry, e);
- mIconsState.put(entry, STATE_ICONS_ERROR);
- }
- break;
- case STATE_ICONS_ERROR:
- // do nothing
- break;
+ if (!Flags.notificationsBackgroundMediaIcons()) {
+ inflateOrUpdateIcons(entry);
}
return true;
@@ -87,24 +71,67 @@ public class MediaCoordinator implements Coordinator {
private final NotifCollectionListener mCollectionListener = new NotifCollectionListener() {
@Override
- public void onEntryInit(NotificationEntry entry) {
- mIconsState.put(entry, STATE_ICONS_UNINFLATED);
+ public void onEntryInit(@NonNull NotificationEntry entry) {
+ // We default to STATE_ICONS_UNINFLATED anyway, so there's no need to initialize it.
+ if (!Flags.notificationsBackgroundMediaIcons()) {
+ mIconsState.put(entry, STATE_ICONS_UNINFLATED);
+ }
+ }
+
+ @Override
+ public void onEntryAdded(@NonNull NotificationEntry entry) {
+ if (Flags.notificationsBackgroundMediaIcons()) {
+ if (isMediaNotification(entry.getSbn())) {
+ inflateOrUpdateIcons(entry);
+ }
+ }
}
@Override
- public void onEntryUpdated(NotificationEntry entry) {
+ public void onEntryUpdated(@NonNull NotificationEntry entry) {
if (mIconsState.getOrDefault(entry, STATE_ICONS_UNINFLATED) == STATE_ICONS_ERROR) {
// The update may have fixed the inflation error, so give it another chance.
mIconsState.put(entry, STATE_ICONS_UNINFLATED);
}
+
+ if (Flags.notificationsBackgroundMediaIcons()) {
+ if (isMediaNotification(entry.getSbn())) {
+ inflateOrUpdateIcons(entry);
+ }
+ }
}
@Override
- public void onEntryCleanUp(NotificationEntry entry) {
+ public void onEntryCleanUp(@NonNull NotificationEntry entry) {
mIconsState.remove(entry);
}
};
+ private void inflateOrUpdateIcons(NotificationEntry entry) {
+ switch (mIconsState.getOrDefault(entry, STATE_ICONS_UNINFLATED)) {
+ case STATE_ICONS_UNINFLATED:
+ try {
+ mIconManager.createIcons(entry);
+ mIconsState.put(entry, STATE_ICONS_INFLATED);
+ } catch (InflationException e) {
+ reportInflationError(entry, e);
+ mIconsState.put(entry, STATE_ICONS_ERROR);
+ }
+ break;
+ case STATE_ICONS_INFLATED:
+ try {
+ mIconManager.updateIcons(entry);
+ } catch (InflationException e) {
+ reportInflationError(entry, e);
+ mIconsState.put(entry, STATE_ICONS_ERROR);
+ }
+ break;
+ case STATE_ICONS_ERROR:
+ // do nothing
+ break;
+ }
+ }
+
private void reportInflationError(NotificationEntry entry, Exception e) {
// This is the same logic as in PreparationCoordinator; it doesn't handle media
// notifications when the media feature is enabled since they aren't displayed in the shade,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
index bd659d294223..b9d1dde82eeb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
@@ -53,6 +53,7 @@ class NotifCoordinatorsImpl @Inject constructor(
mediaCoordinator: MediaCoordinator,
preparationCoordinator: PreparationCoordinator,
remoteInputCoordinator: RemoteInputCoordinator,
+ rowAlertTimeCoordinator: RowAlertTimeCoordinator,
rowAppearanceCoordinator: RowAppearanceCoordinator,
stackCoordinator: StackCoordinator,
shadeEventCoordinator: ShadeEventCoordinator,
@@ -69,9 +70,7 @@ class NotifCoordinatorsImpl @Inject constructor(
private val mCoordinators: MutableList<Coordinator> = ArrayList()
private val mOrderedSections: MutableList<NotifSectioner> = ArrayList()
- /**
- * Creates all the coordinators.
- */
+ /** Creates all the coordinators. */
init {
// Attach core coordinators.
mCoreCoordinators.add(dataStoreCoordinator)
@@ -89,6 +88,7 @@ class NotifCoordinatorsImpl @Inject constructor(
mCoordinators.add(groupCountCoordinator)
mCoordinators.add(groupWhenCoordinator)
mCoordinators.add(mediaCoordinator)
+ mCoordinators.add(rowAlertTimeCoordinator)
mCoordinators.add(rowAppearanceCoordinator)
mCoordinators.add(stackCoordinator)
mCoordinators.add(shadeEventCoordinator)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAlertTimeCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAlertTimeCoordinator.kt
new file mode 100644
index 000000000000..12de339871bb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAlertTimeCoordinator.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.notification.collection.coordinator
+
+import android.util.ArrayMap
+import com.android.systemui.statusbar.notification.collection.GroupEntry
+import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
+import com.android.systemui.statusbar.notification.collection.render.NotifRowController
+import javax.inject.Inject
+import kotlin.math.max
+
+/**
+ * A small coordinator which ensures the "alerted" bell shows not just for recently alerted entries,
+ * but also on the summary for every such entry.
+ */
+@CoordinatorScope
+class RowAlertTimeCoordinator @Inject constructor() : Coordinator {
+
+ private val latestAlertTimeBySummary = ArrayMap<NotificationEntry, Long>()
+
+ override fun attach(pipeline: NotifPipeline) {
+ pipeline.addOnBeforeFinalizeFilterListener(::onBeforeFinalizeFilterListener)
+ pipeline.addOnAfterRenderEntryListener(::onAfterRenderEntry)
+ }
+
+ private fun onBeforeFinalizeFilterListener(entries: List<ListEntry>) {
+ latestAlertTimeBySummary.clear()
+ entries.asSequence().filterIsInstance<GroupEntry>().forEach { groupEntry ->
+ val summary = checkNotNull(groupEntry.summary)
+ latestAlertTimeBySummary[summary] = groupEntry.calculateLatestAlertTime()
+ }
+ }
+
+ private fun onAfterRenderEntry(entry: NotificationEntry, controller: NotifRowController) {
+ // Show the "alerted" bell icon based on the latest group member for summaries
+ val lastAudiblyAlerted = latestAlertTimeBySummary[entry] ?: entry.lastAudiblyAlertedMs
+ controller.setLastAudibleMs(lastAudiblyAlerted)
+ }
+
+ private fun GroupEntry.calculateLatestAlertTime(): Long {
+ val lastChildAlertedTime = children.maxOf { it.lastAudiblyAlertedMs }
+ val summaryAlertedTime = checkNotNull(summary).lastAudiblyAlertedMs
+ return max(lastChildAlertedTime, summaryAlertedTime)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
index 94cc7e9d1a9f..df694bb67684 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
@@ -75,7 +75,5 @@ class RowAppearanceCoordinator @Inject internal constructor(
(mAutoExpandFirstNotification && entry == entryToExpand))
// Show/hide the feedback icon
controller.setFeedbackIcon(mAssistantFeedbackController.getFeedbackIcon(entry))
- // Show the "alerted" bell icon
- controller.setLastAudiblyAlertedMs(entry.lastAudiblyAlertedMs)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
index 3809ea0f58da..b8a959440312 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
@@ -23,6 +23,7 @@ import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener
+import com.android.systemui.statusbar.notification.ColorUpdateLogger
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
import com.android.systemui.statusbar.notification.row.NotificationGutsManager
@@ -41,7 +42,8 @@ class ViewConfigCoordinator @Inject internal constructor(
private val mConfigurationController: ConfigurationController,
private val mLockscreenUserManager: NotificationLockscreenUserManager,
private val mGutsManager: NotificationGutsManager,
- private val mKeyguardUpdateMonitor: KeyguardUpdateMonitor
+ private val mKeyguardUpdateMonitor: KeyguardUpdateMonitor,
+ private val colorUpdateLogger: ColorUpdateLogger,
) : Coordinator, ConfigurationController.ConfigurationListener {
private var mIsSwitchingUser = false
@@ -51,11 +53,13 @@ class ViewConfigCoordinator @Inject internal constructor(
private val mKeyguardUpdateCallback = object : KeyguardUpdateMonitorCallback() {
override fun onUserSwitching(userId: Int) {
+ colorUpdateLogger.logTriggerEvent("VCC.mKeyguardUpdateCallback.onUserSwitching()")
log { "ViewConfigCoordinator.onUserSwitching(userId=$userId)" }
mIsSwitchingUser = true
}
override fun onUserSwitchComplete(userId: Int) {
+ colorUpdateLogger.logTriggerEvent("VCC.mKeyguardUpdateCallback.onUserSwitchComplete()")
log { "ViewConfigCoordinator.onUserSwitchComplete(userId=$userId)" }
mIsSwitchingUser = false
applyChangesOnUserSwitched()
@@ -64,6 +68,7 @@ class ViewConfigCoordinator @Inject internal constructor(
private val mUserChangedListener = object : UserChangedListener {
override fun onUserChanged(userId: Int) {
+ colorUpdateLogger.logTriggerEvent("VCC.mUserChangedListener.onUserChanged()")
log { "ViewConfigCoordinator.onUserChanged(userId=$userId)" }
applyChangesOnUserSwitched()
}
@@ -77,6 +82,7 @@ class ViewConfigCoordinator @Inject internal constructor(
}
override fun onDensityOrFontScaleChanged() {
+ colorUpdateLogger.logTriggerEvent("VCC.onDensityOrFontScaleChanged()")
log {
val keyguardIsSwitchingUser = mKeyguardUpdateMonitor.isSwitchingUser
"ViewConfigCoordinator.onDensityOrFontScaleChanged()" +
@@ -93,6 +99,7 @@ class ViewConfigCoordinator @Inject internal constructor(
}
override fun onUiModeChanged() {
+ colorUpdateLogger.logTriggerEvent("VCC.onUiModeChanged()")
log {
val keyguardIsSwitchingUser = mKeyguardUpdateMonitor.isSwitchingUser
"ViewConfigCoordinator.onUiModeChanged()" +
@@ -107,10 +114,12 @@ class ViewConfigCoordinator @Inject internal constructor(
}
override fun onThemeChanged() {
+ colorUpdateLogger.logTriggerEvent("VCC.onThemeChanged()")
onDensityOrFontScaleChanged()
}
private fun applyChangesOnUserSwitched() {
+ colorUpdateLogger.logEvent("VCC.applyChangesOnUserSwitched()")
if (mReinflateNotificationsOnUserSwitched) {
updateNotificationsOnDensityOrFontScaleChanged()
mReinflateNotificationsOnUserSwitched = false
@@ -122,6 +131,8 @@ class ViewConfigCoordinator @Inject internal constructor(
}
private fun updateNotificationsOnUiModeChanged() {
+ colorUpdateLogger.logEvent("VCC.updateNotificationsOnUiModeChanged()",
+ "mode=" + mConfigurationController.nightModeName)
log { "ViewConfigCoordinator.updateNotificationsOnUiModeChanged()" }
traceSection("updateNotifOnUiModeChanged") {
mPipeline?.allNotifs?.forEach { entry ->
@@ -131,6 +142,7 @@ class ViewConfigCoordinator @Inject internal constructor(
}
private fun updateNotificationsOnDensityOrFontScaleChanged() {
+ colorUpdateLogger.logEvent("VCC.updateNotificationsOnDensityOrFontScaleChanged()")
mPipeline?.allNotifs?.forEach { entry ->
entry.onDensityOrFontScaleChanged()
val exposedGuts = entry.areGutsExposed()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
index 8189fe03b2ed..dfe6cd5f25b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
@@ -25,6 +25,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -92,7 +93,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable {
@Inject
public VisualStabilityCoordinator(
- DelayableExecutor delayableExecutor,
+ @Background DelayableExecutor delayableExecutor,
DumpManager dumpManager,
HeadsUpManager headsUpManager,
ShadeAnimationInteractor shadeAnimationInteractor,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/LaunchFullScreenIntentProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/LaunchFullScreenIntentProvider.kt
index 74ff78e25a2f..e8c59f44da0e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/LaunchFullScreenIntentProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/LaunchFullScreenIntentProvider.kt
@@ -33,7 +33,7 @@ class LaunchFullScreenIntentProvider @Inject constructor() {
private val listeners = ListenerSet<Listener>()
/**
- * Registers a listener with this provider. These listeners will be alerted whenever a full
+ * Registers a listener with this provider. These listeners will be updated whenever a full
* screen intent should be launched for a notification entry.
*/
fun registerListener(listener: Listener) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifRowController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifRowController.kt
index 5ee94ba1624c..82b7e14adcc9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifRowController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifRowController.kt
@@ -29,10 +29,10 @@ interface NotifRowController {
fun setSystemExpanded(systemExpanded: Boolean)
/**
- * Sets the timestamp that the notification was last audibly alerted, which the row uses to
+ * Sets the timestamp that the notification was last audible, which the row uses to
* show a bell icon in the header which indicates to the user which notification made a noise.
*/
- fun setLastAudiblyAlertedMs(lastAudiblyAlertedMs: Long)
+ fun setLastAudibleMs(lastAudibleMs: Long)
/** Shows the given feedback icon, or hides the icon if null. */
fun setFeedbackIcon(icon: FeedbackIcon?)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 2a1ec3e9c64f..6548967c7462 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -16,12 +16,18 @@
package com.android.systemui.statusbar.notification.dagger;
+import android.app.NotificationManager;
import android.content.Context;
import android.service.notification.NotificationListenerService;
import com.android.internal.jank.InteractionJankMonitor;
+import com.android.settingslib.statusbar.notification.data.repository.NotificationsSoundPolicyRepository;
+import com.android.settingslib.statusbar.notification.data.repository.NotificationsSoundPolicyRepositoryImpl;
+import com.android.settingslib.statusbar.notification.domain.interactor.NotificationsSoundPolicyInteractor;
import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Application;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
@@ -79,13 +85,15 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
import com.android.systemui.statusbar.policy.HeadsUpManager;
+import javax.inject.Provider;
+
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.ClassKey;
import dagger.multibindings.IntoMap;
-
-import javax.inject.Provider;
+import kotlin.coroutines.CoroutineContext;
+import kotlinx.coroutines.CoroutineScope;
/**
* Dagger Module for classes found within the com.android.systemui.statusbar.notification package.
@@ -259,4 +267,22 @@ public interface NotificationsModule {
@ClassKey(VisualInterruptionDecisionProvider.class)
CoreStartable startVisualInterruptionDecisionProvider(
VisualInterruptionDecisionProvider provider);
+
+ @Provides
+ @SysUISingleton
+ public static NotificationsSoundPolicyRepository provideNotificationsSoundPolicyRepository(
+ Context context,
+ NotificationManager notificationManager,
+ @Application CoroutineScope coroutineScope,
+ @Background CoroutineContext coroutineContext) {
+ return new NotificationsSoundPolicyRepositoryImpl(context, notificationManager,
+ coroutineScope, coroutineContext);
+ }
+
+ @Provides
+ @SysUISingleton
+ public static NotificationsSoundPolicyInteractor provideNotificationsSoundPolicyInteractror(
+ NotificationsSoundPolicyRepository repository) {
+ return new NotificationsSoundPolicyInteractor(repository);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt
index b187cf15cccd..e5e5292d9a94 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/NotificationDataLayerModule.kt
@@ -15,6 +15,9 @@
*/
package com.android.systemui.statusbar.notification.data
+import com.android.systemui.statusbar.notification.data.repository.HeadsUpNotificationRepository
+import com.android.systemui.statusbar.notification.data.repository.HeadsUpNotificationRepositoryImpl
+import dagger.Binds
import dagger.Module
@Module(
@@ -23,4 +26,9 @@ import dagger.Module
NotificationSettingsRepositoryModule::class,
]
)
-interface NotificationDataLayerModule
+interface NotificationDataLayerModule {
+ @Binds
+ fun bindHeadsUpNotificationRepository(
+ impl: HeadsUpNotificationRepositoryImpl
+ ): HeadsUpNotificationRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpNotificationRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpNotificationRepository.kt
new file mode 100644
index 000000000000..d60ee9896758
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpNotificationRepository.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.data.repository
+
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.policy.HeadsUpManager
+import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
+import javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+
+class HeadsUpNotificationRepositoryImpl
+@Inject
+constructor(
+ headsUpManager: HeadsUpManager,
+) : HeadsUpNotificationRepository {
+ override val hasPinnedHeadsUp: Flow<Boolean> = conflatedCallbackFlow {
+ val listener =
+ object : OnHeadsUpChangedListener {
+ override fun onHeadsUpPinnedModeChanged(inPinnedMode: Boolean) {
+ trySend(headsUpManager.hasPinnedHeadsUp())
+ }
+
+ override fun onHeadsUpPinned(entry: NotificationEntry?) {
+ trySend(headsUpManager.hasPinnedHeadsUp())
+ }
+
+ override fun onHeadsUpUnPinned(entry: NotificationEntry?) {
+ trySend(headsUpManager.hasPinnedHeadsUp())
+ }
+
+ override fun onHeadsUpStateChanged(entry: NotificationEntry, isHeadsUp: Boolean) {
+ trySend(headsUpManager.hasPinnedHeadsUp())
+ }
+ }
+ trySend(headsUpManager.hasPinnedHeadsUp())
+ headsUpManager.addListener(listener)
+ awaitClose { headsUpManager.removeListener(listener) }
+ }
+}
+
+interface HeadsUpNotificationRepository {
+ val hasPinnedHeadsUp: Flow<Boolean>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt
new file mode 100644
index 000000000000..5c8f354de485
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.domain.interactor
+
+import com.android.systemui.statusbar.notification.data.repository.HeadsUpNotificationRepository
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+class HeadsUpNotificationInteractor @Inject constructor(repository: HeadsUpNotificationRepository) {
+ val isHeadsUpOrAnimatingAway: Flow<Boolean> =
+ // TODO(b/296118689): Needs to include the animating away state.
+ repository.hasPinnedHeadsUp
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
index 16f18a3c3fb6..f792898520a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.footer.ui.view;
import static android.graphics.PorterDuff.Mode.SRC_ATOP;
import static com.android.systemui.Flags.notificationBackgroundTintOptimization;
+import static com.android.systemui.util.ColorUtilKt.hexColorString;
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
@@ -40,11 +41,13 @@ import androidx.annotation.NonNull;
import com.android.settingslib.Utils;
import com.android.systemui.res.R;
+import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
import com.android.systemui.statusbar.notification.row.FooterViewButton;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.ViewState;
+import com.android.systemui.util.DrawableDumpKt;
import com.android.systemui.util.DumpUtilsKt;
import java.io.PrintWriter;
@@ -239,6 +242,10 @@ public class FooterView extends StackScrollerDecorView {
@Override
protected void onFinishInflate() {
+ ColorUpdateLogger colorUpdateLogger = ColorUpdateLogger.getInstance();
+ if (colorUpdateLogger != null) {
+ colorUpdateLogger.logTriggerEvent("Footer.onFinishInflate()");
+ }
super.onFinishInflate();
mClearAllButton = (FooterViewButton) findSecondaryView();
mManageOrHistoryButton = findViewById(R.id.manage_text);
@@ -348,6 +355,10 @@ public class FooterView extends StackScrollerDecorView {
@Override
protected void onConfigurationChanged(Configuration newConfig) {
+ ColorUpdateLogger colorUpdateLogger = ColorUpdateLogger.getInstance();
+ if (colorUpdateLogger != null) {
+ colorUpdateLogger.logTriggerEvent("Footer.onConfigurationChanged()");
+ }
super.onConfigurationChanged(newConfig);
updateColors();
if (!FooterViewRefactor.isEnabled()) {
@@ -365,14 +376,17 @@ public class FooterView extends StackScrollerDecorView {
com.android.internal.R.attr.materialColorOnSurface);
final Drawable clearAllBg = theme.getDrawable(R.drawable.notif_footer_btn_background);
final Drawable manageBg = theme.getDrawable(R.drawable.notif_footer_btn_background);
+ final @ColorInt int scHigh;
if (!notificationBackgroundTintOptimization()) {
- final @ColorInt int scHigh = Utils.getColorAttrDefaultColor(mContext,
+ scHigh = Utils.getColorAttrDefaultColor(mContext,
com.android.internal.R.attr.materialColorSurfaceContainerHigh);
if (scHigh != 0) {
final ColorFilter bgColorFilter = new PorterDuffColorFilter(scHigh, SRC_ATOP);
clearAllBg.setColorFilter(bgColorFilter);
manageBg.setColorFilter(bgColorFilter);
}
+ } else {
+ scHigh = 0;
}
mClearAllButton.setBackground(clearAllBg);
mClearAllButton.setTextColor(onSurface);
@@ -380,6 +394,13 @@ public class FooterView extends StackScrollerDecorView {
mManageOrHistoryButton.setTextColor(onSurface);
mSeenNotifsFooterTextView.setTextColor(onSurface);
mSeenNotifsFooterTextView.setCompoundDrawableTintList(ColorStateList.valueOf(onSurface));
+ ColorUpdateLogger colorUpdateLogger = ColorUpdateLogger.getInstance();
+ if (colorUpdateLogger != null) {
+ colorUpdateLogger.logEvent("Footer.updateColors()",
+ "textColor(onSurface)=" + hexColorString(onSurface)
+ + " backgroundTint(surfaceContainerHigh)=" + hexColorString(scHigh)
+ + " background=" + DrawableDumpKt.dumpToString(manageBg));
+ }
}
private void updateResources() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
index 9fb453afb55e..65ab4fdeb77b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
@@ -136,7 +136,7 @@ object FooterViewBinder {
}
launch {
- viewModel.clearAllButton.accessibilityDescriptionId.collect { textId ->
+ viewModel.manageOrHistoryButton.accessibilityDescriptionId.collect { textId ->
footer.setManageOrHistoryButtonDescription(textId)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
index 76e5fd3bd4f2..a5f42bb99e10 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
@@ -45,10 +45,12 @@ import javax.inject.Inject
* icons and keeping the icon assets themselves up to date as notifications change.
*
* TODO: Much of this code was copied whole-sale in order to get it out of NotificationEntry.
- * Long-term, it should probably live somewhere in the content inflation pipeline.
+ * Long-term, it should probably live somewhere in the content inflation pipeline.
*/
@SysUISingleton
-class IconManager @Inject constructor(
+class IconManager
+@Inject
+constructor(
private val notifCollection: CommonNotifCollection,
private val launcherApps: LauncherApps,
private val iconBuilder: IconBuilder
@@ -59,30 +61,30 @@ class IconManager @Inject constructor(
notifCollection.addCollectionListener(entryListener)
}
- private val entryListener = object : NotifCollectionListener {
- override fun onEntryInit(entry: NotificationEntry) {
- entry.addOnSensitivityChangedListener(sensitivityListener)
- }
+ private val entryListener =
+ object : NotifCollectionListener {
+ override fun onEntryInit(entry: NotificationEntry) {
+ entry.addOnSensitivityChangedListener(sensitivityListener)
+ }
- override fun onEntryCleanUp(entry: NotificationEntry) {
- entry.removeOnSensitivityChangedListener(sensitivityListener)
- }
+ override fun onEntryCleanUp(entry: NotificationEntry) {
+ entry.removeOnSensitivityChangedListener(sensitivityListener)
+ }
- override fun onRankingApplied() {
- // rankings affect whether a conversation is important, which can change the icons
- recalculateForImportantConversationChange()
+ override fun onRankingApplied() {
+ // rankings affect whether a conversation is important, which can change the icons
+ recalculateForImportantConversationChange()
+ }
}
- }
- private val sensitivityListener = NotificationEntry.OnSensitivityChangedListener {
- entry -> updateIconsSafe(entry)
- }
+ private val sensitivityListener =
+ NotificationEntry.OnSensitivityChangedListener { entry -> updateIconsSafe(entry) }
private fun recalculateForImportantConversationChange() {
for (entry in notifCollection.allNotifs) {
val isImportant = isImportantConversation(entry)
- if (entry.icons.areIconsAvailable &&
- isImportant != entry.icons.isImportantConversation
+ if (
+ entry.icons.areIconsAvailable && isImportant != entry.icons.isImportantConversation
) {
updateIconsSafe(entry)
}
@@ -97,34 +99,35 @@ class IconManager @Inject constructor(
* @throws InflationException Exception if required icons are not valid or specified
*/
@Throws(InflationException::class)
- fun createIcons(entry: NotificationEntry) = traceSection("IconManager.createIcons") {
- // Construct the status bar icon view.
- val sbIcon = iconBuilder.createIconView(entry)
- sbIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
-
- // Construct the shelf icon view.
- val shelfIcon = iconBuilder.createIconView(entry)
- shelfIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
- shelfIcon.visibility = View.INVISIBLE
-
- // Construct the aod icon view.
- val aodIcon = iconBuilder.createIconView(entry)
- aodIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
- aodIcon.setIncreasedSize(true)
-
- // Set the icon views' icons
- val (normalIconDescriptor, sensitiveIconDescriptor) = getIconDescriptors(entry)
-
- try {
- setIcon(entry, normalIconDescriptor, sbIcon)
- setIcon(entry, sensitiveIconDescriptor, shelfIcon)
- setIcon(entry, sensitiveIconDescriptor, aodIcon)
- entry.icons = IconPack.buildPack(sbIcon, shelfIcon, aodIcon, entry.icons)
- } catch (e: InflationException) {
- entry.icons = IconPack.buildEmptyPack(entry.icons)
- throw e
+ fun createIcons(entry: NotificationEntry) =
+ traceSection("IconManager.createIcons") {
+ // Construct the status bar icon view.
+ val sbIcon = iconBuilder.createIconView(entry)
+ sbIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
+
+ // Construct the shelf icon view.
+ val shelfIcon = iconBuilder.createIconView(entry)
+ shelfIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
+ shelfIcon.visibility = View.INVISIBLE
+
+ // Construct the aod icon view.
+ val aodIcon = iconBuilder.createIconView(entry)
+ aodIcon.scaleType = ImageView.ScaleType.CENTER_INSIDE
+ aodIcon.setIncreasedSize(true)
+
+ // Set the icon views' icons
+ val (normalIconDescriptor, sensitiveIconDescriptor) = getIconDescriptors(entry)
+
+ try {
+ setIcon(entry, normalIconDescriptor, sbIcon)
+ setIcon(entry, sensitiveIconDescriptor, shelfIcon)
+ setIcon(entry, sensitiveIconDescriptor, aodIcon)
+ entry.icons = IconPack.buildPack(sbIcon, shelfIcon, aodIcon, entry.icons)
+ } catch (e: InflationException) {
+ entry.icons = IconPack.buildEmptyPack(entry.icons)
+ throw e
+ }
}
- }
/**
* Update the notification icons.
@@ -133,33 +136,33 @@ class IconManager @Inject constructor(
* @throws InflationException Exception if required icons are not valid or specified
*/
@Throws(InflationException::class)
- fun updateIcons(entry: NotificationEntry) = traceSection("IconManager.updateIcons") {
- if (!entry.icons.areIconsAvailable) {
- return@traceSection
- }
- entry.icons.smallIconDescriptor = null
- entry.icons.peopleAvatarDescriptor = null
+ fun updateIcons(entry: NotificationEntry) =
+ traceSection("IconManager.updateIcons") {
+ if (!entry.icons.areIconsAvailable) {
+ return@traceSection
+ }
+ entry.icons.smallIconDescriptor = null
+ entry.icons.peopleAvatarDescriptor = null
- val (normalIconDescriptor, sensitiveIconDescriptor) = getIconDescriptors(entry)
- val notificationContentDescription = entry.sbn.notification?.let {
- iconBuilder.getIconContentDescription(it)
- }
+ val (normalIconDescriptor, sensitiveIconDescriptor) = getIconDescriptors(entry)
+ val notificationContentDescription =
+ entry.sbn.notification?.let { iconBuilder.getIconContentDescription(it) }
- entry.icons.statusBarIcon?.let {
- it.setNotification(entry.sbn, notificationContentDescription)
- setIcon(entry, normalIconDescriptor, it)
- }
+ entry.icons.statusBarIcon?.let {
+ it.setNotification(entry.sbn, notificationContentDescription)
+ setIcon(entry, normalIconDescriptor, it)
+ }
- entry.icons.shelfIcon?.let {
- it.setNotification(entry.sbn, notificationContentDescription)
- setIcon(entry, normalIconDescriptor, it)
- }
+ entry.icons.shelfIcon?.let {
+ it.setNotification(entry.sbn, notificationContentDescription)
+ setIcon(entry, sensitiveIconDescriptor, it)
+ }
- entry.icons.aodIcon?.let {
- it.setNotification(entry.sbn, notificationContentDescription)
- setIcon(entry, sensitiveIconDescriptor, it)
+ entry.icons.aodIcon?.let {
+ it.setNotification(entry.sbn, notificationContentDescription)
+ setIcon(entry, sensitiveIconDescriptor, it)
+ }
}
- }
private fun updateIconsSafe(entry: NotificationEntry) {
try {
@@ -173,11 +176,12 @@ class IconManager @Inject constructor(
@Throws(InflationException::class)
private fun getIconDescriptors(entry: NotificationEntry): Pair<StatusBarIcon, StatusBarIcon> {
val iconDescriptor = getIconDescriptor(entry, redact = false)
- val sensitiveDescriptor = if (entry.isSensitive) {
- getIconDescriptor(entry, redact = true)
- } else {
- iconDescriptor
- }
+ val sensitiveDescriptor =
+ if (entry.isSensitive) {
+ getIconDescriptor(entry, redact = true)
+ } else {
+ iconDescriptor
+ }
return Pair(iconDescriptor, sensitiveDescriptor)
}
@@ -197,14 +201,15 @@ class IconManager @Inject constructor(
}
val icon =
- (if (showPeopleAvatar) {
- createPeopleAvatar(entry)
- } else {
- n.smallIcon
- }) ?: throw InflationException(
- "No icon in notification from " + entry.sbn.packageName)
-
- val ic = StatusBarIcon(
+ (if (showPeopleAvatar) {
+ createPeopleAvatar(entry)
+ } else {
+ n.smallIcon
+ })
+ ?: throw InflationException("No icon in notification from " + entry.sbn.packageName)
+
+ val ic =
+ StatusBarIcon(
entry.sbn.user,
entry.sbn.packageName,
icon,
@@ -282,8 +287,8 @@ class IconManager @Inject constructor(
/**
* Determines if this icon shows a conversation based on the sensitivity of the icon, its
- * context and the user's indicated sensitivity preference. If we're using a fall back icon
- * of the small icon, we don't consider this to be showing a conversation
+ * context and the user's indicated sensitivity preference. If we're using a fall back icon of
+ * the small icon, we don't consider this to be showing a conversation
*
* @param iconView The icon that shows the conversation.
*/
@@ -293,19 +298,20 @@ class IconManager @Inject constructor(
iconDescriptor: StatusBarIcon
): Boolean {
val usedInSensitiveContext =
- iconView === entry.icons.shelfIcon || iconView === entry.icons.aodIcon
+ iconView === entry.icons.shelfIcon || iconView === entry.icons.aodIcon
val isSmallIcon = iconDescriptor.icon.equals(entry.sbn.notification.smallIcon)
- return isImportantConversation(entry) && !isSmallIcon &&
- (!usedInSensitiveContext || !entry.isSensitive)
+ return isImportantConversation(entry) &&
+ !isSmallIcon &&
+ (!usedInSensitiveContext || !entry.isSensitive)
}
private fun isImportantConversation(entry: NotificationEntry): Boolean {
// Also verify that the Notification is MessagingStyle, since we're going to access
// MessagingStyle-specific data (EXTRA_MESSAGES, EXTRA_MESSAGING_PERSON).
return entry.ranking.channel != null &&
- entry.ranking.channel.isImportantConversation &&
- entry.sbn.notification.isStyle(MessagingStyle::class.java) &&
- entry.key !in unimportantConversationKeys
+ entry.ranking.channel.isImportantConversation &&
+ entry.sbn.notification.isStyle(MessagingStyle::class.java) &&
+ entry.key !in unimportantConversationKeys
}
override fun setUnimportantConversations(keys: Collection<String>) {
@@ -323,8 +329,8 @@ private const val TAG = "IconManager"
interface ConversationIconManager {
/**
* Sets the complete current set of notification keys which should (for the purposes of icon
- * presentation) be considered unimportant. This tells the icon manager to remove the avatar
- * of a group from which the priority notification has been removed.
+ * presentation) be considered unimportant. This tells the icon manager to remove the avatar of
+ * a group from which the priority notification has been removed.
*/
fun setUnimportantConversations(keys: Collection<String>)
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
index d7fe36f75418..332ece428a6e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
@@ -219,15 +219,11 @@ class KeyguardNotificationVisibilityProviderImpl @Inject constructor(
}
private fun isRankingVisibilitySecret(entry: NotificationEntry): Boolean {
- return if (featureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
- // ranking.lockscreenVisibilityOverride contains possibly out of date DPC and Setting
- // info, and NotificationLockscreenUserManagerImpl is already listening for updates
- // to those
- entry.ranking.channel != null && entry.ranking.channel.lockscreenVisibility ==
+ // ranking.lockscreenVisibilityOverride contains possibly out of date DPC and Setting
+ // info, and NotificationLockscreenUserManagerImpl is already listening for updates
+ // to those
+ return entry.ranking.channel != null && entry.ranking.channel.lockscreenVisibility ==
VISIBILITY_SECRET
- } else {
- entry.ranking.lockscreenVisibilityOverride == VISIBILITY_SECRET
- }
}
override fun dump(pw: PrintWriter, args: Array<out String>) = pw.asIndenting().run {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index fca527f5fc4d..73580343fab7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -88,7 +88,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
private boolean mActivated;
private Interpolator mCurrentAppearInterpolator;
- NotificationBackgroundView mBackgroundNormal;
+ protected NotificationBackgroundView mBackgroundNormal;
private float mAnimationTranslationY;
private boolean mDrawingAppearAnimation;
private ValueAnimator mAppearAnimator;
@@ -142,6 +142,10 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
updateBackgroundTint();
}
+ protected int getNormalBgColor() {
+ return mNormalColor;
+ }
+
/**
* @param width The actual width to apply to the background view.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/CallLayoutSetDataAsyncFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/CallLayoutSetDataAsyncFactory.kt
deleted file mode 100644
index 4deebdb8de7d..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/CallLayoutSetDataAsyncFactory.kt
+++ /dev/null
@@ -1,37 +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.statusbar.notification.row
-
-import android.content.Context
-import android.util.AttributeSet
-import android.view.View
-import com.android.internal.widget.CallLayout
-import javax.inject.Inject
-
-class CallLayoutSetDataAsyncFactory @Inject constructor() : NotifRemoteViewsFactory {
- override fun instantiate(
- row: ExpandableNotificationRow,
- @NotificationRowContentBinder.InflationFlag layoutType: Int,
- parent: View?,
- name: String,
- context: Context,
- attrs: AttributeSet
- ): View? =
- if (name == CallLayout::class.java.name)
- CallLayout(context, attrs).apply { setSetDataAsyncEnabled(true) }
- else null
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index cc91ed394f48..decb244947ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -21,6 +21,7 @@ import static android.service.notification.NotificationListenerService.REASON_CA
import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.PARENT_DISMISSED;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
+import static com.android.systemui.util.ColorUtilKt.hexColorString;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -85,6 +86,7 @@ import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
+import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.FeedbackIcon;
import com.android.systemui.statusbar.notification.LaunchAnimationParameters;
import com.android.systemui.statusbar.notification.NotificationFadeAware;
@@ -172,6 +174,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private Optional<BubblesManager> mBubblesManagerOptional;
private MetricsLogger mMetricsLogger;
private NotificationChildrenContainerLogger mChildrenContainerLogger;
+ private ColorUpdateLogger mColorUpdateLogger;
private NotificationDismissibilityProvider mDismissibilityProvider;
private FeatureFlags mFeatureFlags;
private int mIconTransformContentShift;
@@ -445,6 +448,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
/**
* Sets animations running in the layouts of this row, including public, private, and children.
+ *
* @param running whether the animations should be started running or stopped.
*/
public void setAnimationRunning(boolean running) {
@@ -611,6 +615,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private void updateBackgroundColorsOfSelf() {
super.updateBackgroundColors();
+ if (mColorUpdateLogger.isEnabled()) {
+ mColorUpdateLogger.logNotificationEvent("ENR.updateBackgroundColorsOfSelf()",
+ mLoggingKey,
+ "normalBgColor=" + hexColorString(getNormalBgColor())
+ + " background=" + mBackgroundNormal.toDumpString());
+ }
}
@Override
@@ -839,6 +849,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
public void setNotificationGroupWhen(long whenMillis) {
if (mIsSummaryWithChildren) {
mChildrenContainer.setNotificationGroupWhen(whenMillis);
+ mPublicLayout.setNotificationWhen(whenMillis);
} else {
Log.w(TAG, "setNotificationGroupWhen( whenMillis: " + whenMillis + ")"
+ " mIsSummaryWithChildren: false"
@@ -1389,7 +1400,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
public void setContentBackground(int customBackgroundColor, boolean animate,
- NotificationContentView notificationContentView) {
+ NotificationContentView notificationContentView) {
if (getShowingLayout() == notificationContentView) {
setTintColor(customBackgroundColor, animate);
}
@@ -1458,7 +1469,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
/**
- * @return if this entry should be kept in its parent during removal.
+ * @return if this entry should be kept in its parent during removal.
*/
public boolean keepInParentForDismissAnimation() {
return mKeepInParentForDismissAnimation;
@@ -1769,6 +1780,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
NotificationDismissibilityProvider dismissibilityProvider,
MetricsLogger metricsLogger,
NotificationChildrenContainerLogger childrenContainerLogger,
+ ColorUpdateLogger colorUpdateLogger,
SmartReplyConstants smartReplyConstants,
SmartReplyController smartReplyController,
FeatureFlags featureFlags,
@@ -1807,6 +1819,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mNotificationGutsManager = gutsManager;
mMetricsLogger = metricsLogger;
mChildrenContainerLogger = childrenContainerLogger;
+ mColorUpdateLogger = colorUpdateLogger;
mDismissibilityProvider = dismissibilityProvider;
mFeatureFlags = featureFlags;
}
@@ -2265,7 +2278,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
public Animator getTranslateViewAnimator(final float leftTarget,
- AnimatorUpdateListener listener) {
+ AnimatorUpdateListener listener) {
if (mTranslateAnim != null) {
mTranslateAnim.cancel();
}
@@ -2664,6 +2677,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return getCollapsedHeight();
}
}
+
/**
* @return {@code true} if the notification can show it's heads up layout. This is mostly true
* except for legacy use cases.
@@ -2691,6 +2705,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
private void onAttachedChildrenCountChanged() {
+ final boolean wasSummary = mIsSummaryWithChildren;
mIsSummaryWithChildren = mChildrenContainer != null
&& mChildrenContainer.getNotificationChildCount() > 0;
if (mIsSummaryWithChildren) {
@@ -2701,6 +2716,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
isConversation());
}
}
+ if (!mIsSummaryWithChildren && wasSummary) {
+ // Reset the 'when' once the row stops being a summary
+ mPublicLayout.setNotificationWhen(mEntry.getSbn().getNotification().when);
+ }
getShowingLayout().updateBackgroundColor(false /* animate */);
mPrivateLayout.updateExpandButtons(isExpandable());
updateChildrenAppearance();
@@ -2833,7 +2852,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@Override
public void setHideSensitive(boolean hideSensitive, boolean animated, long delay,
- long duration) {
+ long duration) {
if (getVisibility() == GONE) {
// If we are GONE, the hideSensitive parameter will not be calculated and always be
// false, which is incorrect, let's wait until a real call comes in later.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index 0afdefabd4f0..e59829bf3c99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -41,6 +41,7 @@ import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.SmartReplyController;
+import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.FeedbackIcon;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider;
@@ -86,6 +87,7 @@ public class ExpandableNotificationRowController implements NotifViewController
private final SystemClock mClock;
private final String mAppName;
private final String mNotificationKey;
+ private final ColorUpdateLogger mColorUpdateLogger;
private final KeyguardBypassController mKeyguardBypassController;
private final GroupMembershipManager mGroupMembershipManager;
private final GroupExpansionManager mGroupExpansionManager;
@@ -200,6 +202,7 @@ public class ExpandableNotificationRowController implements NotifViewController
ActivatableNotificationViewController activatableNotificationViewController,
RemoteInputViewSubcomponent.Factory rivSubcomponentFactory,
MetricsLogger metricsLogger,
+ ColorUpdateLogger colorUpdateLogger,
NotificationRowLogger logBufferLogger,
NotificationChildrenContainerLogger childrenContainerLogger,
NotificationListContainer listContainer,
@@ -256,6 +259,7 @@ public class ExpandableNotificationRowController implements NotifViewController
mDragController = dragController;
mMetricsLogger = metricsLogger;
mChildrenContainerLogger = childrenContainerLogger;
+ mColorUpdateLogger = colorUpdateLogger;
mLogBufferLogger = logBufferLogger;
mSmartReplyConstants = smartReplyConstants;
mSmartReplyController = smartReplyController;
@@ -290,6 +294,7 @@ public class ExpandableNotificationRowController implements NotifViewController
mDismissibilityProvider,
mMetricsLogger,
mChildrenContainerLogger,
+ mColorUpdateLogger,
mSmartReplyConstants,
mSmartReplyController,
mFeatureFlags,
@@ -431,8 +436,8 @@ public class ExpandableNotificationRowController implements NotifViewController
}
@Override
- public void setLastAudiblyAlertedMs(long lastAudiblyAlertedMs) {
- mView.setLastAudiblyAlertedMs(lastAudiblyAlertedMs);
+ public void setLastAudibleMs(long lastAudibleMs) {
+ mView.setLastAudiblyAlertedMs(lastAudibleMs);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index ec8e5d730c36..ea9df9af8cff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.row;
+import static com.android.systemui.Flags.notificationColorUpdateLogger;
+
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.content.res.Configuration;
@@ -54,8 +56,8 @@ import java.util.List;
public abstract class ExpandableView extends FrameLayout implements Dumpable, Roundable {
private static final String TAG = "ExpandableView";
/** whether the dump() for this class should include verbose details */
- protected static final boolean DUMP_VERBOSE =
- Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE);
+ protected static final boolean DUMP_VERBOSE = Compile.IS_DEBUG
+ && (Log.isLoggable(TAG, Log.VERBOSE) || notificationColorUpdateLogger());
private RoundableState mRoundableState = null;
protected OnHeightChangedListener mOnHeightChangedListener;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt
index 195fe785b538..dab89c5235b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt
@@ -31,7 +31,6 @@ constructor(
featureFlags: FeatureFlags,
precomputedTextViewFactory: PrecomputedTextViewFactory,
bigPictureLayoutInflaterFactory: BigPictureLayoutInflaterFactory,
- callLayoutSetDataAsyncFactory: CallLayoutSetDataAsyncFactory,
optimizedLinearLayoutFactory: NotificationOptimizedLinearLayoutFactory
) : NotifRemoteViewsFactoryContainer {
override val factories: Set<NotifRemoteViewsFactory> = buildSet {
@@ -39,9 +38,6 @@ constructor(
if (featureFlags.isEnabled(Flags.BIGPICTURE_NOTIFICATION_LAZY_LOADING)) {
add(bigPictureLayoutInflaterFactory)
}
- if (featureFlags.isEnabled(Flags.CALL_LAYOUT_ASYNC_SET_DATA)) {
- add(callLayoutSetDataAsyncFactory)
- }
if (notifLinearlayoutOptimized()) {
add(optimizedLinearLayoutFactory)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index 7ea9b14353d3..ed3a38d3305b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -36,6 +36,7 @@ import com.android.internal.util.ContrastColorUtil;
import com.android.settingslib.Utils;
import com.android.systemui.Dumpable;
import com.android.systemui.res.R;
+import com.android.systemui.util.DrawableDumpKt;
import java.io.PrintWriter;
import java.util.Arrays;
@@ -333,6 +334,16 @@ public class NotificationBackgroundView extends View implements Dumpable {
pw.println("mActualHeight: " + mActualHeight);
pw.println("mTintColor: " + hexColorString(mTintColor));
pw.println("mRippleColor: " + hexColorString(mRippleColor));
- pw.println("mBackground: " + mBackground);
+ pw.println("mBackground: " + DrawableDumpKt.dumpToString(mBackground));
+ }
+
+ /** create a concise dump of this view's colors */
+ public String toDumpString() {
+ return "<NotificationBackgroundView"
+ + " tintColor=" + hexColorString(mTintColor)
+ + " rippleColor=" + hexColorString(mRippleColor)
+ + " bgColor=" + DrawableDumpKt.getSolidColor(mBackground)
+ + ">";
+
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 913d5f6d3848..e288e857bf4a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -43,7 +43,7 @@ import android.widget.RemoteViews;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.ImageMessageConsumer;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.NotifInflation;
import com.android.systemui.media.controls.util.MediaFeatureFlag;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.InflationTask;
@@ -82,7 +82,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
private final NotificationRemoteInputManager mRemoteInputManager;
private final NotifRemoteViewCache mRemoteViewCache;
private final ConversationNotificationProcessor mConversationProcessor;
- private final Executor mBgExecutor;
+ private final Executor mInflationExecutor;
private final SmartReplyStateInflater mSmartReplyStateInflater;
private final NotifLayoutInflaterFactory.Provider mNotifLayoutInflaterFactoryProvider;
private final NotificationContentInflaterLogger mLogger;
@@ -93,7 +93,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
NotificationRemoteInputManager remoteInputManager,
ConversationNotificationProcessor conversationProcessor,
MediaFeatureFlag mediaFeatureFlag,
- @Background Executor bgExecutor,
+ @NotifInflation Executor inflationExecutor,
SmartReplyStateInflater smartRepliesInflater,
NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider,
NotificationContentInflaterLogger logger) {
@@ -101,7 +101,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
mRemoteInputManager = remoteInputManager;
mConversationProcessor = conversationProcessor;
mIsMediaInQS = mediaFeatureFlag.getEnabled();
- mBgExecutor = bgExecutor;
+ mInflationExecutor = inflationExecutor;
mSmartReplyStateInflater = smartRepliesInflater;
mNotifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider;
mLogger = logger;
@@ -138,7 +138,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
cancelContentViewFrees(row, contentToBind);
AsyncInflationTask task = new AsyncInflationTask(
- mBgExecutor,
+ mInflationExecutor,
mInflateSynchronously,
/* reInflateFlags = */ contentToBind,
mRemoteViewCache,
@@ -157,7 +157,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
if (mInflateSynchronously) {
task.onPostExecute(task.doInBackground());
} else {
- task.executeOnExecutor(mBgExecutor);
+ task.executeOnExecutor(mInflationExecutor);
}
}
@@ -208,7 +208,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
}
apply(
- mBgExecutor,
+ mInflationExecutor,
inflateSynchronously,
result,
reInflateFlags,
@@ -416,7 +416,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
}
private static CancellationSignal apply(
- Executor bgExecutor,
+ Executor inflationExecutor,
boolean inflateSynchronously,
InflationProgress result,
@InflationFlag int reInflateFlags,
@@ -447,7 +447,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
}
};
logger.logAsyncTaskProgress(entry, "applying contracted view");
- applyRemoteView(bgExecutor, inflateSynchronously, result, reInflateFlags, flag,
+ applyRemoteView(inflationExecutor, inflateSynchronously, result, reInflateFlags, flag,
remoteViewCache, entry, row, isNewView, remoteViewClickHandler, callback,
privateLayout, privateLayout.getContractedChild(),
privateLayout.getVisibleWrapper(
@@ -474,11 +474,10 @@ public class NotificationContentInflater implements NotificationRowContentBinder
}
};
logger.logAsyncTaskProgress(entry, "applying expanded view");
- applyRemoteView(bgExecutor, inflateSynchronously, result, reInflateFlags, flag,
- remoteViewCache, entry, row, isNewView, remoteViewClickHandler,
+ applyRemoteView(inflationExecutor, inflateSynchronously, result, reInflateFlags,
+ flag, remoteViewCache, entry, row, isNewView, remoteViewClickHandler,
callback, privateLayout, privateLayout.getExpandedChild(),
- privateLayout.getVisibleWrapper(
- NotificationContentView.VISIBLE_TYPE_EXPANDED), runningInflations,
+ privateLayout.getVisibleWrapper(VISIBLE_TYPE_EXPANDED), runningInflations,
applyCallback, logger);
}
}
@@ -502,11 +501,10 @@ public class NotificationContentInflater implements NotificationRowContentBinder
}
};
logger.logAsyncTaskProgress(entry, "applying heads up view");
- applyRemoteView(bgExecutor, inflateSynchronously, result, reInflateFlags, flag,
- remoteViewCache, entry, row, isNewView, remoteViewClickHandler,
+ applyRemoteView(inflationExecutor, inflateSynchronously, result, reInflateFlags,
+ flag, remoteViewCache, entry, row, isNewView, remoteViewClickHandler,
callback, privateLayout, privateLayout.getHeadsUpChild(),
- privateLayout.getVisibleWrapper(
- VISIBLE_TYPE_HEADSUP), runningInflations,
+ privateLayout.getVisibleWrapper(VISIBLE_TYPE_HEADSUP), runningInflations,
applyCallback, logger);
}
}
@@ -529,7 +527,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
}
};
logger.logAsyncTaskProgress(entry, "applying public view");
- applyRemoteView(bgExecutor, inflateSynchronously, result, reInflateFlags, flag,
+ applyRemoteView(inflationExecutor, inflateSynchronously, result, reInflateFlags, flag,
remoteViewCache, entry, row, isNewView, remoteViewClickHandler, callback,
publicLayout, publicLayout.getContractedChild(),
publicLayout.getVisibleWrapper(NotificationContentView.VISIBLE_TYPE_CONTRACTED),
@@ -551,7 +549,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
@VisibleForTesting
static void applyRemoteView(
- Executor bgExecutor,
+ Executor inflationExecutor,
boolean inflateSynchronously,
final InflationProgress result,
final @InflationFlag int reInflateFlags,
@@ -655,14 +653,14 @@ public class NotificationContentInflater implements NotificationRowContentBinder
cancellationSignal = newContentView.applyAsync(
result.packageContext,
parentLayout,
- bgExecutor,
+ inflationExecutor,
listener,
remoteViewClickHandler);
} else {
cancellationSignal = newContentView.reapplyAsync(
result.packageContext,
existingView,
- bgExecutor,
+ inflationExecutor,
listener,
remoteViewClickHandler);
}
@@ -918,7 +916,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
private final boolean mUsesIncreasedHeadsUpHeight;
private final @InflationFlag int mReInflateFlags;
private final NotifRemoteViewCache mRemoteViewCache;
- private final Executor mBgExecutor;
+ private final Executor mInflationExecutor;
private ExpandableNotificationRow mRow;
private Exception mError;
private RemoteViews.InteractionHandler mRemoteViewClickHandler;
@@ -930,7 +928,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
private final NotificationContentInflaterLogger mLogger;
private AsyncInflationTask(
- Executor bgExecutor,
+ Executor inflationExecutor,
boolean inflateSynchronously,
@InflationFlag int reInflateFlags,
NotifRemoteViewCache cache,
@@ -948,7 +946,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
NotificationContentInflaterLogger logger) {
mEntry = entry;
mRow = row;
- mBgExecutor = bgExecutor;
+ mInflationExecutor = inflationExecutor;
mInflateSynchronously = inflateSynchronously;
mReInflateFlags = reInflateFlags;
mRemoteViewCache = cache;
@@ -1067,7 +1065,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
if (mError == null) {
// Logged in detail in apply.
mCancellationSignal = apply(
- mBgExecutor,
+ mInflationExecutor,
mInflateSynchronously,
result,
mReInflateFlags,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 402ea51bebb6..374248252d1f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -59,6 +59,7 @@ import com.android.systemui.statusbar.notification.collection.render.GroupMember
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationCustomViewWrapper;
+import com.android.systemui.statusbar.notification.row.wrapper.NotificationHeaderViewWrapper;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder;
@@ -2314,6 +2315,13 @@ public class NotificationContentView extends FrameLayout implements Notification
return false;
}
+ public void setNotificationWhen(long whenMillis) {
+ NotificationViewWrapper wrapper = getNotificationViewWrapper();
+ if (wrapper instanceof NotificationHeaderViewWrapper headerViewWrapper) {
+ headerViewWrapper.setNotificationWhen(whenMillis);
+ }
+ }
+
private static class RemoteInputViewData {
@Nullable RemoteInputView mView;
@Nullable RemoteInputViewController mController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 20fae88b6f33..c90aceef6934 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -263,8 +263,8 @@ public class AmbientState implements Dumpable {
return mStackHeight;
}
- /** Tracks the state from AlertingNotificationManager#hasNotifications() */
- private boolean mHasAlertEntries;
+ /** Tracks the state from HeadsUpManager#hasNotifications() */
+ private boolean mHasHeadsUpEntries;
@Inject
public AmbientState(
@@ -563,7 +563,7 @@ public class AmbientState implements Dumpable {
}
public boolean hasPulsingNotifications() {
- return mPulsing && mHasAlertEntries;
+ return mPulsing && mHasHeadsUpEntries;
}
public void setPulsing(boolean hasPulsing) {
@@ -716,8 +716,8 @@ public class AmbientState implements Dumpable {
return mAppearFraction;
}
- public void setHasAlertEntries(boolean hasAlertEntries) {
- mHasAlertEntries = hasAlertEntries;
+ public void setHasHeadsUpEntries(boolean hasHeadsUpEntries) {
+ mHasHeadsUpEntries = hasHeadsUpEntries;
}
public void setStackTopMargin(int stackTopMargin) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
index bae5baaf91ed..5551ab46262c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
@@ -22,6 +22,8 @@ import android.graphics.Canvas
import android.graphics.Path
import android.graphics.RectF
import android.util.AttributeSet
+import android.util.Log
+import com.android.systemui.Flags
import com.android.systemui.res.R
import com.android.systemui.statusbar.notification.row.ExpandableView
@@ -87,4 +89,63 @@ class MediaContainerView(context: Context, attrs: AttributeSet?) : ExpandableVie
) {
// No animation, it doesn't need it, this would be local
}
+
+ override fun setVisibility(visibility: Int) {
+ if (Flags.bindKeyguardMediaVisibility()) {
+ if (isVisibilityValid(visibility)) {
+ super.setVisibility(visibility)
+ }
+ } else {
+ super.setVisibility(visibility)
+ }
+
+ assertMediaContainerVisibility(visibility)
+ }
+
+ /**
+ * visibility should be aligned with MediaContainerView visibility on the keyguard.
+ */
+ private fun isVisibilityValid(visibility: Int): Boolean {
+ val currentViewState = viewState as? MediaContainerViewState ?: return true
+ val shouldBeGone = !currentViewState.shouldBeVisible
+ return if (shouldBeGone) visibility == GONE else visibility != GONE
+ }
+
+ /**
+ * b/298213983
+ * MediaContainerView's visibility is changed to VISIBLE when it should be GONE.
+ * This method check this state and logs.
+ */
+ private fun assertMediaContainerVisibility(visibility: Int) {
+ val currentViewState = viewState
+
+ if (currentViewState is MediaContainerViewState) {
+ if (!currentViewState.shouldBeVisible && visibility == VISIBLE) {
+ Log.wtf("MediaContainerView", "MediaContainerView should be GONE " +
+ "but its visibility changed to VISIBLE")
+ }
+ }
+ }
+
+ fun setKeyguardVisibility(isVisible: Boolean) {
+ val currentViewState = viewState
+ if (currentViewState is MediaContainerViewState) {
+ currentViewState.shouldBeVisible = isVisible
+ }
+
+ visibility = if (isVisible) VISIBLE else GONE
+ }
+
+ override fun createExpandableViewState(): ExpandableViewState = MediaContainerViewState()
+
+ class MediaContainerViewState : ExpandableViewState() {
+ var shouldBeVisible: Boolean = false
+
+ override fun copyFrom(viewState: ViewState) {
+ super.copyFrom(viewState)
+ if (viewState is MediaContainerViewState) {
+ shouldBeVisible = viewState.shouldBeVisible
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index cfc433a09c4d..d269eda6795a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -19,7 +19,7 @@ import android.annotation.ColorInt
import android.util.Log
import android.view.View
import com.android.internal.annotations.VisibleForTesting
-import com.android.systemui.media.controls.ui.KeyguardMediaController
+import com.android.systemui.media.controls.ui.controller.KeyguardMediaController
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
import com.android.systemui.statusbar.notification.SourceType
import com.android.systemui.statusbar.notification.collection.render.MediaContainerController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index aa9d3b23e47b..e397a70ea1f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -99,6 +99,7 @@ import com.android.systemui.shade.TouchLogger;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.FakeShadowView;
import com.android.systemui.statusbar.notification.LaunchAnimationParameters;
import com.android.systemui.statusbar.notification.NotificationTransitionAnimatorController;
@@ -122,6 +123,7 @@ import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.ScrollAdapter;
import com.android.systemui.statusbar.policy.SplitShadeStateController;
import com.android.systemui.util.Assert;
+import com.android.systemui.util.ColorUtilKt;
import com.android.systemui.util.DumpUtilsKt;
import com.google.errorprone.annotations.CompileTimeConstant;
@@ -571,7 +573,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
* Do notifications dismiss with normal transitioning
*/
private boolean mDismissUsingRowTranslationX = true;
- private NotificationEntry mTopHeadsUpEntry;
+ private ExpandableNotificationRow mTopHeadsUpRow;
private long mNumHeadsUp;
private NotificationStackScrollLayoutController.TouchHandler mTouchHandler;
private final ScreenOffAnimationController mScreenOffAnimationController;
@@ -805,8 +807,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
updateBackgroundDimming();
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
- if (child instanceof ActivatableNotificationView) {
- ((ActivatableNotificationView) child).updateBackgroundColors();
+ if (child instanceof ActivatableNotificationView activatableView) {
+ activatableView.updateBackgroundColors();
}
}
}
@@ -1686,10 +1688,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
* is mainly used when dragging down from a heads up notification.
*/
private int getTopHeadsUpPinnedHeight() {
- if (mTopHeadsUpEntry == null) {
+ if (mTopHeadsUpRow == null) {
return 0;
}
- ExpandableNotificationRow row = mTopHeadsUpEntry.getRow();
+ ExpandableNotificationRow row = mTopHeadsUpRow;
if (row.isChildInGroup()) {
final NotificationEntry groupSummary =
mGroupMembershipManager.getGroupSummary(row.getEntry());
@@ -1870,8 +1872,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
if (slidingChild instanceof ExpandableNotificationRow row) {
NotificationEntry entry = row.getEntry();
if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
- && mTopHeadsUpEntry.getRow() != row
- && mGroupMembershipManager.getGroupSummary(mTopHeadsUpEntry) != entry) {
+ && mTopHeadsUpRow != row
+ && mGroupMembershipManager.getGroupSummary(mTopHeadsUpRow.getEntry())
+ != entry) {
continue;
}
return row.getViewAtPosition(touchY - childTop);
@@ -4595,6 +4598,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
final @ColorInt int onSurfaceVariant = Utils.getColorAttrDefaultColor(
mContext, com.android.internal.R.attr.materialColorOnSurfaceVariant);
+ ColorUpdateLogger colorUpdateLogger = ColorUpdateLogger.getInstance();
+ if (colorUpdateLogger != null) {
+ colorUpdateLogger.logEvent("NSSL.updateDecorViews()",
+ "onSurface=" + ColorUtilKt.hexColorString(onSurface)
+ + " onSurfaceVariant=" + ColorUtilKt.hexColorString(onSurfaceVariant));
+ }
+
mSectionsManager.setHeaderForegroundColors(onSurface, onSurfaceVariant);
if (mFooterView != null) {
@@ -5715,13 +5725,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
mShelf.updateAppearance();
}
- void setTopHeadsUpEntry(NotificationEntry topEntry) {
- mTopHeadsUpEntry = topEntry;
+ void setTopHeadsUpRow(ExpandableNotificationRow topHeadsUpRow) {
+ mTopHeadsUpRow = topHeadsUpRow;
}
void setNumHeadsUp(long numHeadsUp) {
mNumHeadsUp = numHeadsUp;
- mAmbientState.setHasAlertEntries(numHeadsUp > 0);
+ mAmbientState.setHasHeadsUpEntries(numHeadsUp > 0);
}
public boolean getIsExpanded() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 830b8c1ae63e..7c138776d5a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -23,6 +23,7 @@ import static com.android.app.animation.Interpolators.STANDARD;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
import static com.android.server.notification.Flags.screenshareNotificationHiding;
import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
+import static com.android.systemui.Flags.migrateClocksToBlueprint;
import static com.android.systemui.Flags.nsslFalsingFix;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnEmptySpaceClickListener;
@@ -71,10 +72,9 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlagsClassic;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository;
-import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl;
import com.android.systemui.keyguard.shared.model.KeyguardState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
-import com.android.systemui.media.controls.ui.KeyguardMediaController;
+import com.android.systemui.media.controls.ui.controller.KeyguardMediaController;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -96,6 +96,7 @@ import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.LaunchAnimationParameters;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
@@ -177,6 +178,7 @@ public class NotificationStackScrollLayoutController implements Dumpable {
private final ConfigurationController mConfigurationController;
private final ZenModeController mZenModeController;
private final MetricsLogger mMetricsLogger;
+ private final ColorUpdateLogger mColorUpdateLogger;
private final DumpManager mDumpManager;
private final FalsingCollector mFalsingCollector;
@@ -239,6 +241,7 @@ public class NotificationStackScrollLayoutController implements Dumpable {
new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
+ mColorUpdateLogger.logTriggerEvent("NSSLC.onViewAttachedToWindow()");
mConfigurationController.addCallback(mConfigurationListener);
if (!FooterViewRefactor.isEnabled()) {
mZenModeController.addCallback(mZenModeControllerCallback);
@@ -254,6 +257,7 @@ public class NotificationStackScrollLayoutController implements Dumpable {
@Override
public void onViewDetachedFromWindow(View v) {
+ mColorUpdateLogger.logTriggerEvent("NSSLC.onViewDetachedFromWindow()");
mConfigurationController.removeCallback(mConfigurationListener);
if (!FooterViewRefactor.isEnabled()) {
mZenModeController.removeCallback(mZenModeControllerCallback);
@@ -332,12 +336,16 @@ public class NotificationStackScrollLayoutController implements Dumpable {
@Override
public void onUiModeChanged() {
+ mColorUpdateLogger.logTriggerEvent("NSSLC.onUiModeChanged()",
+ "mode=" + mConfigurationController.getNightModeName());
mView.updateBgColor();
mView.updateDecorViews();
}
@Override
public void onThemeChanged() {
+ mColorUpdateLogger.logTriggerEvent("NSSLC.onThemeChanged()",
+ "mode=" + mConfigurationController.getNightModeName());
mView.updateCornerRadius();
mView.updateBgColor();
mView.updateDecorViews();
@@ -684,7 +692,7 @@ public class NotificationStackScrollLayoutController implements Dumpable {
long numEntries = mHeadsUpManager.getAllEntries().count();
NotificationEntry topEntry = mHeadsUpManager.getTopEntry();
mView.setNumHeadsUp(numEntries);
- mView.setTopHeadsUpEntry(topEntry);
+ mView.setTopHeadsUpRow(topEntry != null ? topEntry.getRow() : null);
generateHeadsUpAnimation(entry, isHeadsUp);
}
};
@@ -719,6 +727,7 @@ public class NotificationStackScrollLayoutController implements Dumpable {
ZenModeController zenModeController,
NotificationLockscreenUserManager lockscreenUserManager,
MetricsLogger metricsLogger,
+ ColorUpdateLogger colorUpdateLogger,
DumpManager dumpManager,
FalsingCollector falsingCollector,
FalsingManager falsingManager,
@@ -773,6 +782,7 @@ public class NotificationStackScrollLayoutController implements Dumpable {
mZenModeController = zenModeController;
mLockscreenUserManager = lockscreenUserManager;
mMetricsLogger = metricsLogger;
+ mColorUpdateLogger = colorUpdateLogger;
mDumpManager = dumpManager;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
mFalsingCollector = falsingCollector;
@@ -1029,6 +1039,11 @@ public class NotificationStackScrollLayoutController implements Dumpable {
mView.setTranslationY(translationY);
}
+ /** Set view x-translation */
+ public void setTranslationX(float translationX) {
+ mView.setTranslationX(translationX);
+ }
+
public int indexOfChild(View view) {
return mView.indexOfChild(view);
}
@@ -2078,7 +2093,7 @@ public class NotificationStackScrollLayoutController implements Dumpable {
}
boolean horizontalSwipeWantsIt = false;
boolean scrollerWantsIt = false;
- if (nsslFalsingFix() || KeyguardShadeMigrationNssl.isEnabled()) {
+ if (nsslFalsingFix() || migrateClocksToBlueprint()) {
// Reverse the order relative to the else statement. onScrollTouch will reset on an
// UP event, causing horizontalSwipeWantsIt to be set to true on vertical swipes.
if (mLongPressedView == null && !mView.isBeingDragged()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
index 30708b708f25..2d9c63efee53 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
@@ -23,7 +23,7 @@ import androidx.annotation.VisibleForTesting
import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.StatusBarState.KEYGUARD
import com.android.systemui.statusbar.SysuiStatusBarStateController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 634de7a17ef7..1ef9a8f3d7ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -853,7 +853,7 @@ public class StackScrollAlgorithm {
}
}
if (row.isHeadsUpAnimatingAway()) {
- if (NotificationsImprovedHunAnimation.isEnabled()) {
+ if (NotificationsImprovedHunAnimation.isEnabled() && !ambientState.isDozing()) {
if (shouldHunAppearFromBottom(ambientState, childState)) {
// move to the bottom of the screen
childState.setYTranslation(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
index 4b8fb1e5a0ff..20e8cac6e94b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
@@ -18,7 +18,7 @@
package com.android.systemui.statusbar.notification.stack.domain.interactor
import android.content.Context
-import com.android.systemui.Flags.centralizedStatusBarDimensRefactor
+import com.android.systemui.Flags.centralizedStatusBarHeightFix
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
@@ -76,7 +76,7 @@ constructor(
getDimensionPixelSize(R.dimen.notification_panel_margin_bottom),
marginTop = getDimensionPixelSize(R.dimen.notification_panel_margin_top),
marginTopLargeScreen =
- if (centralizedStatusBarDimensRefactor()) {
+ if (centralizedStatusBarHeightFix()) {
largeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight()
} else {
getDimensionPixelSize(R.dimen.large_screen_shade_header_height)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/SceneContainerFlagsExtension.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/SceneContainerFlagsExtension.kt
index 5a71bd6fa116..7dfd53e544c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/SceneContainerFlagsExtension.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/shared/SceneContainerFlagsExtension.kt
@@ -18,7 +18,7 @@ package com.android.systemui.statusbar.notification.stack.shared
import com.android.systemui.scene.shared.flag.SceneContainerFlags
-private const val FLEXI_NOTIFS = false
+private const val FLEXI_NOTIFS = true
/**
* Returns whether flexiglass is displaying notifications, which is currently an optional piece of
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerImpl.kt
index 4897b4275f75..534e5c3fda87 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerImpl.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.stack.ui.view
+import android.os.Trace
import android.service.notification.NotificationListenerService
import androidx.annotation.VisibleForTesting
import com.android.internal.statusbar.IStatusBarService
@@ -182,6 +183,8 @@ constructor(
maybeLogVisibilityChanges(newlyVisible, noLongerVisible, activeNotifCount)
updateExpansionStates(newlyVisible, noLongerVisible)
+ Trace.traceCounter(Trace.TRACE_TAG_APP, "Notifications [Active]", activeNotifCount)
+ Trace.traceCounter(Trace.TRACE_TAG_APP, "Notifications [Visible]", newVisibilities.size)
lastLoggedVisibilities.clear()
lastLoggedVisibilities.putAll(newVisibilities)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt
index a1577854e732..76495cb23947 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt
@@ -20,12 +20,14 @@ import android.content.Context
import android.util.TypedValue
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.statusbar.notification.stack.AmbientState
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationStackAppearanceViewModel
import kotlin.math.roundToInt
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.launch
@@ -40,8 +42,9 @@ object NotificationStackAppearanceViewBinder {
viewModel: NotificationStackAppearanceViewModel,
ambientState: AmbientState,
controller: NotificationStackScrollLayoutController,
+ @Main mainImmediateDispatcher: CoroutineDispatcher,
): DisposableHandle {
- return view.repeatWhenAttached {
+ return view.repeatWhenAttached(mainImmediateDispatcher) {
repeatOnLifecycle(Lifecycle.State.CREATED) {
launch {
viewModel.stackBounds.collect { bounds ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
index daea8af8f334..566c0303b286 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
@@ -75,6 +75,24 @@ object SharedNotificationContainerBinder {
}
}
+ // Required to capture keyguard media changes and ensure the notification count is correct
+ val layoutChangeListener =
+ object : View.OnLayoutChangeListener {
+ override fun onLayoutChange(
+ view: View,
+ left: Int,
+ top: Int,
+ right: Int,
+ bottom: Int,
+ oldLeft: Int,
+ oldTop: Int,
+ oldRight: Int,
+ oldBottom: Int
+ ) {
+ viewModel.notificationStackChanged()
+ }
+ }
+
val burnInParams = MutableStateFlow(BurnInParameters())
val viewState =
ViewStateAccessor(
@@ -91,10 +109,10 @@ object SharedNotificationContainerBinder {
if (!sceneContainerFlags.flexiNotifsEnabled()) {
launch {
// Only temporarily needed, until flexi notifs go live
- viewModel.shadeCollpaseFadeIn.collect { fadeIn ->
+ viewModel.shadeCollapseFadeIn.collect { fadeIn ->
if (fadeIn) {
android.animation.ValueAnimator.ofFloat(0f, 1f).apply {
- duration = 350
+ duration = 250
addUpdateListener { animation ->
controller.setMaxAlphaForExpansion(
animation.getAnimatedFraction()
@@ -144,6 +162,8 @@ object SharedNotificationContainerBinder {
.collect { y -> controller.setTranslationY(y) }
}
+ launch { viewModel.translationX.collect { x -> controller.translationX = x } }
+
if (!sceneContainerFlags.isEnabled()) {
launch {
viewModel.expansionAlpha(viewState).collect {
@@ -168,6 +188,7 @@ object SharedNotificationContainerBinder {
}
insets
}
+ view.addOnLayoutChangeListener(layoutChangeListener)
return object : DisposableHandle {
override fun dispose() {
@@ -175,6 +196,7 @@ object SharedNotificationContainerBinder {
disposableHandleMainImmediate.dispose()
controller.setOnHeightChangedRunnable(null)
view.setOnApplyWindowInsetsListener(null)
+ view.removeOnLayoutChangeListener(layoutChangeListener)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
index 6321820b0733..7b502564ecb1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
@@ -122,32 +122,31 @@ constructor(
qsFullScreen,
isRemoteInputActive,
isShadeClosed ->
- // A pair of (visible, canAnimate)
when {
- !hasNotifications -> Pair(false, true)
+ !hasNotifications -> VisibilityChange.HIDE_WITH_ANIMATION
// Hide the footer until the user setup is complete, to prevent access
// to settings (b/193149550).
- !isUserSetUp -> Pair(false, true)
+ !isUserSetUp -> VisibilityChange.HIDE_WITH_ANIMATION
// Do not show the footer if the lockscreen is visible (incl. AOD),
// except if the shade is opened on top. See also b/219680200.
// Do not animate, as that makes the footer appear briefly when
// transitioning between the shade and keyguard.
- isShowingOnLockscreen -> Pair(false, false)
+ isShowingOnLockscreen -> VisibilityChange.HIDE_WITHOUT_ANIMATION
// Do not show the footer if quick settings are fully expanded (except
// for the foldable split shade view). See b/201427195 && b/222699879.
- qsExpansion == 1f && qsFullScreen -> Pair(false, true)
+ qsExpansion == 1f && qsFullScreen -> VisibilityChange.HIDE_WITH_ANIMATION
// Hide the footer if remote input is active (i.e. user is replying to a
// notification). See b/75984847.
- isRemoteInputActive -> Pair(false, true)
+ isRemoteInputActive -> VisibilityChange.HIDE_WITH_ANIMATION
// Never show the footer if the shade is collapsed (e.g. when HUNing).
- isShadeClosed -> Pair(false, false)
- else -> Pair(true, true)
+ isShadeClosed -> VisibilityChange.HIDE_WITHOUT_ANIMATION
+ else -> VisibilityChange.SHOW_WITH_ANIMATION
}
}
.distinctUntilChanged(
// Equivalent unless visibility changes
- areEquivalent = { a: Pair<Boolean, Boolean>, b: Pair<Boolean, Boolean> ->
- a.first == b.first
+ areEquivalent = { a: VisibilityChange, b: VisibilityChange ->
+ a.visible == b.visible
}
)
// Should we animate the visibility change?
@@ -160,17 +159,24 @@ constructor(
::Pair
)
.onStart { emit(Pair(false, false)) }
- ) { (visible, canAnimate), (isShadeFullyExpanded, animationsEnabled) ->
+ ) { visibilityChange, (isShadeFullyExpanded, animationsEnabled) ->
// Animate if the shade is interactive, but NOT on the lockscreen. Having
// animations enabled while on the lockscreen makes the footer appear briefly
// when transitioning between the shade and keyguard.
- val shouldAnimate = isShadeFullyExpanded && animationsEnabled && canAnimate
- AnimatableEvent(visible, shouldAnimate)
+ val shouldAnimate =
+ isShadeFullyExpanded && animationsEnabled && visibilityChange.canAnimate
+ AnimatableEvent(visibilityChange.visible, shouldAnimate)
}
.toAnimatedValueFlow()
}
}
+ enum class VisibilityChange(val visible: Boolean, val canAnimate: Boolean) {
+ HIDE_WITHOUT_ANIMATION(visible = false, canAnimate = false),
+ HIDE_WITH_ANIMATION(visible = false, canAnimate = true),
+ SHOW_WITH_ANIMATION(visible = true, canAnimate = true)
+ }
+
private val isShowingOnLockscreen: Flow<Boolean> by lazy {
if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
flowOf(false)
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 476b05492758..052e35c44bbe 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
@@ -45,6 +45,7 @@ import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
import com.android.systemui.keyguard.ui.viewmodel.DozingToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToLockscreenTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.GoneToAodTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.GoneToDozingTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel
@@ -98,6 +99,7 @@ constructor(
private val dreamingToLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel,
private val glanceableHubToLockscreenTransitionViewModel:
GlanceableHubToLockscreenTransitionViewModel,
+ private val goneToAodTransitionViewModel: GoneToAodTransitionViewModel,
private val goneToDozingTransitionViewModel: GoneToDozingTransitionViewModel,
private val goneToDreamingTransitionViewModel: GoneToDreamingTransitionViewModel,
private val lockscreenToDreamingTransitionViewModel: LockscreenToDreamingTransitionViewModel,
@@ -131,6 +133,21 @@ constructor(
.distinctUntilChanged()
.onStart { emit(false) }
+ /**
+ * Shade locked is a legacy concept, but necessary to mimic current functionality. Listen for
+ * both SHADE_LOCKED and shade/qs expansion in order to determine lock state, as one can arrive
+ * before the other.
+ */
+ private val isShadeLocked: Flow<Boolean> =
+ combine(
+ keyguardInteractor.statusBarState.map { it == SHADE_LOCKED },
+ shadeInteractor.qsExpansion.map { it > 0f },
+ shadeInteractor.shadeExpansion.map { it > 0f },
+ ) { isShadeLocked, isQsExpanded, isShadeExpanded ->
+ isShadeLocked && (isQsExpanded || isShadeExpanded)
+ }
+ .distinctUntilChanged()
+
val shadeCollapseFadeInComplete = MutableStateFlow(false)
val configurationBasedDimensions: Flow<ConfigurationBasedDimensions> =
@@ -188,7 +205,7 @@ constructor(
)
/** Are we purely on the glanceable hub without the shade/qs? */
- internal val isOnGlanceableHubWithoutShade: Flow<Boolean> =
+ val isOnGlanceableHubWithoutShade: Flow<Boolean> =
combine(
communalInteractor.isIdleOnCommunal,
// Shade with notifications
@@ -206,12 +223,12 @@ constructor(
)
/** Fade in only for use after the shade collapses */
- val shadeCollpaseFadeIn: Flow<Boolean> =
+ val shadeCollapseFadeIn: Flow<Boolean> =
flow {
while (currentCoroutineContext().isActive) {
emit(false)
// Wait for shade to be fully expanded
- keyguardInteractor.statusBarState.first { it == SHADE_LOCKED }
+ isShadeLocked.first { it }
// ... and then for it to be collapsed
isOnLockscreenWithoutShade.first { it }
emit(true)
@@ -304,9 +321,10 @@ constructor(
val alphaTransitions =
merge(
alternateBouncerToGoneTransitionViewModel.lockscreenAlpha,
- aodToLockscreenTransitionViewModel.lockscreenAlpha(viewState),
+ aodToLockscreenTransitionViewModel.notificationAlpha,
dozingToLockscreenTransitionViewModel.lockscreenAlpha,
dreamingToLockscreenTransitionViewModel.lockscreenAlpha,
+ goneToAodTransitionViewModel.notificationAlpha,
goneToDreamingTransitionViewModel.lockscreenAlpha,
goneToDozingTransitionViewModel.lockscreenAlpha,
lockscreenToDreamingTransitionViewModel.lockscreenAlpha,
@@ -327,16 +345,16 @@ constructor(
// shade expansion or swipe to dismiss
combineTransform(
isOnLockscreenWithoutShade,
- shadeCollpaseFadeIn,
+ shadeCollapseFadeIn,
alphaForShadeAndQsExpansion,
keyguardInteractor.dismissAlpha,
) {
isOnLockscreenWithoutShade,
- shadeCollpaseFadeIn,
+ shadeCollapseFadeIn,
alphaForShadeAndQsExpansion,
dismissAlpha ->
if (isOnLockscreenWithoutShade) {
- if (!shadeCollpaseFadeIn && dismissAlpha != null) {
+ if (!shadeCollapseFadeIn && dismissAlpha != null) {
emit(dismissAlpha)
}
} else {
@@ -360,14 +378,9 @@ constructor(
lockscreenToGlanceableHubRunning,
glanceableHubToLockscreenRunning,
merge(
- lockscreenToGlanceableHubTransitionViewModel.notificationAlpha,
- glanceableHubToLockscreenTransitionViewModel.notificationAlpha,
- )
- .onStart {
- // Transition flows don't emit a value on start, kick things off so the
- // combine starts.
- emit(1f)
- }
+ lockscreenToGlanceableHubTransitionViewModel.notificationAlpha,
+ glanceableHubToLockscreenTransitionViewModel.notificationAlpha,
+ )
) { lockscreenToGlanceableHubRunning, glanceableHubToLockscreenRunning, alpha ->
if (isOnGlanceableHubWithoutShade) {
// Notifications should not be visible on the glanceable hub.
@@ -406,6 +419,16 @@ constructor(
}
/**
+ * The container may need to be translated in the x direction as the keyguard fades out, such as
+ * when swiping open the glanceable hub from the lockscreen.
+ */
+ val translationX: Flow<Float> =
+ merge(
+ lockscreenToGlanceableHubTransitionViewModel.notificationTranslationX,
+ glanceableHubToLockscreenTransitionViewModel.notificationTranslationX,
+ )
+
+ /**
* When on keyguard, there is limited space to display notifications so calculate how many could
* be shown. Otherwise, there is no limit since the vertical space will be scrollable.
*
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 a135802f095e..db15144340e2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -27,6 +27,7 @@ import static androidx.lifecycle.Lifecycle.State.RESUMED;
import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
import static com.android.systemui.Flags.lightRevealMigration;
+import static com.android.systemui.Flags.migrateClocksToBlueprint;
import static com.android.systemui.Flags.newAodTransition;
import static com.android.systemui.Flags.predictiveBackSysui;
import static com.android.systemui.Flags.truncatedStatusBarIconsFix;
@@ -143,7 +144,6 @@ import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
-import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl;
import com.android.systemui.keyguard.ui.binder.LightRevealScrimViewBinder;
import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel;
import com.android.systemui.navigationbar.NavigationBarController;
@@ -982,7 +982,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
this,
mStatusBarKeyguardViewManager,
getNotificationShadeWindowViewController(),
- mShadeSurface,
mAmbientIndicationContainer);
updateLightRevealScrimVisibility();
@@ -1376,7 +1375,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
|| !mKeyguardStateController.canDismissLockScreen()
|| mKeyguardViewMediator.isAnySimPinSecure()
|| (mQsController.getExpanded() && trackingTouch)
- || mShadeSurface.getBarState() == StatusBarState.SHADE_LOCKED) {
+ || mShadeSurface.getBarState() == StatusBarState.SHADE_LOCKED
+ // This last one causes a race condition when the shade resets. Don't send a 0
+ // and let StatusBarStateController process a keyguard state change instead
+ || 1f - fraction == 0f) {
return;
}
@@ -1468,7 +1470,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
return (v, event) -> {
mAutoHideController.checkUserAutoHide(event);
mRemoteInputManager.checkRemoteInputOutside(event);
- if (!KeyguardShadeMigrationNssl.isEnabled()) {
+ if (!migrateClocksToBlueprint()) {
mShadeController.onStatusBarTouch(event);
}
return getNotificationShadeWindowView().onTouchEvent(event);
@@ -2505,7 +2507,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mNotificationShadeWindowController.batchApplyWindowLayoutParams(()-> {
mDeviceInteractive = true;
- boolean isFlaggedOff = newAodTransition() && KeyguardShadeMigrationNssl.isEnabled();
+ boolean isFlaggedOff = newAodTransition() && migrateClocksToBlueprint();
if (!isFlaggedOff && shouldAnimateDozeWakeup()) {
// If this is false, the power button must be physically pressed in order to
// trigger fingerprint authentication.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ComponentSystemUIDialog.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ComponentSystemUIDialog.kt
index 4fe9c8ccca0b..f3a4f0e64924 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ComponentSystemUIDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ComponentSystemUIDialog.kt
@@ -24,6 +24,7 @@ import android.view.ViewGroup
import androidx.activity.OnBackPressedDispatcher
import androidx.activity.OnBackPressedDispatcherOwner
import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
+import androidx.annotation.GravityInt
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
@@ -56,6 +57,7 @@ class ComponentSystemUIDialog(
sysUiState: SysUiState,
broadcastDispatcher: BroadcastDispatcher,
dialogTransitionAnimator: DialogTransitionAnimator,
+ @GravityInt private val dialogGravity: Int?,
) :
SystemUIDialog(
context,
@@ -90,6 +92,7 @@ class ComponentSystemUIDialog(
@CallSuper
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ dialogGravity?.let { window?.setGravity(it) }
onBackPressedDispatcher.setOnBackInvokedDispatcher(onBackInvokedDispatcher)
savedStateRegistryController.performRestore(savedInstanceState)
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
index 6e8ad2e50620..dea94162ad0e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
@@ -159,6 +159,15 @@ class ConfigurationControllerImpl @Inject constructor(
override fun isLayoutRtl(): Boolean {
return layoutDirection == LAYOUT_DIRECTION_RTL
}
+
+ override fun getNightModeName(): String {
+ return when (uiMode and Configuration.UI_MODE_NIGHT_MASK) {
+ Configuration.UI_MODE_NIGHT_YES -> "night"
+ Configuration.UI_MODE_NIGHT_NO -> "day"
+ Configuration.UI_MODE_NIGHT_UNDEFINED -> "undefined"
+ else -> "err"
+ }
+ }
}
// This could be done with a Collection.filter and Collection.forEach, but Collection.filter
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index 45005cbc28a5..442e43a9dae2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -41,7 +41,7 @@ import com.android.systemui.flags.FeatureFlagsClassic;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.keyguard.domain.interactor.DozeInteractor;
import com.android.systemui.shade.NotificationShadeWindowViewController;
-import com.android.systemui.shade.ShadeViewController;
+import com.android.systemui.shade.ShadeLockscreenInteractor;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.StatusBarState;
@@ -98,7 +98,7 @@ public final class DozeServiceHost implements DozeHost {
private final AuthController mAuthController;
private final NotificationIconAreaController mNotificationIconAreaController;
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
- private ShadeViewController mNotificationPanel;
+ private final ShadeLockscreenInteractor mShadeLockscreenInteractor;
private View mAmbientIndicationContainer;
private CentralSurfaces mCentralSurfaces;
private boolean mAlwaysOnSuppressed;
@@ -121,6 +121,7 @@ public final class DozeServiceHost implements DozeHost {
NotificationWakeUpCoordinator notificationWakeUpCoordinator,
AuthController authController,
NotificationIconAreaController notificationIconAreaController,
+ ShadeLockscreenInteractor shadeLockscreenInteractor,
DozeInteractor dozeInteractor) {
super();
mDozeLog = dozeLog;
@@ -141,6 +142,7 @@ public final class DozeServiceHost implements DozeHost {
mNotificationWakeUpCoordinator = notificationWakeUpCoordinator;
mAuthController = authController;
mNotificationIconAreaController = notificationIconAreaController;
+ mShadeLockscreenInteractor = shadeLockscreenInteractor;
mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
mDozeInteractor = dozeInteractor;
}
@@ -154,11 +156,9 @@ public final class DozeServiceHost implements DozeHost {
CentralSurfaces centralSurfaces,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
NotificationShadeWindowViewController notificationShadeWindowViewController,
- ShadeViewController notificationPanel,
View ambientIndicationContainer) {
mCentralSurfaces = centralSurfaces;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
- mNotificationPanel = notificationPanel;
mNotificationShadeWindowViewController = notificationShadeWindowViewController;
mAmbientIndicationContainer = ambientIndicationContainer;
}
@@ -290,7 +290,7 @@ public final class DozeServiceHost implements DozeHost {
private void setPulsing(boolean pulsing) {
mStatusBarKeyguardViewManager.setPulsing(pulsing);
- mNotificationPanel.setPulsing(pulsing);
+ mShadeLockscreenInteractor.setPulsing(pulsing);
mStatusBarStateController.setPulsing(pulsing);
mIgnoreTouchWhilePulsing = false;
if (mKeyguardUpdateMonitor != null && passiveAuthInterrupt) {
@@ -329,7 +329,7 @@ public final class DozeServiceHost implements DozeHost {
@Override
public void dozeTimeTick() {
mDozeInteractor.dozeTimeTick();
- mNotificationPanel.dozeTimeTick();
+ mShadeLockscreenInteractor.dozeTimeTick();
mAuthController.dozeTimeTick();
if (mAmbientIndicationContainer instanceof DozeReceiver) {
((DozeReceiver) mAmbientIndicationContainer).dozeTimeTick();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index db55da7b8acb..0adc1b0af66f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.Flags.centralizedStatusBarDimensRefactor;
+import static com.android.systemui.Flags.centralizedStatusBarHeightFix;
import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInScale;
import static com.android.systemui.statusbar.notification.NotificationUtils.interpolate;
@@ -169,7 +169,7 @@ public class KeyguardClockPositionAlgorithm {
mStatusViewBottomMargin =
res.getDimensionPixelSize(R.dimen.keyguard_status_view_bottom_margin);
mSplitShadeTopNotificationsMargin =
- centralizedStatusBarDimensRefactor()
+ centralizedStatusBarHeightFix()
? LargeScreenHeaderHelper.getLargeScreenHeaderHeight(context)
: res.getDimensionPixelSize(R.dimen.large_screen_shade_header_height);
mSplitShadeTargetTopMargin =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 769145923886..302bdcc2ea77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.Flags.centralizedStatusBarDimensRefactor;
+import static com.android.systemui.Flags.centralizedStatusBarHeightFix;
import static com.android.systemui.ScreenDecorations.DisplayCutoutView.boundsFromDirection;
import static com.android.systemui.util.Utils.getStatusBarHeaderHeightKeyguard;
@@ -131,7 +131,7 @@ public class KeyguardStatusBarView extends RelativeLayout {
mUserSwitcherContainer = findViewById(R.id.user_switcher_container);
mIsPrivacyDotEnabled = mContext.getResources().getBoolean(R.bool.config_enablePrivacyDot);
loadDimens();
- if (!centralizedStatusBarDimensRefactor()) {
+ if (!centralizedStatusBarHeightFix()) {
setGravity(Gravity.CENTER_VERTICAL);
}
}
@@ -311,7 +311,7 @@ public class KeyguardStatusBarView extends RelativeLayout {
final int minRight = (!isLayoutRtl() && mIsPrivacyDotEnabled)
? Math.max(mMinDotWidth, mPadding.right) : mPadding.right;
- int top = centralizedStatusBarDimensRefactor() ? waterfallTop + mPadding.top : waterfallTop;
+ int top = centralizedStatusBarHeightFix() ? waterfallTop + mPadding.top : waterfallTop;
setPadding(minLeft, top, minRight, 0);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java
index e79f3ff19031..94f62e075a4a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImpl.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
import static com.android.systemui.Flags.newAodTransition;
+import static com.android.systemui.Flags.migrateClocksToBlueprint;
import android.content.Context;
import android.content.res.Resources;
@@ -40,7 +41,6 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -545,7 +545,7 @@ public class LegacyNotificationIconAreaControllerImpl implements
return;
}
if (mScreenOffAnimationController.shouldAnimateAodIcons()) {
- if (!KeyguardShadeMigrationNssl.isEnabled()) {
+ if (!migrateClocksToBlueprint()) {
mAodIcons.setTranslationY(-mAodIconAppearTranslation);
}
mAodIcons.setAlpha(0);
@@ -557,14 +557,14 @@ public class LegacyNotificationIconAreaControllerImpl implements
.start();
} else {
mAodIcons.setAlpha(1.0f);
- if (!KeyguardShadeMigrationNssl.isEnabled()) {
+ if (!migrateClocksToBlueprint()) {
mAodIcons.setTranslationY(0);
}
}
}
private void animateInAodIconTranslation() {
- if (!KeyguardShadeMigrationNssl.isEnabled()) {
+ if (!migrateClocksToBlueprint()) {
mAodIcons.animate()
.setInterpolator(Interpolators.DECELERATE_QUINT)
.translationY(0)
@@ -667,7 +667,7 @@ public class LegacyNotificationIconAreaControllerImpl implements
}
} else {
mAodIcons.setAlpha(1.0f);
- if (!KeyguardShadeMigrationNssl.isEnabled()) {
+ if (!migrateClocksToBlueprint()) {
mAodIcons.setTranslationY(0);
}
mAodIcons.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 8ac3b4a75141..d10ca3d31de2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -38,9 +38,9 @@ import android.widget.LinearLayout;
import com.android.internal.policy.SystemBarUtils;
import com.android.systemui.Dependency;
import com.android.systemui.Gefingerpoken;
-import com.android.systemui.res.R;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer;
import com.android.systemui.statusbar.policy.Clock;
import com.android.systemui.statusbar.window.StatusBarWindowController;
@@ -67,6 +67,8 @@ public class PhoneStatusBarView extends FrameLayout {
private int mStatusBarHeight;
@Nullable
private Gefingerpoken mTouchEventHandler;
+ private int mDensity;
+ private float mFontScale;
/**
* Draw this many pixels into the left/right side of the cutout to optimally use the space
@@ -167,13 +169,23 @@ public class PhoneStatusBarView extends FrameLayout {
mDisplayCutout = getRootWindowInsets().getDisplayCutout();
}
- final Rect newSize = mContext.getResources().getConfiguration().windowConfiguration
- .getMaxBounds();
+ Configuration newConfiguration = mContext.getResources().getConfiguration();
+ final Rect newSize = newConfiguration.windowConfiguration.getMaxBounds();
if (!Objects.equals(newSize, mDisplaySize)) {
changed = true;
mDisplaySize = newSize;
}
+ int density = newConfiguration.densityDpi;
+ if (density != mDensity) {
+ changed = true;
+ mDensity = density;
+ }
+ float fontScale = newConfiguration.fontScale;
+ if (fontScale != mFontScale) {
+ changed = true;
+ mFontScale = fontScale;
+ }
return changed;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index f9702dd12535..a39bfe00be28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -81,6 +81,7 @@ private constructor(
statusContainer.setOnHoverListener(
statusOverlayHoverListenerFactory.createDarkAwareListener(statusContainer)
)
+ statusContainer.setOnClickListener { shadeViewController.expand(/* animate= */true) }
progressProvider?.setReadyToHandleTransition(true)
configurationController.addCallback(configurationListener)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 6f78604e996f..d2e36b88fd9d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -733,7 +733,8 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
if (mPanelExpansionFraction != panelExpansionFraction) {
if (panelExpansionFraction != 0f
- && mKeyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) {
+ && mKeyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()
+ && mState != ScrimState.UNLOCKED) {
mAnimatingPanelExpansionOnUnlock = true;
} else if (panelExpansionFraction == 0f) {
mAnimatingPanelExpansionOnUnlock = false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
index ac203db209e0..613efaa148f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
@@ -27,11 +27,17 @@ import android.util.Pair
import android.view.DisplayCutout
import android.view.Surface
import androidx.annotation.VisibleForTesting
+import com.android.app.tracing.traceSection
import com.android.internal.policy.SystemBarUtils
+import com.android.systemui.BottomMarginCommand
import com.android.systemui.Dumpable
+import com.android.systemui.StatusBarInsetsCommand
+import com.android.systemui.SysUICutoutInformation
+import com.android.systemui.SysUICutoutProvider
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
import com.android.systemui.res.R
+import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.policy.CallbackController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE
@@ -41,12 +47,6 @@ import com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN
import com.android.systemui.util.leak.RotationUtils.Rotation
import com.android.systemui.util.leak.RotationUtils.getExactRotation
import com.android.systemui.util.leak.RotationUtils.getResourcesForRotation
-import com.android.app.tracing.traceSection
-import com.android.systemui.BottomMarginCommand
-import com.android.systemui.StatusBarInsetsCommand
-import com.android.systemui.SysUICutoutInformation
-import com.android.systemui.SysUICutoutProvider
-import com.android.systemui.statusbar.commandline.CommandRegistry
import java.io.PrintWriter
import java.lang.Math.max
import javax.inject.Inject
@@ -54,45 +54,53 @@ import javax.inject.Inject
/**
* Encapsulates logic that can solve for the left/right insets required for the status bar contents.
* Takes into account:
- * 1. rounded_corner_content_padding
- * 2. status_bar_padding_start, status_bar_padding_end
- * 2. display cutout insets from left or right
- * 3. waterfall insets
+ * 1. rounded_corner_content_padding
+ * 2. status_bar_padding_start, status_bar_padding_end
+ * 2. display cutout insets from left or right
+ * 3. waterfall insets
*
+ * Importantly, these functions can determine status bar content left/right insets for any rotation
+ * before having done a layout pass in that rotation.
*
- * Importantly, these functions can determine status bar content left/right insets for any rotation
- * before having done a layout pass in that rotation.
- *
- * NOTE: This class is not threadsafe
+ * NOTE: This class is not threadsafe
*/
@SysUISingleton
-class StatusBarContentInsetsProvider @Inject constructor(
+class StatusBarContentInsetsProvider
+@Inject
+constructor(
val context: Context,
val configurationController: ConfigurationController,
val dumpManager: DumpManager,
val commandRegistry: CommandRegistry,
val sysUICutoutProvider: SysUICutoutProvider,
-) : CallbackController<StatusBarContentInsetsChangedListener>,
- ConfigurationController.ConfigurationListener,
- Dumpable {
+) :
+ CallbackController<StatusBarContentInsetsChangedListener>,
+ ConfigurationController.ConfigurationListener,
+ Dumpable {
// Limit cache size as potentially we may connect large number of displays
// (e.g. network displays)
private val insetsCache = LruCache<CacheKey, Rect>(MAX_CACHE_SIZE)
private val listeners = mutableSetOf<StatusBarContentInsetsChangedListener>()
- private val isPrivacyDotEnabled: Boolean by lazy(LazyThreadSafetyMode.PUBLICATION) {
- context.resources.getBoolean(R.bool.config_enablePrivacyDot)
- }
+ private val isPrivacyDotEnabled: Boolean by
+ lazy(LazyThreadSafetyMode.PUBLICATION) {
+ context.resources.getBoolean(R.bool.config_enablePrivacyDot)
+ }
init {
configurationController.addCallback(this)
dumpManager.registerDumpable(TAG, this)
commandRegistry.registerCommand(StatusBarInsetsCommand.NAME) {
- StatusBarInsetsCommand(object : StatusBarInsetsCommand.Callback {
- override fun onExecute(command: StatusBarInsetsCommand, printWriter: PrintWriter) {
- executeCommand(command, printWriter)
+ StatusBarInsetsCommand(
+ object : StatusBarInsetsCommand.Callback {
+ override fun onExecute(
+ command: StatusBarInsetsCommand,
+ printWriter: PrintWriter
+ ) {
+ executeCommand(command, printWriter)
+ }
}
- })
+ )
}
}
@@ -122,15 +130,13 @@ class StatusBarContentInsetsProvider @Inject constructor(
}
private fun notifyInsetsChanged() {
- listeners.forEach {
- it.onStatusBarContentInsetsChanged()
- }
+ listeners.forEach { it.onStatusBarContentInsetsChanged() }
}
/**
- * Some views may need to care about whether or not the current top display cutout is located
- * in the corner rather than somewhere in the center. In the case of a corner cutout, the
- * status bar area is contiguous.
+ * Some views may need to care about whether or not the current top display cutout is located in
+ * the corner rather than somewhere in the center. In the case of a corner cutout, the status
+ * bar area is contiguous.
*/
fun currentRotationHasCornerCutout(): Boolean {
val cutout = checkNotNull(context.display).cutout ?: return false
@@ -147,8 +153,8 @@ class StatusBarContentInsetsProvider @Inject constructor(
* dot in the coordinates relative to the given rotation.
*
* @param rotation the rotation for which the bounds are required. This is an absolute value
- * (i.e., ROTATION_NONE will always return the same bounds regardless of the context
- * from which this method is called)
+ * (i.e., ROTATION_NONE will always return the same bounds regardless of the context from
+ * which this method is called)
*/
fun getBoundingRectForPrivacyChipForRotation(
@Rotation rotation: Int,
@@ -163,8 +169,8 @@ class StatusBarContentInsetsProvider @Inject constructor(
val rotatedResources = getResourcesForRotation(rotation, context)
val dotWidth = rotatedResources.getDimensionPixelSize(R.dimen.ongoing_appops_dot_diameter)
- val chipWidth = rotatedResources.getDimensionPixelSize(
- R.dimen.ongoing_appops_chip_max_width)
+ val chipWidth =
+ rotatedResources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_max_width)
val isRtl = configurationController.isLayoutRtl
return getPrivacyChipBoundingRectForInsets(insets, dotWidth, chipWidth, isRtl)
@@ -179,7 +185,7 @@ class StatusBarContentInsetsProvider @Inject constructor(
*/
fun getStatusBarContentInsetsForRotation(@Rotation rotation: Int): Insets =
traceSection(tag = "StatusBarContentInsetsProvider.getStatusBarContentInsetsForRotation") {
- val sysUICutout = sysUICutoutProvider.cutoutInfoForCurrentDisplay()
+ val sysUICutout = sysUICutoutProvider.cutoutInfoForCurrentDisplayAndRotation()
val displayCutout = sysUICutout?.cutout
val key = getCacheKey(rotation, displayCutout)
@@ -190,14 +196,21 @@ class StatusBarContentInsetsProvider @Inject constructor(
point.orientToRotZero(getExactRotation(context))
val width = point.logicalWidth(rotation)
- val area = insetsCache[key] ?: getAndSetCalculatedAreaForRotation(
- rotation, sysUICutout, getResourcesForRotation(rotation, context), key)
+ val area =
+ insetsCache[key]
+ ?: getAndSetCalculatedAreaForRotation(
+ rotation,
+ sysUICutout,
+ getResourcesForRotation(rotation, context),
+ key
+ )
Insets.of(area.left, area.top, /* right= */ width - area.right, /* bottom= */ 0)
}
/**
* Calculate the insets for the status bar content in the device's current rotation
+ *
* @see getStatusBarContentAreaForRotation
*/
fun getStatusBarContentInsetsForCurrentRotation(): Insets {
@@ -205,27 +218,28 @@ class StatusBarContentInsetsProvider @Inject constructor(
}
/**
- * Calculates the area of the status bar contents invariant of the current device rotation,
- * in the target rotation's coordinates
+ * Calculates the area of the status bar contents invariant of the current device rotation, in
+ * the target rotation's coordinates
*
* @param rotation the rotation for which the bounds are required. This is an absolute value
- * (i.e., ROTATION_NONE will always return the same bounds regardless of the context
- * from which this method is called)
+ * (i.e., ROTATION_NONE will always return the same bounds regardless of the context from
+ * which this method is called)
*/
@JvmOverloads
- fun getStatusBarContentAreaForRotation(
- @Rotation rotation: Int
- ): Rect {
- val sysUICutout = sysUICutoutProvider.cutoutInfoForCurrentDisplay()
+ fun getStatusBarContentAreaForRotation(@Rotation rotation: Int): Rect {
+ val sysUICutout = sysUICutoutProvider.cutoutInfoForCurrentDisplayAndRotation()
val displayCutout = sysUICutout?.cutout
val key = getCacheKey(rotation, displayCutout)
- return insetsCache[key] ?: getAndSetCalculatedAreaForRotation(
- rotation, sysUICutout, getResourcesForRotation(rotation, context), key)
+ return insetsCache[key]
+ ?: getAndSetCalculatedAreaForRotation(
+ rotation,
+ sysUICutout,
+ getResourcesForRotation(rotation, context),
+ key
+ )
}
- /**
- * Get the status bar content area for the given rotation, in absolute bounds
- */
+ /** Get the status bar content area for the given rotation, in absolute bounds */
fun getStatusBarContentAreaForCurrentRotation(): Rect {
val rotation = getExactRotation(context)
return getStatusBarContentAreaForRotation(rotation)
@@ -237,10 +251,9 @@ class StatusBarContentInsetsProvider @Inject constructor(
rotatedResources: Resources,
key: CacheKey
): Rect {
- return getCalculatedAreaForRotation(sysUICutout, targetRotation, rotatedResources)
- .also {
- insetsCache.put(key, it)
- }
+ return getCalculatedAreaForRotation(sysUICutout, targetRotation, rotatedResources).also {
+ insetsCache.put(key, it)
+ }
}
private fun getCalculatedAreaForRotation(
@@ -250,12 +263,14 @@ class StatusBarContentInsetsProvider @Inject constructor(
): Rect {
val currentRotation = getExactRotation(context)
- val roundedCornerPadding = rotatedResources
- .getDimensionPixelSize(R.dimen.rounded_corner_content_padding)
- val minDotPadding = if (isPrivacyDotEnabled)
+ val roundedCornerPadding =
+ rotatedResources.getDimensionPixelSize(R.dimen.rounded_corner_content_padding)
+ val minDotPadding =
+ if (isPrivacyDotEnabled)
rotatedResources.getDimensionPixelSize(R.dimen.ongoing_appops_dot_min_padding)
else 0
- val dotWidth = if (isPrivacyDotEnabled)
+ val dotWidth =
+ if (isPrivacyDotEnabled)
rotatedResources.getDimensionPixelSize(R.dimen.ongoing_appops_dot_diameter)
else 0
@@ -271,20 +286,21 @@ class StatusBarContentInsetsProvider @Inject constructor(
val bottomAlignedMargin = getBottomAlignedMargin(targetRotation, rotatedResources)
val statusBarContentHeight =
- rotatedResources.getDimensionPixelSize(R.dimen.status_bar_icon_size_sp)
+ rotatedResources.getDimensionPixelSize(R.dimen.status_bar_icon_size_sp)
return calculateInsetsForRotationWithRotatedResources(
- currentRotation,
- targetRotation,
- sysUICutout,
- context.resources.configuration.windowConfiguration.maxBounds,
- SystemBarUtils.getStatusBarHeightForRotation(context, targetRotation),
- minLeft,
- minRight,
- configurationController.isLayoutRtl,
- dotWidth,
- bottomAlignedMargin,
- statusBarContentHeight)
+ currentRotation,
+ targetRotation,
+ sysUICutout,
+ context.resources.configuration.windowConfiguration.maxBounds,
+ SystemBarUtils.getStatusBarHeightForRotation(context, targetRotation),
+ minLeft,
+ minRight,
+ configurationController.isLayoutRtl,
+ dotWidth,
+ bottomAlignedMargin,
+ statusBarContentHeight
+ )
}
private fun executeCommand(command: StatusBarInsetsCommand, printWriter: PrintWriter) {
@@ -295,7 +311,7 @@ class StatusBarContentInsetsProvider @Inject constructor(
val rotation = command.rotationValue
if (rotation == null) {
printWriter.println(
- "Rotation should be one of ${BottomMarginCommand.ROTATION_DEGREES_OPTIONS}"
+ "Rotation should be one of ${BottomMarginCommand.ROTATION_DEGREES_OPTIONS}"
)
return
}
@@ -323,13 +339,13 @@ class StatusBarContentInsetsProvider @Inject constructor(
return override
}
val dimenRes =
- when (targetRotation) {
- Surface.ROTATION_0 -> R.dimen.status_bar_bottom_aligned_margin_rotation_0
- Surface.ROTATION_90 -> R.dimen.status_bar_bottom_aligned_margin_rotation_90
- Surface.ROTATION_180 -> R.dimen.status_bar_bottom_aligned_margin_rotation_180
- Surface.ROTATION_270 -> R.dimen.status_bar_bottom_aligned_margin_rotation_270
- else -> throw IllegalStateException("Unknown rotation: $targetRotation")
- }
+ when (targetRotation) {
+ Surface.ROTATION_0 -> R.dimen.status_bar_bottom_aligned_margin_rotation_0
+ Surface.ROTATION_90 -> R.dimen.status_bar_bottom_aligned_margin_rotation_90
+ Surface.ROTATION_180 -> R.dimen.status_bar_bottom_aligned_margin_rotation_180
+ Surface.ROTATION_270 -> R.dimen.status_bar_bottom_aligned_margin_rotation_270
+ else -> throw IllegalStateException("Unknown rotation: $targetRotation")
+ }
return resources.getDimensionPixelSize(dimenRes)
}
@@ -339,17 +355,12 @@ class StatusBarContentInsetsProvider @Inject constructor(
}
override fun dump(pw: PrintWriter, args: Array<out String>) {
- insetsCache.snapshot().forEach { (key, rect) ->
- pw.println("$key -> $rect")
- }
+ insetsCache.snapshot().forEach { (key, rect) -> pw.println("$key -> $rect") }
pw.println(insetsCache)
pw.println("Bottom margin overrides: $marginBottomOverrides")
}
- private fun getCacheKey(
- @Rotation rotation: Int,
- displayCutout: DisplayCutout?
- ): CacheKey =
+ private fun getCacheKey(@Rotation rotation: Int, displayCutout: DisplayCutout?): CacheKey =
CacheKey(
rotation = rotation,
displaySize = Rect(context.resources.configuration.windowConfiguration.maxBounds),
@@ -387,15 +398,19 @@ fun getPrivacyChipBoundingRectForInsets(
isRtl: Boolean
): Rect {
return if (isRtl) {
- Rect(contentRect.left - dotWidth,
- contentRect.top,
- contentRect.left + chipWidth,
- contentRect.bottom)
+ Rect(
+ contentRect.left - dotWidth,
+ contentRect.top,
+ contentRect.left + chipWidth,
+ contentRect.bottom
+ )
} else {
- Rect(contentRect.right - chipWidth,
- contentRect.top,
- contentRect.right + dotWidth,
- contentRect.bottom)
+ Rect(
+ contentRect.right - chipWidth,
+ contentRect.top,
+ contentRect.right + dotWidth,
+ contentRect.bottom
+ )
}
}
@@ -439,20 +454,21 @@ fun calculateInsetsForRotationWithRotatedResources(
val rotZeroBounds = getRotationZeroDisplayBounds(maxBounds, currentRotation)
return getStatusBarContentBounds(
- sysUICutout,
- statusBarHeight,
- rotZeroBounds.right,
- rotZeroBounds.bottom,
- maxBounds.width(),
- maxBounds.height(),
- minLeft,
- minRight,
- isRtl,
- dotWidth,
- targetRotation,
- currentRotation,
- bottomAlignedMargin,
- statusBarContentHeight)
+ sysUICutout,
+ statusBarHeight,
+ rotZeroBounds.right,
+ rotZeroBounds.bottom,
+ maxBounds.width(),
+ maxBounds.height(),
+ minLeft,
+ minRight,
+ isRtl,
+ dotWidth,
+ targetRotation,
+ currentRotation,
+ bottomAlignedMargin,
+ statusBarContentHeight
+ )
}
/**
@@ -470,31 +486,30 @@ fun calculateInsetsForRotationWithRotatedResources(
* @param dotWidth privacy dot image width (0 if privacy dot is disabled)
* @param targetRotation the rotation for which to calculate margins
* @param currentRotation the rotation from which the display cutout was generated
- *
* @return a Rect which exactly calculates the Status Bar's content rect relative to the target
- * rotation
+ * rotation
*/
private fun getStatusBarContentBounds(
- sysUICutout: SysUICutoutInformation?,
- sbHeight: Int,
- width: Int,
- height: Int,
- cWidth: Int,
- cHeight: Int,
- minLeft: Int,
- minRight: Int,
- isRtl: Boolean,
- dotWidth: Int,
- @Rotation targetRotation: Int,
- @Rotation currentRotation: Int,
- bottomAlignedMargin: Int,
- statusBarContentHeight: Int
+ sysUICutout: SysUICutoutInformation?,
+ sbHeight: Int,
+ width: Int,
+ height: Int,
+ cWidth: Int,
+ cHeight: Int,
+ minLeft: Int,
+ minRight: Int,
+ isRtl: Boolean,
+ dotWidth: Int,
+ @Rotation targetRotation: Int,
+ @Rotation currentRotation: Int,
+ bottomAlignedMargin: Int,
+ statusBarContentHeight: Int
): Rect {
val insetTop = getInsetTop(bottomAlignedMargin, statusBarContentHeight, sbHeight)
val logicalDisplayWidth = if (targetRotation.isHorizontal()) height else width
- // Exclude the bottom rect, as it doesn't intersect with the status bar.
+ // Exclude the bottom rect, as it doesn't intersect with the status bar.
val cutoutRects = sysUICutout?.cutout?.boundingRectsLeftRightTop
if (cutoutRects.isNullOrEmpty()) {
return Rect(minLeft, insetTop, logicalDisplayWidth - minRight, sbHeight)
@@ -513,7 +528,7 @@ private fun getStatusBarContentBounds(
var leftMargin = minLeft
var rightMargin = minRight
for (cutoutRect in cutoutRects) {
- val protectionRect = sysUICutout.cameraProtection?.cutoutBounds
+ val protectionRect = sysUICutout.cameraProtection?.bounds
val actualCutoutRect =
if (protectionRect?.intersects(cutoutRect) == true) {
rectUnion(cutoutRect, protectionRect)
@@ -580,9 +595,9 @@ private val DisplayCutout.boundingRectsLeftRightTop
*/
@Px
private fun getInsetTop(
- bottomAlignedMargin: Int,
- statusBarContentHeight: Int,
- statusBarHeight: Int
+ bottomAlignedMargin: Int,
+ statusBarContentHeight: Int,
+ statusBarHeight: Int
): Int {
val bottomAlignmentEnabled = bottomAlignedMargin >= 0
if (!bottomAlignmentEnabled) {
@@ -672,7 +687,8 @@ private fun Rect.logicalLeft(@Rotation rot: Int): Int {
private fun Rect.logicalWidth(@Rotation rot: Int): Int {
return when (rot) {
- ROTATION_NONE, ROTATION_UPSIDE_DOWN -> width()
+ ROTATION_NONE,
+ ROTATION_UPSIDE_DOWN -> width()
else /* LANDSCAPE, SEASCAPE */ -> height()
}
}
@@ -683,7 +699,8 @@ private fun Int.isHorizontal(): Boolean {
private fun Point.orientToRotZero(@Rotation rot: Int) {
when (rot) {
- ROTATION_NONE, ROTATION_UPSIDE_DOWN -> return
+ ROTATION_NONE,
+ ROTATION_UPSIDE_DOWN -> return
else -> {
// swap width and height to zero-orient bounds
val yTmp = y
@@ -695,7 +712,8 @@ private fun Point.orientToRotZero(@Rotation rot: Int) {
private fun Point.logicalWidth(@Rotation rot: Int): Int {
return when (rot) {
- ROTATION_NONE, ROTATION_UPSIDE_DOWN -> x
+ ROTATION_NONE,
+ ROTATION_UPSIDE_DOWN -> x
else -> y
}
}
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 9d70f4221e3a..29fd2258b40c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -86,10 +86,9 @@ import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.shade.ShadeExpansionListener;
import com.android.systemui.shade.ShadeExpansionStateManager;
-import com.android.systemui.shade.ShadeViewController;
+import com.android.systemui.shade.ShadeLockscreenInteractor;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.SysUiStatsLog;
-import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
@@ -269,7 +268,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
protected LockPatternUtils mLockPatternUtils;
protected ViewMediatorCallback mViewMediatorCallback;
@Nullable protected CentralSurfaces mCentralSurfaces;
- private ShadeViewController mShadeViewController;
+ private ShadeLockscreenInteractor mShadeLockscreenInteractor;
private BiometricUnlockController mBiometricUnlockController;
private boolean mCentralSurfacesRegistered;
@@ -314,7 +313,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
// Dismiss action to be launched when we stop dozing or the keyguard is gone.
private DismissWithActionRequest mPendingWakeupAction;
private final KeyguardStateController mKeyguardStateController;
- private final NotificationMediaManager mMediaManager;
private final SysuiStatusBarStateController mStatusBarStateController;
private final DockManager mDockManager;
private final KeyguardUpdateMonitor mKeyguardUpdateManager;
@@ -363,7 +361,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
DockManager dockManager,
NotificationShadeWindowController notificationShadeWindowController,
KeyguardStateController keyguardStateController,
- NotificationMediaManager notificationMediaManager,
KeyguardMessageAreaController.Factory keyguardMessageAreaFactory,
Optional<SysUIUnfoldComponent> sysUIUnfoldComponent,
Lazy<ShadeController> shadeController,
@@ -391,7 +388,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mNotificationShadeWindowController = notificationShadeWindowController;
mDreamOverlayStateController = dreamOverlayStateController;
mKeyguardStateController = keyguardStateController;
- mMediaManager = notificationMediaManager;
mKeyguardUpdateManager = keyguardUpdateMonitor;
mStatusBarStateController = sysuiStatusBarStateController;
mDockManager = dockManager;
@@ -422,7 +418,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
@Override
public void registerCentralSurfaces(CentralSurfaces centralSurfaces,
- ShadeViewController shadeViewController,
+ ShadeLockscreenInteractor shadeLockscreenInteractor,
ShadeExpansionStateManager shadeExpansionStateManager,
BiometricUnlockController biometricUnlockController,
View notificationContainer,
@@ -431,7 +427,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mBiometricUnlockController = biometricUnlockController;
mPrimaryBouncerCallbackInteractor.addBouncerExpansionCallback(mExpansionCallback);
- mShadeViewController = shadeViewController;
+ mShadeLockscreenInteractor = shadeLockscreenInteractor;
if (shadeExpansionStateManager != null) {
ShadeExpansionChangeEvent currentState =
shadeExpansionStateManager.addExpansionListener(this);
@@ -565,8 +561,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
// Avoid having the shade and the bouncer open at the same time over a dream.
final boolean hideBouncerOverDream =
mDreamOverlayStateController.isOverlayActive()
- && (mShadeViewController.isExpanded()
- || mShadeViewController.isExpandingOrCollapsing());
+ && (mShadeLockscreenInteractor.isExpanded()
+ || mShadeLockscreenInteractor.isExpandingOrCollapsing());
final boolean isUserTrackingStarted =
event.getFraction() != EXPANSION_HIDDEN && event.getTracking();
@@ -834,7 +830,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
if (mKeyguardStateController.isShowing() && !bouncerIsAnimatingAway()) {
final boolean isOccluded = mKeyguardStateController.isOccluded();
// Hide quick settings.
- mShadeViewController.resetViews(/* animate= */ !isOccluded);
+ mShadeLockscreenInteractor.resetViews(/* animate= */ !isOccluded);
// Hide bouncer and quick-quick settings.
if (isOccluded && !mDozing) {
mCentralSurfaces.hideKeyguard();
@@ -1008,7 +1004,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
public void startPreHideAnimation(Runnable finishRunnable) {
if (primaryBouncerIsShowing()) {
mPrimaryBouncerInteractor.startDisappearAnimation(finishRunnable);
- mShadeViewController.startBouncerPreHideAnimation();
+ mShadeLockscreenInteractor.startBouncerPreHideAnimation();
// We update the state (which will show the keyguard) only if an animation will run on
// the keyguard. If there is no animation, we wait before updating the state so that we
@@ -1023,12 +1019,12 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
} else if (finishRunnable != null) {
finishRunnable.run();
}
- mShadeViewController.blockExpansionForCurrentTouch();
+ mShadeLockscreenInteractor.blockExpansionForCurrentTouch();
}
@Override
public void blockPanelExpansionFromCurrentTouch() {
- mShadeViewController.blockExpansionForCurrentTouch();
+ mShadeLockscreenInteractor.blockExpansionForCurrentTouch();
}
@Override
@@ -1125,7 +1121,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
public void onKeyguardFadedAway() {
mNotificationContainer.postDelayed(() -> mNotificationShadeWindowController
.setKeyguardFadingAway(false), 100);
- mShadeViewController.resetViewGroupFade();
+ mShadeLockscreenInteractor.resetViewGroupFade();
mCentralSurfaces.finishKeyguardFadingAway();
mBiometricUnlockController.finishKeyguardFadingAway();
}
@@ -1208,7 +1204,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
if (hideImmediately) {
mStatusBarStateController.setLeaveOpenOnKeyguardHide(false);
} else {
- mShadeViewController.expandToNotifications();
+ mShadeLockscreenInteractor.expandToNotifications();
}
}
return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 4fd33ba458d8..5610ed926f70 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -55,6 +55,7 @@ import com.android.systemui.EventLogTags;
import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.power.domain.interactor.PowerInteractor;
@@ -138,7 +139,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit
Context context,
@DisplayId int displayId,
Handler mainThreadHandler,
- Executor uiBgExecutor,
+ @Background Executor uiBgExecutor,
NotificationVisibilityProvider visibilityProvider,
HeadsUpManager headsUpManager,
ActivityStarter activityStarter,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogFactory.kt
index 553edf9b5d13..1edd4d11351c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogFactory.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone
import android.content.Context
+import androidx.annotation.GravityInt
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.qualifiers.Application
@@ -43,11 +44,14 @@ constructor(
* @param context the [Context] in which the dialog will be constructed.
* @param dismissOnDeviceLock whether the dialog should be automatically dismissed when the
* device is locked (true by default).
+ * @param dialogGravity is one of the [android.view.Gravity] and determines dialog position on
+ * the screen.
*/
fun create(
context: Context = this.applicationContext,
theme: Int = SystemUIDialog.DEFAULT_THEME,
dismissOnDeviceLock: Boolean = SystemUIDialog.DEFAULT_DISMISS_ON_DEVICE_LOCK,
+ @GravityInt dialogGravity: Int? = null,
): ComponentSystemUIDialog {
Assert.isMainThread()
@@ -59,6 +63,7 @@ constructor(
sysUiState,
broadcastDispatcher,
dialogTransitionAnimator,
+ dialogGravity,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index 665a5714e277..223eaf74e2e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -17,10 +17,10 @@ import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF
import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF_SHOW_AOD
import com.android.systemui.DejankUtils
import com.android.app.animation.Interpolators
+import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.keyguard.WakefulnessLifecycle
-import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.shade.ShadeViewController
import com.android.systemui.statusbar.CircleReveal
import com.android.systemui.statusbar.LightRevealScrim
@@ -286,7 +286,7 @@ class UnlockedScreenOffAnimationController @Inject constructor(
// up, with unpredictable consequences.
if (!powerManager.isInteractive(Display.DEFAULT_DISPLAY) &&
shouldAnimateInKeyguard) {
- if (!KeyguardShadeMigrationNssl.isEnabled) {
+ if (!migrateClocksToBlueprint()) {
// Tracking this state should no longer be relevant, as the isInteractive
// check covers it
aodUiAnimationPlaying = true
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
index f73d089c36b9..3e3ea855ccf7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
@@ -315,8 +315,8 @@ constructor(
// TTL for satellite polling is one hour
const val POLLING_INTERVAL_MS: Long = 1000 * 60 * 60
- // Let the system boot up (5s) and stabilize before we check for system support
- const val MIN_UPTIME: Long = 1000 * 5
+ // Let the system boot up and stabilize before we check for system support
+ const val MIN_UPTIME: Long = 1000 * 60
private const val TAG = "DeviceBasedSatelliteRepo"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
index 1c33d3fd0288..bef6b0ba483f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
@@ -18,15 +18,22 @@ package com.android.systemui.statusbar.pipeline.satellite.ui.viewmodel
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository
+import com.android.systemui.statusbar.pipeline.dagger.OemSatelliteInputLog
import com.android.systemui.statusbar.pipeline.satellite.domain.interactor.DeviceBasedSatelliteInteractor
import com.android.systemui.statusbar.pipeline.satellite.ui.model.SatelliteIconModel
import javax.inject.Inject
+import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.stateIn
@@ -42,25 +49,44 @@ constructor(
interactor: DeviceBasedSatelliteInteractor,
@Application scope: CoroutineScope,
airplaneModeRepository: AirplaneModeRepository,
+ @OemSatelliteInputLog logBuffer: LogBuffer,
) {
- private val shouldShowIcon: StateFlow<Boolean> =
- interactor.areAllConnectionsOutOfService
- .flatMapLatest { allOos ->
- if (!allOos) {
- flowOf(false)
+ private val shouldShowIcon: Flow<Boolean> =
+ interactor.areAllConnectionsOutOfService.flatMapLatest { allOos ->
+ if (!allOos) {
+ flowOf(false)
+ } else {
+ combine(interactor.isSatelliteAllowed, airplaneModeRepository.isAirplaneMode) {
+ isSatelliteAllowed,
+ isAirplaneMode ->
+ isSatelliteAllowed && !isAirplaneMode
+ }
+ }
+ }
+
+ // This adds a 10 seconds delay before showing the icon
+ private val shouldActuallyShowIcon: StateFlow<Boolean> =
+ shouldShowIcon
+ .distinctUntilChanged()
+ .flatMapLatest { shouldShow ->
+ if (shouldShow) {
+ logBuffer.log(
+ TAG,
+ LogLevel.INFO,
+ { long1 = DELAY_DURATION.inWholeSeconds },
+ { "Waiting $long1 seconds before showing the satellite icon" }
+ )
+ delay(DELAY_DURATION)
+ flowOf(true)
} else {
- combine(interactor.isSatelliteAllowed, airplaneModeRepository.isAirplaneMode) {
- isSatelliteAllowed,
- isAirplaneMode ->
- isSatelliteAllowed && !isAirplaneMode
- }
+ flowOf(false)
}
}
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
val icon: StateFlow<Icon?> =
combine(
- shouldShowIcon,
+ shouldActuallyShowIcon,
interactor.connectionState,
interactor.signalStrength,
) { shouldShow, state, signalStrength ->
@@ -71,4 +97,9 @@ constructor(
}
}
.stateIn(scope, SharingStarted.WhileSubscribed(), null)
+
+ companion object {
+ private const val TAG = "DeviceBasedSatelliteViewModel"
+ private val DELAY_DURATION = 10.seconds
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
index 1414150c5511..2c1780d3b304 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
@@ -158,7 +158,15 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager {
@Override
public void showNotification(@NonNull NotificationEntry entry) {
mLogger.logShowNotification(entry);
- addEntry(entry);
+
+ // Add new entry and begin managing it
+ HeadsUpEntry headsUpEntry = createHeadsUpEntry();
+ headsUpEntry.setEntry(entry);
+ mHeadsUpEntryMap.put(entry.getKey(), headsUpEntry);
+ onEntryAdded(headsUpEntry);
+ entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+ entry.setIsHeadsUpEntry(true);
+
updateNotification(entry.getKey(), true /* shouldHeadsUpAgain */);
entry.setInterruption();
}
@@ -319,19 +327,6 @@ public abstract class BaseHeadsUpManager implements HeadsUpManager {
}
/**
- * Add a new entry and begin managing it.
- * @param entry the entry to add
- */
- protected final void addEntry(@NonNull NotificationEntry entry) {
- HeadsUpEntry headsUpEntry = createHeadsUpEntry();
- headsUpEntry.setEntry(entry);
- mHeadsUpEntryMap.put(entry.getKey(), headsUpEntry);
- onEntryAdded(headsUpEntry);
- entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
- entry.setIsHeadsUpEntry(true);
- }
-
- /**
* Manager-specific logic that should occur when an entry is added.
* @param headsUpEntry entry added
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt
index a078dd5cf28c..2ad4d361df1f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt
@@ -23,6 +23,7 @@ import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.net.Uri
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.res.R
import com.android.systemui.util.concurrency.DelayableExecutor
import javax.inject.Inject
@@ -34,7 +35,7 @@ import javax.inject.Inject
class BatteryStateNotifier @Inject constructor(
val controller: BatteryController,
val noMan: NotificationManager,
- val delayableExecutor: DelayableExecutor,
+ @Background val delayableExecutor: DelayableExecutor,
val context: Context
) : BatteryController.BatteryStateChangeCallback {
var stateUnknown = false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
index b2ef818d3282..cec77c12a40b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
@@ -33,6 +33,9 @@ public interface ConfigurationController extends CallbackController<Configuratio
/** Query the current configuration's layout direction */
boolean isLayoutRtl();
+ /** Logging only; Query the current configuration's night mode name */
+ String getNightModeName();
+
interface ConfigurationListener {
default void onConfigChanged(Configuration newConfig) {}
default void onDensityOrFontScaleChanged() {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt
index a7352be8d80a..420701f026d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt
@@ -61,16 +61,16 @@ interface HeadsUpManager : Dumpable {
fun getTouchableRegion(): Region?
/**
- * Whether or not there are any active alerting notifications.
+ * Whether or not there are any entries managed by HeadsUpManager.
*
- * @return true if there is an alert, false otherwise
+ * @return true if there is a heads up entry, false otherwise
*/
fun hasNotifications(): Boolean = false
/** Returns whether there are any pinned Heads Up Notifications or not. */
fun hasPinnedHeadsUp(): Boolean
- /** Returns whether or not the given notification is alerting and managed by this manager. */
+ /** Returns whether or not the given notification is managed by this manager. */
fun isHeadsUpEntry(key: String): Boolean
fun isHeadsUpGoingAway(): Boolean
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java
index eb08f37503c6..eba3162febe5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java
@@ -16,9 +16,12 @@
package com.android.systemui.statusbar.policy;
+import android.annotation.FlaggedApi;
import android.hardware.SensorPrivacyManager.Sensors.Sensor;
import android.hardware.SensorPrivacyManager.Sources.Source;
+import com.android.internal.camera.flags.Flags;
+
public interface IndividualSensorPrivacyController extends
CallbackController<IndividualSensorPrivacyController.Callback> {
void init();
@@ -42,6 +45,12 @@ public interface IndividualSensorPrivacyController extends
*/
boolean requiresAuthentication();
+ /**
+ * @return whether camera privacy is enabled for the package.
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
+ boolean isCameraPrivacyEnabled(String packageName);
+
interface Callback {
void onSensorBlockedChanged(@Sensor int sensor, boolean blocked);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
index 87dfc9962675..58b82f166623 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
@@ -19,6 +19,9 @@ package com.android.systemui.statusbar.policy;
import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
+import android.Manifest;
+import android.annotation.FlaggedApi;
+import android.annotation.RequiresPermission;
import android.hardware.SensorPrivacyManager;
import android.hardware.SensorPrivacyManager.Sensors.Sensor;
import android.hardware.SensorPrivacyManager.Sources.Source;
@@ -28,6 +31,8 @@ import android.util.SparseBooleanArray;
import androidx.annotation.NonNull;
+import com.android.internal.camera.flags.Flags;
+
import java.util.Set;
public class IndividualSensorPrivacyControllerImpl implements IndividualSensorPrivacyController {
@@ -102,6 +107,13 @@ public class IndividualSensorPrivacyControllerImpl implements IndividualSensorPr
}
@Override
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
+ @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ public boolean isCameraPrivacyEnabled(String packageName) {
+ return mSensorPrivacyManager.isCameraPrivacyEnabled(packageName);
+ }
+
+ @Override
public void addCallback(@NonNull Callback listener) {
mCallbacks.add(listener);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java
index 2b0a92c6ecd7..18ec68bd89eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java
@@ -51,6 +51,7 @@ import javax.inject.Inject;
public class SensitiveNotificationProtectionControllerImpl
implements SensitiveNotificationProtectionController {
private static final String LOG_TAG = "SNPC";
+ private final SensitiveNotificationProtectionControllerLogger mLogger;
private final ArraySet<String> mExemptPackages = new ArraySet<>();
private final ListenerSet<Runnable> mListeners = new ListenerSet<>();
private volatile MediaProjectionInfo mProjection;
@@ -66,6 +67,7 @@ public class SensitiveNotificationProtectionControllerImpl
if (mDisableScreenShareProtections) {
Log.w(LOG_TAG,
"Screen share protections disabled, ignoring projectionstart");
+ mLogger.logProjectionStart(false, info.getPackageName());
return;
}
@@ -73,6 +75,7 @@ public class SensitiveNotificationProtectionControllerImpl
// Launch cookie only set (non-null) if sharing single app/task
updateProjectionStateAndNotifyListeners(
(info.getLaunchCookie() == null) ? info : null);
+ mLogger.logProjectionStart(isSensitiveStateActive(), info.getPackageName());
} finally {
Trace.endSection();
}
@@ -82,6 +85,7 @@ public class SensitiveNotificationProtectionControllerImpl
public void onStop(MediaProjectionInfo info) {
Trace.beginSection("SNPC.onProjectionStop");
try {
+ mLogger.logProjectionStop();
updateProjectionStateAndNotifyListeners(null);
} finally {
Trace.endSection();
@@ -96,7 +100,10 @@ public class SensitiveNotificationProtectionControllerImpl
MediaProjectionManager mediaProjectionManager,
IActivityManager activityManager,
@Main Handler mainHandler,
- @Background Executor bgExecutor) {
+ @Background Executor bgExecutor,
+ SensitiveNotificationProtectionControllerLogger logger) {
+ mLogger = logger;
+
if (!screenshareNotificationHiding()) {
return;
}
@@ -202,8 +209,6 @@ public class SensitiveNotificationProtectionControllerImpl
return false;
}
- // TODO(b/316955558): Add disabled by developer option
-
return !mExemptPackages.contains(projection.getPackageName());
}
@@ -221,10 +226,15 @@ public class SensitiveNotificationProtectionControllerImpl
// Exempt foreground service notifications from protection in effort to keep screen share
// stop actions easily accessible
StatusBarNotification sbn = entry.getSbn();
- if (sbn.getNotification().isFgsOrUij()) {
- return !sbn.getPackageName().equals(projection.getPackageName());
+ if (sbn.getNotification().isFgsOrUij()
+ && sbn.getPackageName().equals(projection.getPackageName())) {
+ return false;
}
- return true;
+ // Only protect/redact notifications if the developer has not explicitly set notification
+ // visibility as public and users has not adjusted default channel visibility to private
+ boolean notificationRequestsRedaction = entry.isNotificationVisibilityPrivate();
+ boolean userForcesRedaction = entry.isChannelVisibilityPrivate();
+ return notificationRequestsRedaction || userForcesRedaction;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerLogger.kt
new file mode 100644
index 000000000000..70c5239f0ec6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerLogger.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.dagger.SensitiveNotificationProtectionLog
+import javax.inject.Inject
+
+/** Logger for [SensitiveNotificationProtectionController]. */
+class SensitiveNotificationProtectionControllerLogger
+@Inject
+constructor(@SensitiveNotificationProtectionLog private val buffer: LogBuffer) {
+ fun logProjectionStart(protectionEnabled: Boolean, pkg: String) {
+ buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ bool1 = protectionEnabled
+ str1 = pkg
+ },
+ { "Projection started - protection enabled:$bool1, pkg=$str1" }
+ )
+ }
+
+ fun logProjectionStop() {
+ buffer.log(TAG, LogLevel.DEBUG, {}, { "Projection ended - protection disabled" })
+ }
+}
+
+private const val TAG = "SNPC"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index df210b073e77..600005b97610 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.policy;
+import static com.android.systemui.Flags.registerZenModeContentObserverBackground;
+
import android.app.AlarmManager;
import android.app.Flags;
import android.app.NotificationManager;
@@ -45,6 +47,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dumpable;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.settings.UserTracker;
@@ -104,6 +107,7 @@ public class ZenModeControllerImpl implements ZenModeController, Dumpable {
public ZenModeControllerImpl(
Context context,
@Main Handler handler,
+ @Background Handler bgHandler,
BroadcastDispatcher broadcastDispatcher,
DumpManager dumpManager,
GlobalSettings globalSettings,
@@ -134,9 +138,18 @@ public class ZenModeControllerImpl implements ZenModeController, Dumpable {
}
};
mNoMan = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
- globalSettings.registerContentObserver(Global.ZEN_MODE, modeContentObserver);
+ if (registerZenModeContentObserverBackground()) {
+ bgHandler.post(() -> {
+ globalSettings.registerContentObserver(Global.ZEN_MODE, modeContentObserver);
+ globalSettings.registerContentObserver(Global.ZEN_MODE_CONFIG_ETAG,
+ configContentObserver);
+ });
+ } else {
+ globalSettings.registerContentObserver(Global.ZEN_MODE, modeContentObserver);
+ globalSettings.registerContentObserver(Global.ZEN_MODE_CONFIG_ETAG,
+ configContentObserver);
+ }
updateZenMode(getModeSettingValueFromProvider());
- globalSettings.registerContentObserver(Global.ZEN_MODE_CONFIG_ETAG, configContentObserver);
updateZenModeConfig();
updateConsolidatedNotificationPolicy();
mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 585ab72dd963..44c684c34587 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -815,7 +815,7 @@ public class ThemeOverlayController implements CoreStartable, Dumpable {
? () -> {}
: () -> {
Log.d(TAG, "ThemeHomeDelay: ThemeOverlayController ready");
- mActivityManager.setThemeOverlayReady(true);
+ mActivityManager.setThemeOverlayReady(currentUser);
};
if (colorSchemeIsApplied(managedProfiles)) {
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
index 4dfd5a1bae46..b3e60e35d89e 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
@@ -29,25 +29,31 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.power.shared.model.ScreenPowerState
import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.shared.system.SysUiStatsLog
+import com.android.systemui.unfold.DisplaySwitchLatencyTracker.DisplaySwitchLatencyEvent
import com.android.systemui.unfold.dagger.UnfoldSingleThreadBg
import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
import com.android.systemui.util.Compile
import com.android.systemui.util.Utils.isDeviceFoldable
import com.android.systemui.util.animation.data.repository.AnimationStatusRepository
import com.android.systemui.util.kotlin.pairwise
+import com.android.systemui.util.kotlin.race
import com.android.systemui.util.time.SystemClock
import com.android.systemui.util.time.measureTimeMillis
+import java.time.Duration
import java.util.concurrent.Executor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.launch
+import kotlinx.coroutines.withTimeout
/**
* [DisplaySwitchLatencyTracker] tracks latency and related fields for display switch of a foldable
@@ -70,6 +76,8 @@ constructor(
) : CoreStartable {
private val backgroundDispatcher = singleThreadBgExecutor.asCoroutineDispatcher()
+ private val isAodEnabled: Boolean
+ get() = keyguardInteractor.isAodAvailable.value
@OptIn(ExperimentalCoroutinesApi::class)
override fun start() {
@@ -98,8 +106,14 @@ constructor(
val displaySwitchTimeMs =
measureTimeMillis(systemClock) {
- traceAsync(TAG, "displaySwitch") {
- waitForDisplaySwitch(toFoldableDeviceState)
+ try {
+ withTimeout(SCREEN_EVENT_TIMEOUT) {
+ traceAsync(TAG, "displaySwitch") {
+ waitForDisplaySwitch(toFoldableDeviceState)
+ }
+ }
+ } catch (e: TimeoutCancellationException) {
+ Log.e(TAG, "Wait for display switch timed out")
}
}
@@ -129,19 +143,19 @@ constructor(
val isTransitionEnabled =
unfoldTransitionInteractor.isAvailable &&
animationStatusRepository.areAnimationsEnabled().first()
- if (shouldWaitForScreenOn(toFoldableDeviceState, isTransitionEnabled)) {
- waitForScreenTurnedOn()
- } else {
+ if (shouldWaitForTransitionStart(toFoldableDeviceState, isTransitionEnabled)) {
traceAsync(TAG, "waitForTransitionStart()") {
unfoldTransitionInteractor.waitForTransitionStart()
}
+ } else {
+ race({ waitForScreenTurnedOn() }, { waitForGoToSleepWithScreenOff() })
}
}
- private fun shouldWaitForScreenOn(
+ private fun shouldWaitForTransitionStart(
toFoldableDeviceState: Int,
isTransitionEnabled: Boolean
- ): Boolean = (toFoldableDeviceState == FOLDABLE_DEVICE_STATE_CLOSED || !isTransitionEnabled)
+ ): Boolean = (toFoldableDeviceState != FOLDABLE_DEVICE_STATE_CLOSED && isTransitionEnabled)
private suspend fun waitForScreenTurnedOn() {
traceAsync(TAG, "waitForScreenTurnedOn()") {
@@ -149,19 +163,30 @@ constructor(
}
}
+ private suspend fun waitForGoToSleepWithScreenOff() {
+ traceAsync(TAG, "waitForGoToSleepWithScreenOff()") {
+ powerInteractor.detailedWakefulness
+ .filter { it.internalWakefulnessState == WakefulnessState.ASLEEP && !isAodEnabled }
+ .first()
+ }
+ }
+
private fun getCurrentState(): Int =
when {
isStateAod() -> SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TO_STATE__AOD
+ isStateScreenOff() -> SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TO_STATE__SCREEN_OFF
else -> SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TO_STATE__UNKNOWN
}
- private fun isStateAod(): Boolean {
+ private fun isStateAod(): Boolean = (isAsleepDueToFold() && isAodEnabled)
+
+ private fun isStateScreenOff(): Boolean = (isAsleepDueToFold() && !isAodEnabled)
+
+ private fun isAsleepDueToFold(): Boolean {
val lastWakefulnessEvent = powerInteractor.detailedWakefulness.value
- val isAodEnabled = keyguardInteractor.isAodAvailable.value
return (lastWakefulnessEvent.isAsleep() &&
- (lastWakefulnessEvent.lastSleepReason == WakeSleepReason.FOLD) &&
- isAodEnabled)
+ (lastWakefulnessEvent.lastSleepReason == WakeSleepReason.FOLD))
}
private inline fun log(msg: () -> String) {
@@ -232,6 +257,7 @@ constructor(
private const val VALUE_UNKNOWN = -1
private const val TAG = "DisplaySwitchLatency"
private val DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE)
+ private val SCREEN_EVENT_TIMEOUT = Duration.ofMillis(15000).toMillis()
private const val FOLDABLE_DEVICE_STATE_UNKNOWN =
SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__FROM_FOLDABLE_DEVICE_STATE__STATE_UNKNOWN
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FullscreenLightRevealAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/FullscreenLightRevealAnimation.kt
index 668b1439abab..ca5ea3bc1caa 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/FullscreenLightRevealAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FullscreenLightRevealAnimation.kt
@@ -45,7 +45,6 @@ import com.android.wm.shell.displayareahelper.DisplayAreaHelper
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
-import java.lang.IllegalArgumentException
import java.util.Optional
import java.util.concurrent.Executor
import java.util.function.Consumer
@@ -71,7 +70,7 @@ constructor(
private val displayTracker: DisplayTracker,
@Background private val applicationScope: CoroutineScope,
@Main private val executor: Executor,
- @Assisted private val displaySelector: Sequence<DisplayInfo>.() -> DisplayInfo?,
+ @Assisted private val displaySelector: List<DisplayInfo>.() -> DisplayInfo?,
@Assisted private val lightRevealEffectFactory: (rotation: Int) -> LightRevealEffect,
@Assisted private val overlayContainerName: String
) {
@@ -84,13 +83,11 @@ constructor(
private var scrimView: LightRevealScrim? = null
private val rotationWatcher = RotationWatcher()
- private val internalDisplayInfos: Sequence<DisplayInfo>
- get() =
- displayManager
- .getDisplays(DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)
- .asSequence()
- .map { DisplayInfo().apply { it.getDisplayInfo(this) } }
- .filter { it.type == Display.TYPE_INTERNAL }
+ private val internalDisplayInfos: List<DisplayInfo> =
+ displayManager
+ .getDisplays(DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)
+ .map { DisplayInfo().apply { it.getDisplayInfo(this) } }
+ .filter { it.type == Display.TYPE_INTERNAL }
var isTouchBlocked: Boolean = false
set(value) {
@@ -252,7 +249,7 @@ constructor(
@AssistedFactory
interface Factory {
fun create(
- displaySelector: Sequence<DisplayInfo>.() -> DisplayInfo?,
+ displaySelector: List<DisplayInfo>.() -> DisplayInfo?,
effectFactory: (rotation: Int) -> LightRevealEffect,
overlayContainerName: String
): FullscreenLightRevealAnimationController
diff --git a/packages/SystemUI/src/com/android/systemui/util/DrawableDump.kt b/packages/SystemUI/src/com/android/systemui/util/DrawableDump.kt
new file mode 100644
index 000000000000..0c079a309962
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/DrawableDump.kt
@@ -0,0 +1,201 @@
+/*
+ * 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.util
+
+import android.content.res.ColorStateList
+import android.graphics.BlendMode
+import android.graphics.BlendModeColorFilter
+import android.graphics.ColorFilter
+import android.graphics.LightingColorFilter
+import android.graphics.PorterDuffColorFilter
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.DrawableWrapper
+import android.graphics.drawable.GradientDrawable
+import android.graphics.drawable.LayerDrawable
+import android.graphics.drawable.RippleDrawable
+import android.util.Log
+
+fun dumpToString(drawable: Drawable?): String =
+ if (Compile.IS_DEBUG) StringBuilder().appendDrawable(drawable).toString()
+ else drawable.toString()
+
+fun getSolidColor(drawable: Drawable?): String =
+ if (Compile.IS_DEBUG) hexColorString(getSolidColors(drawable)?.defaultColor)
+ else if (drawable == null) "null" else "?"
+
+private fun getSolidColors(drawable: Drawable?): ColorStateList? {
+ return when (drawable) {
+ is GradientDrawable -> {
+ return drawable.getStateField<ColorStateList>("mSolidColors")
+ }
+ is LayerDrawable -> {
+ for (iLayer in 0 until drawable.numberOfLayers) {
+ getSolidColors(drawable.getDrawable(iLayer))?.let {
+ return it
+ }
+ }
+ null
+ }
+ is DrawableWrapper -> {
+ return getSolidColors(drawable.drawable)
+ }
+ else -> null
+ }
+}
+
+private fun StringBuilder.appendDrawable(drawable: Drawable?): StringBuilder {
+ if (drawable == null) {
+ append("null")
+ return this
+ }
+ append("<")
+ append(drawable.javaClass.simpleName)
+
+ drawable.getStateField<ColorStateList>("mTint", fieldRequired = false)?.let {
+ append(" tint=")
+ appendColors(it)
+ append(" blendMode=")
+ append(drawable.getStateField<BlendMode>("mBlendMode"))
+ }
+ drawable.colorFilter
+ ?.takeUnless { drawable is DrawableWrapper }
+ ?.let {
+ append(" colorFilter=")
+ appendColorFilter(it)
+ }
+ when (drawable) {
+ is DrawableWrapper -> {
+ append(" wrapped=")
+ appendDrawable(drawable.drawable)
+ }
+ is LayerDrawable -> {
+ if (drawable is RippleDrawable) {
+ drawable.getStateField<ColorStateList>("mColor")?.let {
+ append(" color=")
+ appendColors(it)
+ }
+ drawable.effectColor?.let {
+ append(" effectColor=")
+ appendColors(it)
+ }
+ }
+ append(" layers=[")
+ for (iLayer in 0 until drawable.numberOfLayers) {
+ if (iLayer != 0) append(", ")
+ appendDrawable(drawable.getDrawable(iLayer))
+ }
+ append("]")
+ }
+ is GradientDrawable -> {
+ drawable
+ .getStateField<Int>("mShape")
+ ?.takeIf { it != 0 }
+ ?.let {
+ append(" shape=")
+ append(it)
+ }
+ drawable.getStateField<ColorStateList>("mSolidColors")?.let {
+ append(" solidColors=")
+ appendColors(it)
+ }
+ drawable.getStateField<ColorStateList>("mStrokeColors")?.let {
+ append(" strokeColors=")
+ appendColors(it)
+ }
+ drawable.colors?.let {
+ append(" gradientColors=[")
+ it.forEachIndexed { iColor, color ->
+ if (iColor != 0) append(", ")
+ append(hexColorString(color))
+ }
+ append("]")
+ }
+ }
+ }
+ append(">")
+ return this
+}
+
+private inline fun <reified T> Drawable.getStateField(
+ name: String,
+ fieldRequired: Boolean = true
+): T? {
+ val state = this.constantState ?: return null
+ val clazz = state.javaClass
+ return try {
+ val field = clazz.getDeclaredField(name)
+ field.isAccessible = true
+ field.get(state) as T?
+ } catch (ex: Exception) {
+ if (fieldRequired) {
+ Log.w(TAG, "Missing ${clazz.simpleName}.$name: ${T::class.simpleName}", ex)
+ } else if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Missing ${clazz.simpleName}.$name: ${T::class.simpleName} ($ex)")
+ }
+ null
+ }
+}
+
+private fun Appendable.appendColors(colorStateList: ColorStateList?) {
+ if (colorStateList == null) {
+ append("null")
+ return
+ }
+ val colors = colorStateList.colors
+ if (colors.size == 1) {
+ append(hexColorString(colors[0]))
+ return
+ }
+ append("<ColorStateList size=")
+ append(colors.size.toString())
+ append(" default=")
+ append(hexColorString(colorStateList.defaultColor))
+ append(">")
+}
+
+private fun Appendable.appendColorFilter(colorFilter: ColorFilter?) {
+ if (colorFilter == null) {
+ append("null")
+ return
+ }
+ append("<")
+ append(colorFilter.javaClass.simpleName)
+ when (colorFilter) {
+ is PorterDuffColorFilter -> {
+ append(" color=")
+ append(hexColorString(colorFilter.color))
+ append(" mode=")
+ append(colorFilter.mode.toString())
+ }
+ is BlendModeColorFilter -> {
+ append(" color=")
+ append(hexColorString(colorFilter.color))
+ append(" mode=")
+ append(colorFilter.mode.toString())
+ }
+ is LightingColorFilter -> {
+ append(" multiply=")
+ append(hexColorString(colorFilter.colorMultiply))
+ append(" add=")
+ append(hexColorString(colorFilter.colorAdd))
+ }
+ else -> append(" unhandled")
+ }
+ append(">")
+}
+
+private const val TAG = "DrawableDump"
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
index 7b652c11e9ec..2cad8442e3ba 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
@@ -23,11 +23,13 @@ import android.os.HandlerThread;
import android.os.Looper;
import android.os.Process;
+import com.android.systemui.Flags;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.BroadcastRunning;
import com.android.systemui.dagger.qualifiers.LongRunning;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dagger.qualifiers.NotifInflation;
import dagger.Module;
import dagger.Provides;
@@ -50,6 +52,8 @@ public abstract class SysUIConcurrencyModule {
private static final Long LONG_SLOW_DELIVERY_THRESHOLD = 2500L;
private static final Long BROADCAST_SLOW_DISPATCH_THRESHOLD = 1000L;
private static final Long BROADCAST_SLOW_DELIVERY_THRESHOLD = 1000L;
+ private static final Long NOTIFICATION_INFLATION_SLOW_DISPATCH_THRESHOLD = 1000L;
+ private static final Long NOTIFICATION_INFLATION_SLOW_DELIVERY_THRESHOLD = 1000L;
/** Background Looper */
@Provides
@@ -90,6 +94,24 @@ public abstract class SysUIConcurrencyModule {
return thread.getLooper();
}
+ /** Notification inflation Looper */
+ @Provides
+ @SysUISingleton
+ @NotifInflation
+ public static Looper provideNotifInflationLooper(@Background Looper bgLooper) {
+ if (!Flags.dedicatedNotifInflationThread()) {
+ return bgLooper;
+ }
+
+ final HandlerThread thread = new HandlerThread("NotifInflation",
+ Process.THREAD_PRIORITY_BACKGROUND);
+ thread.start();
+ final Looper looper = thread.getLooper();
+ looper.setSlowLogThresholdMs(NOTIFICATION_INFLATION_SLOW_DISPATCH_THRESHOLD,
+ NOTIFICATION_INFLATION_SLOW_DELIVERY_THRESHOLD);
+ return looper;
+ }
+
/**
* Background Handler.
*
@@ -102,15 +124,6 @@ public abstract class SysUIConcurrencyModule {
}
/**
- * Provide a Background-Thread Executor by default.
- */
- @Provides
- @SysUISingleton
- public static Executor provideExecutor(@Background Looper looper) {
- return new ExecutorImpl(looper);
- }
-
- /**
* Provide a BroadcastRunning Executor (for sending and receiving broadcasts).
*/
@Provides
@@ -152,15 +165,6 @@ public abstract class SysUIConcurrencyModule {
}
/**
- * Provide a Background-Thread Executor by default.
- */
- @Provides
- @SysUISingleton
- public static DelayableExecutor provideDelayableExecutor(@Background Looper looper) {
- return new ExecutorImpl(looper);
- }
-
- /**
* Provide a Background-Thread Executor.
*/
@Provides
@@ -171,15 +175,6 @@ public abstract class SysUIConcurrencyModule {
}
/**
- * Provide a Background-Thread Executor by default.
- */
- @Provides
- @SysUISingleton
- public static RepeatableExecutor provideRepeatableExecutor(@Background DelayableExecutor exec) {
- return new RepeatableExecutorImpl(exec);
- }
-
- /**
* Provide a Background-Thread Executor.
*/
@Provides
@@ -225,4 +220,12 @@ public abstract class SysUIConcurrencyModule {
thread.start();
return new Handler(thread.getLooper());
}
+
+ /** */
+ @Provides
+ @SysUISingleton
+ @NotifInflation
+ public static Executor provideNotifInflationExecutor(@NotifInflation Looper looper) {
+ return new ExecutorImpl(looper);
+ }
}
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 d47413faeadf..bb907cc0055e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
@@ -245,6 +245,29 @@ fun <T> Flow<T>.throttle(periodMs: Long, clock: SystemClock = SystemClockImpl())
}
}
+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>,
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/FlowDumper.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/FlowDumper.kt
new file mode 100644
index 000000000000..e17274c435aa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/FlowDumper.kt
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.kotlin
+
+import android.util.IndentingPrintWriter
+import com.android.systemui.Dumpable
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.asIndenting
+import com.android.systemui.util.printCollection
+import java.io.PrintWriter
+import java.util.concurrent.ConcurrentHashMap
+import java.util.concurrent.atomic.AtomicBoolean
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flow
+
+/**
+ * An interface which gives the implementing type flow extension functions which will register a
+ * given flow as a field in the Dumpable.
+ */
+interface FlowDumper : Dumpable {
+ /**
+ * Include the last emitted value of this Flow whenever it is being collected. Remove its value
+ * when collection ends.
+ *
+ * @param dumpName the name to use for this field in the dump output
+ */
+ fun <T> Flow<T>.dumpWhileCollecting(dumpName: String): Flow<T>
+
+ /**
+ * Include the [SharedFlow.replayCache] for this Flow in the dump.
+ *
+ * @param dumpName the name to use for this field in the dump output
+ */
+ fun <T, F : SharedFlow<T>> F.dumpReplayCache(dumpName: String): F
+
+ /**
+ * Include the [StateFlow.value] for this Flow in the dump.
+ *
+ * @param dumpName the name to use for this field in the dump output
+ */
+ fun <T, F : StateFlow<T>> F.dumpValue(dumpName: String): F
+
+ /** The default [Dumpable.dump] implementation which just calls [dumpFlows] */
+ override fun dump(pw: PrintWriter, args: Array<out String>) = dumpFlows(pw.asIndenting())
+
+ /** Dump all the values from any registered / active Flows. */
+ fun dumpFlows(pw: IndentingPrintWriter)
+}
+
+/**
+ * An implementation of [FlowDumper]. This be extended directly, or can be used to implement
+ * [FlowDumper] by delegation.
+ *
+ * @param dumpManager if provided, this will be used by the [FlowDumperImpl] to register and
+ * unregister itself when there is something to dump.
+ * @param tag a static name by which this [FlowDumperImpl] is registered. If not provided, this
+ * class's name will be used. If you're implementing by delegation, you probably want to provide
+ * this tag to get a meaningful dumpable name.
+ */
+open class FlowDumperImpl(private val dumpManager: DumpManager?, tag: String? = null) : FlowDumper {
+ private val stateFlowMap = ConcurrentHashMap<String, StateFlow<*>>()
+ private val sharedFlowMap = ConcurrentHashMap<String, SharedFlow<*>>()
+ private val flowCollectionMap = ConcurrentHashMap<Pair<String, String>, Any>()
+ override fun dumpFlows(pw: IndentingPrintWriter) {
+ pw.printCollection("StateFlow (value)", stateFlowMap.toSortedMap().entries) { (key, flow) ->
+ append(key).append('=').println(flow.value)
+ }
+ pw.printCollection("SharedFlow (replayCache)", sharedFlowMap.toSortedMap().entries) {
+ (key, flow) ->
+ append(key).append('=').println(flow.replayCache)
+ }
+ val comparator = compareBy<Pair<String, String>> { it.first }.thenBy { it.second }
+ pw.printCollection("Flow (latest)", flowCollectionMap.toSortedMap(comparator).entries) {
+ (pair, value) ->
+ append(pair.first).append('=').println(value)
+ }
+ }
+
+ private val Any.idString: String
+ get() = Integer.toHexString(System.identityHashCode(this))
+
+ override fun <T> Flow<T>.dumpWhileCollecting(dumpName: String): Flow<T> = flow {
+ val mapKey = dumpName to idString
+ try {
+ collect {
+ flowCollectionMap[mapKey] = it ?: "null"
+ updateRegistration(required = true)
+ emit(it)
+ }
+ } finally {
+ flowCollectionMap.remove(mapKey)
+ updateRegistration(required = false)
+ }
+ }
+
+ override fun <T, F : StateFlow<T>> F.dumpValue(dumpName: String): F {
+ stateFlowMap[dumpName] = this
+ return this
+ }
+
+ override fun <T, F : SharedFlow<T>> F.dumpReplayCache(dumpName: String): F {
+ sharedFlowMap[dumpName] = this
+ return this
+ }
+
+ private val dumpManagerName = tag ?: "[$idString] ${javaClass.simpleName}"
+ private var registered = AtomicBoolean(false)
+ private fun updateRegistration(required: Boolean) {
+ if (dumpManager == null) return
+ if (required && registered.get()) return
+ synchronized(registered) {
+ val shouldRegister =
+ stateFlowMap.isNotEmpty() ||
+ sharedFlowMap.isNotEmpty() ||
+ flowCollectionMap.isNotEmpty()
+ val wasRegistered = registered.getAndSet(shouldRegister)
+ if (wasRegistered != shouldRegister) {
+ if (shouldRegister) {
+ dumpManager.registerCriticalDumpable(dumpManagerName, this)
+ } else {
+ dumpManager.unregisterDumpable(dumpManagerName)
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt
index fa0d0306f157..a88be065d722 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt
@@ -36,6 +36,17 @@ class Utils {
fun <A, B, C, D, E, F> toSextuple(a: A, bcdef: Quint<B, C, D, E, F>) =
Sextuple(a, bcdef.first, bcdef.second, bcdef.third, bcdef.fourth, bcdef.fifth)
+ fun <A, B, C, D, E, F, G> toSeptuple(a: A, bcdefg: Sextuple<B, C, D, E, F, G>) =
+ Septuple(
+ a,
+ bcdefg.first,
+ bcdefg.second,
+ bcdefg.third,
+ bcdefg.fourth,
+ bcdefg.fifth,
+ bcdefg.sixth
+ )
+
/**
* Samples the provided flows, emitting a tuple of the original flow's value as well as each
* of the combined flows' values.
@@ -90,6 +101,24 @@ class Utils {
): Flow<Sextuple<A, B, C, D, E, F>> {
return this.sample(combine(b, c, d, e, f, ::Quint), ::toSextuple)
}
+
+ /**
+ * Samples the provided flows, emitting a tuple of the original flow's value as well as each
+ * of the combined flows' values.
+ *
+ * Flow<A>.sample(Flow<B>, Flow<C>, Flow<D>, Flow<E>, Flow<F>, Flow<G>) -> (A, B, C, D, E,
+ * F, G)
+ */
+ fun <A, B, C, D, E, F, G> Flow<A>.sample(
+ b: Flow<B>,
+ c: Flow<C>,
+ d: Flow<D>,
+ e: Flow<E>,
+ f: Flow<F>,
+ g: Flow<G>,
+ ): Flow<Septuple<A, B, C, D, E, F, G>> {
+ return this.sample(combine(b, c, d, e, f, g, ::Sextuple), ::toSeptuple)
+ }
}
}
@@ -112,6 +141,16 @@ data class Sextuple<A, B, C, D, E, F>(
val sixth: F,
)
+data class Septuple<A, B, C, D, E, F, G>(
+ val first: A,
+ val second: B,
+ val third: C,
+ val fourth: D,
+ val fifth: E,
+ val sixth: F,
+ val seventh: G,
+)
+
fun Int.toPx(context: Context): Int {
return (this * context.resources.displayMetrics.density).toInt()
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java b/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java
index 9b72eb710588..5979f3e60cb9 100644
--- a/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java
+++ b/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java
@@ -28,6 +28,7 @@ import android.util.Log;
import androidx.annotation.NonNull;
import com.android.systemui.Dumpable;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.time.SystemClock;
@@ -94,10 +95,12 @@ public class PersistentConnectionManager<T> implements Dumpable {
}
};
+ // TODO: b/326449074 - Ensure the DelayableExecutor is on the correct thread, and update the
+ // qualifier (to @Main) or name (to bgExecutor) to be consistent with that.
@Inject
public PersistentConnectionManager(
SystemClock clock,
- DelayableExecutor mainExecutor,
+ @Background DelayableExecutor mainExecutor,
DumpManager dumpManager,
@Named(DUMPSYS_NAME) String dumpsysName,
@Named(SERVICE_CONNECTION) ObservableServiceConnection<T> serviceConnection,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/Events.java b/packages/SystemUI/src/com/android/systemui/volume/Events.java
index 50d15475434b..e10d1cb833fa 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/Events.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/Events.java
@@ -60,6 +60,7 @@ public class Events {
public static final int EVENT_DISMISS_USB_OVERHEAT_ALARM = 20; // (reason|int) (keyguard|bool)
public static final int EVENT_ODI_CAPTIONS_CLICK = 21;
public static final int EVENT_ODI_CAPTIONS_TOOLTIP_CLICK = 22;
+ public static final int EVENT_SLIDER_TOUCH_TRACKING = 23; // (tracking|bool)
private static final String[] EVENT_TAGS = {
"show_dialog",
@@ -84,7 +85,8 @@ public class Events {
"show_usb_overheat_alarm",
"dismiss_usb_overheat_alarm",
"odi_captions_click",
- "odi_captions_tooltip_click"
+ "odi_captions_tooltip_click",
+ "slider_touch_tracking"
};
public static final int DISMISS_REASON_UNKNOWN = 0;
@@ -234,6 +236,10 @@ public class Events {
VOLUME_DIALOG_SLIDER(150),
@UiEvent(doc = "The audio stream was set to silent via slider")
VOLUME_DIALOG_SLIDER_TO_ZERO(151),
+ @UiEvent(doc = "The right-most slider started tracking touch")
+ VOLUME_DIALOG_SLIDER_STARTED_TRACKING_TOUCH(1620),
+ @UiEvent(doc = "The right-most slider stopped tracking touch")
+ VOLUME_DIALOG_SLIDER_STOPPED_TRACKING_TOUCH(1621),
@UiEvent(doc = "ODI captions was clicked")
VOLUME_DIALOG_ODI_CAPTIONS_CLICKED(1503),
@UiEvent(doc = "ODI captions tooltip dismiss was clicked")
@@ -491,6 +497,15 @@ public class Events {
.append(" keyguard=").append(keyguard);
}
break;
+ case EVENT_SLIDER_TOUCH_TRACKING:
+ final boolean startedTracking = (boolean) list[0];
+ final VolumeDialogEvent event;
+ if (startedTracking) {
+ event = VolumeDialogEvent.VOLUME_DIALOG_SLIDER_STARTED_TRACKING_TOUCH;
+ } else {
+ event = VolumeDialogEvent.VOLUME_DIALOG_SLIDER_STOPPED_TRACKING_TOUCH;
+ }
+ sUiEventLogger.log(event);
default:
sb.append(Arrays.asList(list));
break;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/OWNERS b/packages/SystemUI/src/com/android/systemui/volume/OWNERS
index e627d610dc0a..4d2b639ba8fb 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/volume/OWNERS
@@ -1,4 +1,6 @@
-asc@google.com # send reviews here
+ethibodeau@google.com
+michaelmikhil@google.com
+apotapov@google.com
+asc@google.com
-juliacr@google.com
-tsuji@google.com
+juliacr@google.com #{LAST_RESORT_SUGGESTION}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 90c5c62718ad..404563087041 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -2518,6 +2518,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
if (D.BUG) Log.d(TAG, "onStartTrackingTouch"+ " " + mRow.stream);
+ Events.writeEvent(Events.EVENT_SLIDER_TOUCH_TRACKING, /* startedTracking= */true);
if (mRow.mHapticPlugin != null) {
mRow.mHapticPlugin.onStartTrackingTouch(seekBar);
}
@@ -2528,6 +2529,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable,
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
if (D.BUG) Log.d(TAG, "onStopTrackingTouch"+ " " + mRow.stream);
+ Events.writeEvent(Events.EVENT_SLIDER_TOUCH_TRACKING, /* startedTracking= */false);
if (mRow.mHapticPlugin != null) {
mRow.mHapticPlugin.onStopTrackingTouch(seekBar);
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/AncModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/AncModule.kt
new file mode 100644
index 000000000000..66df45ca4cfa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/AncModule.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 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.volume.dagger
+
+import android.content.Context
+import androidx.slice.SliceViewManager
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.volume.panel.component.anc.data.repository.AncSliceRepository
+import com.android.systemui.volume.panel.component.anc.data.repository.AncSliceRepositoryImpl
+import dagger.Module
+import dagger.Provides
+
+/** Dagger module that provides ANC controlling backend. */
+@Module
+interface AncModule {
+
+ companion object {
+ @Provides
+ @SysUISingleton
+ fun provideAncSliceRepository(
+ @Application context: Context,
+ implFactory: AncSliceRepositoryImpl.Factory
+ ): AncSliceRepository = implFactory.create(SliceViewManager.getInstance(context))
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
index 67d6a7f5d735..9dedf5c70da1 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
@@ -16,19 +16,15 @@
package com.android.systemui.volume.dagger
-import android.app.NotificationManager
import android.content.Context
import android.media.AudioManager
-import com.android.settingslib.media.data.repository.SpatializerRepository
-import com.android.settingslib.media.data.repository.SpatializerRepositoryImpl
-import com.android.settingslib.media.domain.interactor.SpatializerInteractor
-import com.android.settingslib.statusbar.notification.data.repository.NotificationsSoundPolicyRepository
-import com.android.settingslib.statusbar.notification.data.repository.NotificationsSoundPolicyRepositoryImpl
+import com.android.settingslib.statusbar.notification.domain.interactor.NotificationsSoundPolicyInteractor
import com.android.settingslib.volume.data.repository.AudioRepository
import com.android.settingslib.volume.data.repository.AudioRepositoryImpl
import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
-import com.android.settingslib.volume.shared.AudioManagerIntentsReceiver
-import com.android.settingslib.volume.shared.AudioManagerIntentsReceiverImpl
+import com.android.settingslib.volume.domain.interactor.AudioVolumeInteractor
+import com.android.settingslib.volume.shared.AudioManagerEventsReceiver
+import com.android.settingslib.volume.shared.AudioManagerEventsReceiverImpl
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import dagger.Module
@@ -46,11 +42,11 @@ interface AudioModule {
fun provideAudioManagerIntentsReceiver(
@Application context: Context,
@Application coroutineScope: CoroutineScope,
- ): AudioManagerIntentsReceiver = AudioManagerIntentsReceiverImpl(context, coroutineScope)
+ ): AudioManagerEventsReceiver = AudioManagerEventsReceiverImpl(context, coroutineScope)
@Provides
fun provideAudioRepository(
- intentsReceiver: AudioManagerIntentsReceiver,
+ intentsReceiver: AudioManagerEventsReceiver,
audioManager: AudioManager,
@Background coroutineContext: CoroutineContext,
@Application coroutineScope: CoroutineScope,
@@ -62,28 +58,10 @@ interface AudioModule {
AudioModeInteractor(repository)
@Provides
- fun provdieSpatializerRepository(
- audioManager: AudioManager,
- @Background backgroundContext: CoroutineContext,
- ): SpatializerRepository =
- SpatializerRepositoryImpl(audioManager.spatializer, backgroundContext)
-
- @Provides
- fun provideSpatializerInetractor(repository: SpatializerRepository): SpatializerInteractor =
- SpatializerInteractor(repository)
-
- @Provides
- fun provideNotificationsSoundPolicyRepository(
- context: Context,
- notificationManager: NotificationManager,
- @Background coroutineContext: CoroutineContext,
- @Application coroutineScope: CoroutineScope,
- ): NotificationsSoundPolicyRepository =
- NotificationsSoundPolicyRepositoryImpl(
- context,
- notificationManager,
- coroutineScope,
- coroutineContext,
- )
+ fun provideAudioVolumeInteractor(
+ audioRepository: AudioRepository,
+ notificationsSoundPolicyInteractor: NotificationsSoundPolicyInteractor,
+ ): AudioVolumeInteractor =
+ AudioVolumeInteractor(audioRepository, notificationsSoundPolicyInteractor)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt
index ab76d450eb0a..d134e60ef72f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt
@@ -18,12 +18,17 @@ package com.android.systemui.volume.dagger
import android.media.session.MediaSessionManager
import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.settingslib.volume.data.repository.LocalMediaRepository
import com.android.settingslib.volume.data.repository.MediaControllerRepository
import com.android.settingslib.volume.data.repository.MediaControllerRepositoryImpl
-import com.android.settingslib.volume.shared.AudioManagerIntentsReceiver
+import com.android.settingslib.volume.domain.interactor.LocalMediaInteractor
+import com.android.settingslib.volume.shared.AudioManagerEventsReceiver
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactory
+import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactoryImpl
+import dagger.Binds
import dagger.Module
import dagger.Provides
import kotlin.coroutines.CoroutineContext
@@ -32,12 +37,30 @@ import kotlinx.coroutines.CoroutineScope
@Module
interface MediaDevicesModule {
+ @Binds
+ fun bindLocalMediaRepositoryFactory(
+ impl: LocalMediaRepositoryFactoryImpl
+ ): LocalMediaRepositoryFactory
+
companion object {
@Provides
@SysUISingleton
+ fun provideLocalMediaRepository(
+ factory: LocalMediaRepositoryFactory
+ ): LocalMediaRepository = factory.create(null)
+
+ @Provides
+ @SysUISingleton
+ fun provideLocalMediaInteractor(
+ repository: LocalMediaRepository,
+ @Application scope: CoroutineScope,
+ ): LocalMediaInteractor = LocalMediaInteractor(repository, scope)
+
+ @Provides
+ @SysUISingleton
fun provideMediaDeviceSessionRepository(
- intentsReceiver: AudioManagerIntentsReceiver,
+ intentsReceiver: AudioManagerEventsReceiver,
mediaSessionManager: MediaSessionManager,
localBluetoothManager: LocalBluetoothManager?,
@Application coroutineScope: CoroutineScope,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/SpatializerModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/SpatializerModule.kt
new file mode 100644
index 000000000000..18a9161ac0e3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/SpatializerModule.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 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.volume.dagger
+
+import android.media.AudioManager
+import android.media.Spatializer
+import com.android.settingslib.media.data.repository.SpatializerRepository
+import com.android.settingslib.media.data.repository.SpatializerRepositoryImpl
+import com.android.settingslib.media.domain.interactor.SpatializerInteractor
+import com.android.systemui.dagger.qualifiers.Background
+import dagger.Module
+import dagger.Provides
+import kotlin.coroutines.CoroutineContext
+
+/** Spatializer module. */
+@Module
+interface SpatializerModule {
+ companion object {
+ @Provides
+ fun provideSpatializer(
+ audioManager: AudioManager,
+ ): Spatializer = audioManager.spatializer
+
+ @Provides
+ fun provdieSpatializerRepository(
+ spatializer: Spatializer,
+ @Background backgroundContext: CoroutineContext,
+ ): SpatializerRepository = SpatializerRepositoryImpl(spatializer, backgroundContext)
+
+ @Provides
+ fun provideSpatializerInetractor(repository: SpatializerRepository): SpatializerInteractor =
+ SpatializerInteractor(repository)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
index 32856373dbe9..64a5644bc452 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
@@ -57,8 +57,10 @@ import dagger.multibindings.IntoSet;
@Module(
includes = {
AudioModule.class,
+ AncModule.class,
CaptioningModule.class,
- MediaDevicesModule.class
+ MediaDevicesModule.class,
+ SpatializerModule.class,
},
subcomponents = {
VolumePanelComponent.class
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepository.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepository.kt
new file mode 100644
index 000000000000..8f18aa8021ae
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepository.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.anc.data.repository
+
+import android.bluetooth.BluetoothDevice
+import android.net.Uri
+import androidx.slice.Slice
+import androidx.slice.SliceViewManager
+import com.android.settingslib.bluetooth.BluetoothUtils
+import com.android.settingslib.media.BluetoothMediaDevice
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.slice.sliceForUri
+import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactory
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+
+/** Provides ANC slice data */
+interface AncSliceRepository {
+
+ /**
+ * ANC slice with a given width. Emits null when there is no ANC slice available. This can mean
+ * that:
+ * - there is no supported device connected;
+ * - there is no slice provider for the uri;
+ */
+ fun ancSlice(width: Int): Flow<Slice?>
+}
+
+@OptIn(ExperimentalCoroutinesApi::class)
+class AncSliceRepositoryImpl
+@AssistedInject
+constructor(
+ mediaRepositoryFactory: LocalMediaRepositoryFactory,
+ @Background private val backgroundCoroutineContext: CoroutineContext,
+ @Assisted private val sliceViewManager: SliceViewManager,
+) : AncSliceRepository {
+
+ private val localMediaRepository = mediaRepositoryFactory.create(null)
+
+ override fun ancSlice(width: Int): Flow<Slice?> {
+ return localMediaRepository.currentConnectedDevice
+ .map { (it as? BluetoothMediaDevice)?.cachedDevice?.device?.getExtraControlUri(width) }
+ .distinctUntilChanged()
+ .flatMapLatest { sliceUri ->
+ sliceUri ?: return@flatMapLatest flowOf(null)
+ sliceViewManager.sliceForUri(sliceUri)
+ }
+ .flowOn(backgroundCoroutineContext)
+ }
+
+ private fun BluetoothDevice.getExtraControlUri(width: Int): Uri? {
+ val uri: String? = BluetoothUtils.getControlUriMetaData(this)
+ uri ?: return null
+
+ return if (uri.isEmpty()) {
+ null
+ } else {
+ Uri.parse(
+ "$uri$width" +
+ "&version=${SliceParameters.VERSION}" +
+ "&is_collapsed=${SliceParameters.IS_COLLAPSED}"
+ )
+ }
+ }
+
+ @AssistedFactory
+ interface Factory {
+ fun create(sliceViewManager: SliceViewManager): AncSliceRepositoryImpl
+ }
+
+ private object SliceParameters {
+ /**
+ * Slice version
+ * 1) legacy slice
+ * 2) new slice
+ */
+ const val VERSION = 2
+
+ /**
+ * Collapsed slice shows a single button, and expanded shows a row buttons. Supported since
+ * [VERSION]==2.
+ */
+ const val IS_COLLAPSED = false
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/AncAvailabilityCriteria.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/AncAvailabilityCriteria.kt
new file mode 100644
index 000000000000..89b927480783
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/AncAvailabilityCriteria.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.anc.domain
+
+import com.android.systemui.volume.panel.component.anc.domain.interactor.AncSliceInteractor
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+/** Determines if ANC component is available for the Volume Panel. */
+@VolumePanelScope
+class AncAvailabilityCriteria
+@Inject
+constructor(
+ private val ancSliceInteractor: AncSliceInteractor,
+) : ComponentAvailabilityCriteria {
+
+ override fun isAvailable(): Flow<Boolean> = ancSliceInteractor.ancSlice.map { it != null }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractor.kt
new file mode 100644
index 000000000000..91af622074a0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractor.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.anc.domain.interactor
+
+import android.app.slice.Slice.HINT_ERROR
+import android.app.slice.SliceItem.FORMAT_SLICE
+import androidx.slice.Slice
+import com.android.systemui.volume.panel.component.anc.data.repository.AncSliceRepository
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.shareIn
+
+/** Provides a valid slice from [AncSliceRepository]. */
+@OptIn(ExperimentalCoroutinesApi::class)
+@VolumePanelScope
+class AncSliceInteractor
+@Inject
+constructor(
+ private val ancSliceRepository: AncSliceRepository,
+ scope: CoroutineScope,
+) {
+
+ // Start with a positive width to check is the Slice is available.
+ private val width = MutableStateFlow(1)
+
+ /** Provides a valid ANC slice. */
+ val ancSlice: SharedFlow<Slice?> =
+ width
+ .flatMapLatest { width -> ancSliceRepository.ancSlice(width) }
+ .map { slice ->
+ if (slice?.isValidSlice() == true) {
+ slice
+ } else {
+ null
+ }
+ }
+ .shareIn(scope, SharingStarted.Eagerly, replay = 1)
+
+ /** Updates the width of the [ancSlice] */
+ fun changeWidth(newWidth: Int) {
+ width.value = newWidth
+ }
+
+ private fun Slice.isValidSlice(): Boolean {
+ if (hints.contains(HINT_ERROR)) {
+ return false
+ }
+ for (item in items) {
+ if (item.format == FORMAT_SLICE) {
+ return true
+ }
+ }
+ return false
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/ui/viewmodel/AncViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/ui/viewmodel/AncViewModel.kt
new file mode 100644
index 000000000000..eb96f6cad8f2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/ui/viewmodel/AncViewModel.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.anc.ui.viewmodel
+
+import android.content.Context
+import androidx.slice.Slice
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.res.R
+import com.android.systemui.volume.panel.component.anc.domain.interactor.AncSliceInteractor
+import com.android.systemui.volume.panel.component.button.ui.viewmodel.ButtonViewModel
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+/** Volume Panel ANC component view model. */
+@VolumePanelScope
+class AncViewModel
+@Inject
+constructor(
+ @Application private val context: Context,
+ @VolumePanelScope private val coroutineScope: CoroutineScope,
+ private val interactor: AncSliceInteractor,
+) {
+
+ /** ANC [Slice]. Null when there is no slice available for ANC. */
+ val slice: StateFlow<Slice?> =
+ interactor.ancSlice.stateIn(coroutineScope, SharingStarted.Eagerly, null)
+
+ /**
+ * ButtonViewModel to be shown in the VolumePanel. Null when there is no ANC Slice available.
+ */
+ val button: StateFlow<ButtonViewModel?> =
+ interactor.ancSlice
+ .map { slice ->
+ slice?.let {
+ ButtonViewModel(
+ Icon.Resource(R.drawable.ic_noise_aware, null),
+ context.getString(R.string.volume_panel_noise_control_title)
+ )
+ }
+ }
+ .stateIn(coroutineScope, SharingStarted.Eagerly, null)
+
+ /** Call this to update [slice] width in a reaction to container size change. */
+ fun changeSliceWidth(width: Int) {
+ interactor.changeWidth(width)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/button/ui/viewmodel/ButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/button/ui/viewmodel/ButtonViewModel.kt
new file mode 100644
index 000000000000..754d258b2b86
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/button/ui/viewmodel/ButtonViewModel.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.button.ui.viewmodel
+
+import com.android.systemui.common.shared.model.Icon
+
+/** Models base buttons appearance. */
+data class ButtonViewModel(
+ val icon: Icon,
+ val label: CharSequence,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt
index 0a1ee249d6fb..11b4690e59ee 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt
@@ -18,27 +18,32 @@ package com.android.systemui.volume.panel.component.mediaoutput.data.repository
import android.media.MediaRouter2Manager
import com.android.settingslib.volume.data.repository.LocalMediaRepository
import com.android.settingslib.volume.data.repository.LocalMediaRepositoryImpl
-import com.android.settingslib.volume.shared.AudioManagerIntentsReceiver
+import com.android.settingslib.volume.shared.AudioManagerEventsReceiver
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.media.controls.pipeline.LocalMediaManagerFactory
+import com.android.systemui.media.controls.util.LocalMediaManagerFactory
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
-class LocalMediaRepositoryFactory
+interface LocalMediaRepositoryFactory {
+
+ fun create(packageName: String?): LocalMediaRepository
+}
+
+class LocalMediaRepositoryFactoryImpl
@Inject
constructor(
- private val intentsReceiver: AudioManagerIntentsReceiver,
+ private val eventsReceiver: AudioManagerEventsReceiver,
private val mediaRouter2Manager: MediaRouter2Manager,
private val localMediaManagerFactory: LocalMediaManagerFactory,
@Application private val coroutineScope: CoroutineScope,
@Background private val backgroundCoroutineContext: CoroutineContext,
-) {
+) : LocalMediaRepositoryFactory {
- fun create(packageName: String?): LocalMediaRepository =
+ override fun create(packageName: String?): LocalMediaRepository =
LocalMediaRepositoryImpl(
- intentsReceiver,
+ eventsReceiver,
localMediaManagerFactory.create(packageName),
mediaRouter2Manager,
coroutineScope,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteria.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteria.kt
new file mode 100644
index 000000000000..bac7d15235d0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/MediaOutputAvailabilityCriteria.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.mediaoutput.domain
+
+import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+/** Determines if the Media Output Volume Panel component is available. */
+@VolumePanelScope
+class MediaOutputAvailabilityCriteria
+@Inject
+constructor(
+ private val audioModeInteractor: AudioModeInteractor,
+) : ComponentAvailabilityCriteria {
+
+ override fun isAvailable(): Flow<Boolean> = audioModeInteractor.isOngoingCall.map { !it }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputActionsInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputActionsInteractor.kt
new file mode 100644
index 000000000000..170b32c1d0ea
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputActionsInteractor.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.mediaoutput.domain.interactor
+
+import android.content.Intent
+import android.provider.Settings
+import com.android.internal.jank.InteractionJankMonitor
+import com.android.systemui.animation.DialogCuj
+import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.animation.Expandable
+import com.android.systemui.media.dialog.MediaOutputDialogFactory
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaDeviceSession
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import javax.inject.Inject
+
+/** User actions interactor for Media Output Volume Panel component. */
+@VolumePanelScope
+class MediaOutputActionsInteractor
+@Inject
+constructor(
+ private val mediaOutputDialogFactory: MediaOutputDialogFactory,
+ private val activityStarter: ActivityStarter,
+) {
+
+ fun onDeviceClick(expandable: Expandable) {
+ activityStarter.startActivity(
+ Intent(Settings.ACTION_BLUETOOTH_SETTINGS),
+ true,
+ expandable.activityTransitionController(),
+ )
+ }
+
+ fun onBarClick(session: MediaDeviceSession, expandable: Expandable) {
+ when (session) {
+ is MediaDeviceSession.Active -> {
+ mediaOutputDialogFactory.createWithController(
+ session.packageName,
+ false,
+ expandable.dialogController()
+ )
+ }
+ is MediaDeviceSession.Inactive -> {
+ mediaOutputDialogFactory.createDialogForSystemRouting(expandable.dialogController())
+ }
+ else -> {
+ /* do nothing */
+ }
+ }
+ }
+
+ private fun Expandable.dialogController(): DialogTransitionAnimator.Controller? {
+ return dialogTransitionController(
+ cuj =
+ DialogCuj(
+ InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
+ MediaOutputDialogFactory.INTERACTION_JANK_TAG
+ )
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
index 6c456f963f03..0f5343701ac6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
@@ -17,10 +17,14 @@
package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor
import android.content.pm.PackageManager
+import android.media.session.MediaController
+import android.os.Handler
import android.util.Log
import com.android.settingslib.media.MediaDevice
import com.android.settingslib.volume.data.repository.LocalMediaRepository
+import com.android.settingslib.volume.data.repository.MediaControllerChange
import com.android.settingslib.volume.data.repository.MediaControllerRepository
+import com.android.settingslib.volume.data.repository.stateChanges
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactory
import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaDeviceSession
@@ -30,14 +34,21 @@ import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.withContext
+/** Provides observable models about the current media session state. */
@OptIn(ExperimentalCoroutinesApi::class)
@VolumePanelScope
class MediaOutputInteractor
@@ -47,35 +58,43 @@ constructor(
private val packageManager: PackageManager,
@VolumePanelScope private val coroutineScope: CoroutineScope,
@Background private val backgroundCoroutineContext: CoroutineContext,
+ @Background private val backgroundHandler: Handler,
mediaControllerRepository: MediaControllerRepository
) {
- val mediaDeviceSession: Flow<MediaDeviceSession> =
- mediaControllerRepository.activeMediaController.mapNotNull { mediaController ->
- if (mediaController == null) {
- MediaDeviceSession.Inactive
- } else {
+ /** Current [MediaDeviceSession]. Emits when the session playback changes. */
+ val mediaDeviceSession: StateFlow<MediaDeviceSession> =
+ mediaControllerRepository.activeLocalMediaController
+ .flatMapLatest { it?.mediaDeviceSession() ?: flowOf(MediaDeviceSession.Inactive) }
+ .flowOn(backgroundCoroutineContext)
+ .stateIn(coroutineScope, SharingStarted.Eagerly, MediaDeviceSession.Inactive)
+
+ private fun MediaController.mediaDeviceSession(): Flow<MediaDeviceSession> {
+ return stateChanges(backgroundHandler)
+ .onStart { emit(MediaControllerChange.PlaybackStateChanged(playbackState)) }
+ .filterIsInstance<MediaControllerChange.PlaybackStateChanged>()
+ .map {
MediaDeviceSession.Active(
- appLabel = getApplicationLabel(mediaController.packageName)
- ?: return@mapNotNull null,
- packageName = mediaController.packageName,
- sessionToken = mediaController.sessionToken,
+ appLabel = getApplicationLabel(packageName)
+ ?: return@map MediaDeviceSession.Inactive,
+ packageName = packageName,
+ sessionToken = sessionToken,
+ playbackState = playbackState,
)
}
- }
- private val localMediaRepository: Flow<LocalMediaRepository> =
+ }
+
+ private val localMediaRepository: SharedFlow<LocalMediaRepository> =
mediaDeviceSession
.map { (it as? MediaDeviceSession.Active)?.packageName }
.distinctUntilChanged()
.map { localMediaRepositoryFactory.create(it) }
- .shareIn(coroutineScope, SharingStarted.WhileSubscribed(), replay = 1)
+ .shareIn(coroutineScope, SharingStarted.Eagerly, replay = 1)
+ /** Currently connected [MediaDevice]. */
val currentConnectedDevice: Flow<MediaDevice?> =
localMediaRepository.flatMapLatest { it.currentConnectedDevice }
- val mediaDevices: Flow<Collection<MediaDevice>> =
- localMediaRepository.flatMapLatest { it.mediaDevices }
-
private suspend fun getApplicationLabel(packageName: String): CharSequence? {
return try {
withContext(backgroundCoroutineContext) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaDeviceSession.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaDeviceSession.kt
index f250308802b2..71df8e53b5e2 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaDeviceSession.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaDeviceSession.kt
@@ -17,6 +17,7 @@
package com.android.systemui.volume.panel.component.mediaoutput.domain.model
import android.media.session.MediaSession
+import android.media.session.PlaybackState
/** Represents media playing on the connected device. */
sealed interface MediaDeviceSession {
@@ -26,6 +27,7 @@ sealed interface MediaDeviceSession {
val appLabel: CharSequence,
val packageName: String,
val sessionToken: MediaSession.Token,
+ val playbackState: PlaybackState?,
) : MediaDeviceSession
/** Media is not playing. */
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/ConnectedDeviceViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/ConnectedDeviceViewModel.kt
new file mode 100644
index 000000000000..8ba672d2a15e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/ConnectedDeviceViewModel.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.mediaoutput.ui.viewmodel
+
+/**
+ * Models part of the Media Session Volume Panel component that displays connected device
+ * information.
+ */
+data class ConnectedDeviceViewModel(
+ val label: CharSequence,
+ val deviceName: CharSequence?,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/DeviceIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/DeviceIconViewModel.kt
new file mode 100644
index 000000000000..e518ed022792
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/DeviceIconViewModel.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.mediaoutput.ui.viewmodel
+
+import com.android.systemui.common.shared.model.Color
+import com.android.systemui.common.shared.model.Icon
+
+/** Models Media Session Volume Panel component connected device icon. */
+sealed interface DeviceIconViewModel {
+
+ val icon: Icon
+ val iconColor: Color
+ val backgroundColor: Color
+
+ class IsPlaying(
+ override val icon: Icon,
+ override val iconColor: Color,
+ override val backgroundColor: Color,
+ ) : DeviceIconViewModel
+
+ class IsNotPlaying(
+ override val icon: Icon,
+ override val iconColor: Color,
+ override val backgroundColor: Color,
+ ) : DeviceIconViewModel
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
new file mode 100644
index 000000000000..85d6c9e341ac
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.mediaoutput.ui.viewmodel
+
+import android.content.Context
+import com.android.systemui.animation.Expandable
+import com.android.systemui.common.shared.model.Color
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.res.R
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputActionsInteractor
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor
+import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaDeviceSession
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.stateIn
+
+/** Models the UI of the Media Output Volume Panel component. */
+@VolumePanelScope
+class MediaOutputViewModel
+@Inject
+constructor(
+ private val context: Context,
+ @VolumePanelScope private val coroutineScope: CoroutineScope,
+ private val volumePanelViewModel: VolumePanelViewModel,
+ private val actionsInteractor: MediaOutputActionsInteractor,
+ interactor: MediaOutputInteractor,
+) {
+
+ private val mediaDeviceSession: StateFlow<MediaDeviceSession> =
+ interactor.mediaDeviceSession.stateIn(
+ coroutineScope,
+ SharingStarted.Eagerly,
+ MediaDeviceSession.Unknown,
+ )
+
+ val connectedDeviceViewModel: StateFlow<ConnectedDeviceViewModel?> =
+ combine(mediaDeviceSession, interactor.currentConnectedDevice) {
+ mediaDeviceSession,
+ currentConnectedDevice ->
+ ConnectedDeviceViewModel(
+ if (mediaDeviceSession.isPlaying()) {
+ context.getString(
+ R.string.media_output_label_title,
+ (mediaDeviceSession as MediaDeviceSession.Active).appLabel
+ )
+ } else {
+ context.getString(R.string.media_output_title_without_playing)
+ },
+ currentConnectedDevice?.name,
+ )
+ }
+ .stateIn(
+ coroutineScope,
+ SharingStarted.Eagerly,
+ null,
+ )
+
+ val deviceIconViewModel: StateFlow<DeviceIconViewModel?> =
+ combine(mediaDeviceSession, interactor.currentConnectedDevice) {
+ mediaDeviceSession,
+ currentConnectedDevice ->
+ if (mediaDeviceSession.isPlaying()) {
+ val icon =
+ currentConnectedDevice?.icon?.let { Icon.Loaded(it, null) }
+ ?: Icon.Resource(
+ com.android.internal.R.drawable.ic_bt_headphones_a2dp,
+ null
+ )
+ DeviceIconViewModel.IsPlaying(
+ icon = icon,
+ iconColor =
+ Color.Attribute(com.android.internal.R.attr.materialColorSurface),
+ backgroundColor =
+ Color.Attribute(com.android.internal.R.attr.materialColorSecondary),
+ )
+ } else {
+ DeviceIconViewModel.IsNotPlaying(
+ icon = Icon.Resource(R.drawable.ic_media_home_devices, null),
+ iconColor =
+ Color.Attribute(
+ com.android.internal.R.attr.materialColorOnSurfaceVariant
+ ),
+ backgroundColor =
+ Color.Attribute(com.android.internal.R.attr.materialColorSurface),
+ )
+ }
+ }
+ .stateIn(
+ coroutineScope,
+ SharingStarted.Eagerly,
+ null,
+ )
+
+ private fun MediaDeviceSession.isPlaying(): Boolean =
+ this is MediaDeviceSession.Active && playbackState?.isActive == true
+
+ fun onDeviceClick(expandable: Expandable) {
+ actionsInteractor.onDeviceClick(expandable)
+ volumePanelViewModel.dismissPanel()
+ }
+
+ fun onBarClick(expandable: Expandable) {
+ actionsInteractor.onBarClick(mediaDeviceSession.value, expandable)
+ volumePanelViewModel.dismissPanel()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/shared/model/VolumePanelComponents.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/shared/model/VolumePanelComponents.kt
index 842c3234fe26..9d801fc9bfa1 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/shared/model/VolumePanelComponents.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/shared/model/VolumePanelComponents.kt
@@ -20,6 +20,9 @@ import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey
object VolumePanelComponents {
+ const val MEDIA_OUTPUT: VolumePanelComponentKey = "media_output"
const val BOTTOM_BAR: VolumePanelComponentKey = "bottom_bar"
+ const val VOLUME_SLIDERS: VolumePanelComponentKey = "volume_sliders"
const val CAPTIONING: VolumePanelComponentKey = "captioning"
+ const val ANC: VolumePanelComponentKey = "anc"
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/CastVolumeInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/CastVolumeInteractor.kt
new file mode 100644
index 000000000000..6b62074e023d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/CastVolumeInteractor.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.volume.domain.interactor
+
+import com.android.settingslib.volume.domain.interactor.LocalMediaInteractor
+import com.android.settingslib.volume.domain.model.RoutingSession
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+/** Provides a remote media casting state. */
+@VolumePanelScope
+class CastVolumeInteractor
+@Inject
+constructor(
+ @VolumePanelScope private val coroutineScope: CoroutineScope,
+ private val localMediaInteractor: LocalMediaInteractor,
+) {
+
+ /** Returns a list of [RoutingSession] to show in the UI. */
+ val remoteRoutingSessions: StateFlow<List<RoutingSession>> =
+ localMediaInteractor.remoteRoutingSessions
+ .map { it.filter { routingSession -> routingSession.isVolumeSeekBarEnabled } }
+ .stateIn(coroutineScope, SharingStarted.Eagerly, emptyList())
+
+ /** Sets [routingSession] volume to [volume]. */
+ suspend fun setVolume(routingSession: RoutingSession, volume: Int) {
+ localMediaInteractor.adjustSessionVolume(routingSession.routingSessionInfo.id, volume)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/VolumeSliderInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/VolumeSliderInteractor.kt
new file mode 100644
index 000000000000..0c91bbf60f1b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/VolumeSliderInteractor.kt
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.volume.domain.interactor
+
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import javax.inject.Inject
+
+/** Converts from slider value to volume and back. */
+@VolumePanelScope
+class VolumeSliderInteractor @Inject constructor() {
+
+ /** mimic percentage volume setting */
+ val displayValueRange: ClosedFloatingPointRange<Float> = 0f..100f
+
+ /**
+ * Translates [volume], that belongs to [volumeRange] to the value that belongs to
+ * [displayValueRange].
+ *
+ * [currentValue] is the raw value received from the slider. Returns [currentValue] when it
+ * translates to the same volume as [volume] parameter. This ensures smooth slider experience
+ * (avoids snapping when the user stops dragging).
+ */
+ fun processVolumeToValue(
+ volume: Int,
+ volumeRange: ClosedRange<Int>,
+ currentValue: Float?,
+ isMuted: Boolean,
+ ): Float {
+ if (isMuted) {
+ return 0f
+ }
+ val changedVolume: Int? = currentValue?.let { translateValueToVolume(it, volumeRange) }
+ return if (volume != volumeRange.start && volume == changedVolume) {
+ currentValue
+ } else {
+ translateToRange(
+ currentValue = volume.toFloat(),
+ currentRangeStart = volumeRange.start.toFloat(),
+ currentRangeEnd = volumeRange.endInclusive.toFloat(),
+ targetRangeStart = displayValueRange.start,
+ targetRangeEnd = displayValueRange.endInclusive,
+ )
+ }
+ }
+
+ /** Translates [value] from [displayValueRange] to volume that has [volumeRange]. */
+ fun translateValueToVolume(
+ value: Float,
+ volumeRange: ClosedRange<Int>,
+ ): Int {
+ return translateToRange(
+ currentValue = value,
+ currentRangeStart = displayValueRange.start,
+ currentRangeEnd = displayValueRange.endInclusive,
+ targetRangeStart = volumeRange.start.toFloat(),
+ targetRangeEnd = volumeRange.endInclusive.toFloat(),
+ )
+ .toInt()
+ }
+
+ /**
+ * Translates a value from one range to another.
+ *
+ * ```
+ * Given: currentValue=3, currentRange=[0, 8], targetRange=[0, 100]
+ * Result: 37.5
+ * ```
+ */
+ private fun translateToRange(
+ currentValue: Float,
+ currentRangeStart: Float,
+ currentRangeEnd: Float,
+ targetRangeStart: Float,
+ targetRangeEnd: Float,
+ ): Float {
+ val currentRangeLength: Float = (currentRangeEnd - currentRangeStart)
+ val targetRangeLength: Float = targetRangeEnd - targetRangeStart
+ if (currentRangeLength == 0f || targetRangeLength == 0f) {
+ return 0f
+ }
+ val volumeFraction: Float = (currentValue - currentRangeStart) / currentRangeLength
+ return targetRangeStart + volumeFraction * targetRangeLength
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/model/SliderType.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/model/SliderType.kt
new file mode 100644
index 000000000000..b97123b29b68
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/model/SliderType.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.volume.domain.model
+
+import com.android.settingslib.volume.shared.model.AudioStream
+
+/** The type of volume slider that can be shown at the UI. */
+sealed interface SliderType {
+
+ /** The slider represents one of the device volume streams. */
+ data class Stream(val stream: AudioStream) : SliderType
+
+ /** The represents media device casting volume. */
+ data object MediaDeviceCast : SliderType
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
new file mode 100644
index 000000000000..faf743475579
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.volume.slider.ui.viewmodel
+
+import android.content.Context
+import android.media.AudioManager
+import com.android.settingslib.volume.domain.interactor.AudioVolumeInteractor
+import com.android.settingslib.volume.shared.model.AudioStream
+import com.android.settingslib.volume.shared.model.AudioStreamModel
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.res.R
+import com.android.systemui.volume.panel.component.volume.domain.interactor.VolumeSliderInteractor
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+
+/** Models a particular slider state. */
+class AudioStreamSliderViewModel
+@AssistedInject
+constructor(
+ @Assisted private val audioStreamWrapper: FactoryAudioStreamWrapper,
+ @Assisted private val coroutineScope: CoroutineScope,
+ private val context: Context,
+ private val audioVolumeInteractor: AudioVolumeInteractor,
+ private val volumeSliderInteractor: VolumeSliderInteractor,
+) : SliderViewModel {
+
+ private val audioStream = audioStreamWrapper.audioStream
+ private val iconsByStream =
+ mapOf(
+ AudioStream(AudioManager.STREAM_MUSIC) to R.drawable.ic_music_note,
+ AudioStream(AudioManager.STREAM_VOICE_CALL) to R.drawable.ic_call,
+ AudioStream(AudioManager.STREAM_RING) to R.drawable.ic_ring_volume,
+ AudioStream(AudioManager.STREAM_NOTIFICATION) to R.drawable.ic_volume_ringer,
+ AudioStream(AudioManager.STREAM_ALARM) to R.drawable.ic_volume_alarm,
+ )
+ private val mutedIconsByStream =
+ mapOf(
+ AudioStream(AudioManager.STREAM_MUSIC) to R.drawable.ic_volume_off,
+ AudioStream(AudioManager.STREAM_VOICE_CALL) to R.drawable.ic_volume_off,
+ AudioStream(AudioManager.STREAM_RING) to R.drawable.ic_volume_off,
+ AudioStream(AudioManager.STREAM_NOTIFICATION) to R.drawable.ic_volume_off,
+ AudioStream(AudioManager.STREAM_ALARM) to R.drawable.ic_volume_off,
+ )
+ private val labelsByStream =
+ mapOf(
+ AudioStream(AudioManager.STREAM_MUSIC) to R.string.stream_music,
+ AudioStream(AudioManager.STREAM_VOICE_CALL) to R.string.stream_voice_call,
+ AudioStream(AudioManager.STREAM_RING) to R.string.stream_ring,
+ AudioStream(AudioManager.STREAM_NOTIFICATION) to R.string.stream_notification,
+ AudioStream(AudioManager.STREAM_ALARM) to R.string.stream_alarm,
+ )
+ private val disabledTextByStream =
+ mapOf(
+ AudioStream(AudioManager.STREAM_NOTIFICATION) to
+ R.string.stream_notification_unavailable,
+ )
+
+ private var value = 0f
+ override val slider: StateFlow<SliderState> =
+ combine(
+ audioVolumeInteractor.getAudioStream(audioStream),
+ audioVolumeInteractor.canChangeVolume(audioStream),
+ ) { model, isEnabled ->
+ model.toState(value, isEnabled)
+ }
+ .stateIn(coroutineScope, SharingStarted.Eagerly, EmptyState)
+
+ override fun onValueChangeFinished(state: SliderState, newValue: Float) {
+ val audioViewModel = state as? State
+ audioViewModel ?: return
+ coroutineScope.launch {
+ value = newValue
+ val volume =
+ volumeSliderInteractor.translateValueToVolume(
+ newValue,
+ audioViewModel.audioStreamModel.volumeRange
+ )
+ audioVolumeInteractor.setVolume(audioStream, volume)
+ }
+ }
+
+ private fun AudioStreamModel.toState(value: Float, isEnabled: Boolean): State {
+ return State(
+ value =
+ volumeSliderInteractor.processVolumeToValue(
+ volume,
+ volumeRange,
+ value,
+ isMuted,
+ ),
+ valueRange = volumeSliderInteractor.displayValueRange,
+ icon = getIcon(this),
+ label = labelsByStream[audioStream]?.let(context::getString)
+ ?: error("No label for the stream: $audioStream"),
+ disabledMessage = disabledTextByStream[audioStream]?.let(context::getString),
+ isEnabled = isEnabled,
+ audioStreamModel = this,
+ )
+ }
+
+ private fun getIcon(model: AudioStreamModel): Icon {
+ val isMutedOrNoVolume = model.isMuted || model.volume == model.minVolume
+ val iconRes =
+ if (isMutedOrNoVolume) {
+ mutedIconsByStream
+ } else {
+ iconsByStream
+ }[audioStream]
+ ?: error("No icon for the stream: $audioStream")
+ return Icon.Resource(iconRes, null)
+ }
+
+ private val AudioStreamModel.volumeRange: IntRange
+ get() = minVolume..maxVolume
+
+ private data class State(
+ override val value: Float,
+ override val valueRange: ClosedFloatingPointRange<Float>,
+ override val icon: Icon,
+ override val label: String,
+ override val disabledMessage: String?,
+ override val isEnabled: Boolean,
+ val audioStreamModel: AudioStreamModel,
+ ) : SliderState
+
+ private data object EmptyState : SliderState {
+ override val value: Float = 0f
+ override val valueRange: ClosedFloatingPointRange<Float> = 0f..1f
+ override val icon: Icon? = null
+ override val label: String = ""
+ override val disabledMessage: String? = null
+ override val isEnabled: Boolean = true
+ }
+
+ @AssistedFactory
+ interface Factory {
+
+ fun create(
+ audioStream: FactoryAudioStreamWrapper,
+ coroutineScope: CoroutineScope,
+ ): AudioStreamSliderViewModel
+ }
+
+ /**
+ * AudioStream is a value class that compiles into a primitive. This fail AssistedFactory build
+ * when using [AudioStream] directly because it expects another type.
+ */
+ class FactoryAudioStreamWrapper(val audioStream: AudioStream)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt
new file mode 100644
index 000000000000..ae93826c0c17
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.volume.slider.ui.viewmodel
+
+import android.content.Context
+import com.android.settingslib.volume.domain.model.RoutingSession
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.res.R
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor
+import com.android.systemui.volume.panel.component.volume.domain.interactor.CastVolumeInteractor
+import com.android.systemui.volume.panel.component.volume.domain.interactor.VolumeSliderInteractor
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+
+class CastVolumeSliderViewModel
+@AssistedInject
+constructor(
+ @Assisted private val routingSession: RoutingSession,
+ @Assisted private val coroutineScope: CoroutineScope,
+ private val context: Context,
+ mediaOutputInteractor: MediaOutputInteractor,
+ private val volumeSliderInteractor: VolumeSliderInteractor,
+ private val castVolumeInteractor: CastVolumeInteractor,
+) : SliderViewModel {
+
+ private val volumeRange = 0..routingSession.routingSessionInfo.volumeMax
+ private val value = MutableStateFlow(0f)
+
+ override val slider: StateFlow<SliderState> =
+ combine(value, mediaOutputInteractor.currentConnectedDevice) { value, _ ->
+ getCurrentState(value)
+ }
+ .stateIn(coroutineScope, SharingStarted.Eagerly, getCurrentState(value.value))
+
+ override fun onValueChangeFinished(state: SliderState, newValue: Float) {
+ coroutineScope.launch {
+ value.value = newValue
+ castVolumeInteractor.setVolume(
+ routingSession,
+ volumeSliderInteractor.translateValueToVolume(newValue, volumeRange),
+ )
+ }
+ }
+
+ private fun getCurrentState(value: Float): State {
+ return State(
+ value =
+ volumeSliderInteractor.processVolumeToValue(
+ volume = routingSession.routingSessionInfo.volume,
+ volumeRange = volumeRange,
+ currentValue = value,
+ isMuted = false,
+ ),
+ valueRange = volumeSliderInteractor.displayValueRange,
+ icon = Icon.Resource(R.drawable.ic_cast, null),
+ label = context.getString(R.string.media_device_cast),
+ isEnabled = true,
+ )
+ }
+
+ private data class State(
+ override val value: Float,
+ override val valueRange: ClosedFloatingPointRange<Float>,
+ override val icon: Icon,
+ override val label: String,
+ override val isEnabled: Boolean,
+ ) : SliderState {
+ override val disabledMessage: String?
+ get() = null
+ }
+
+ @AssistedFactory
+ interface Factory {
+
+ fun create(
+ routingSession: RoutingSession,
+ coroutineScope: CoroutineScope,
+ ): CastVolumeSliderViewModel
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderState.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderState.kt
new file mode 100644
index 000000000000..6e9794b02143
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderState.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.volume.slider.ui.viewmodel
+
+import com.android.systemui.common.shared.model.Icon
+
+/**
+ * Models a state of a volume slider.
+ *
+ * @property disabledMessage is shown when [isEnabled] is false
+ */
+sealed interface SliderState {
+ val value: Float
+ val valueRange: ClosedFloatingPointRange<Float>
+ val icon: Icon?
+ val label: String
+ val disabledMessage: String?
+ val isEnabled: Boolean
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderViewModel.kt
new file mode 100644
index 000000000000..0c4b3222e34d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderViewModel.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.volume.slider.ui.viewmodel
+
+import kotlinx.coroutines.flow.StateFlow
+
+/** Controls the behaviour of a volume slider. */
+interface SliderViewModel {
+
+ val slider: StateFlow<SliderState>
+
+ fun onValueChangeFinished(state: SliderState, newValue: Float)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/AudioVolumeComponentViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/AudioVolumeComponentViewModel.kt
new file mode 100644
index 000000000000..2824323775d3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/AudioVolumeComponentViewModel.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.volume.ui.viewmodel
+
+import android.media.AudioManager
+import com.android.settingslib.volume.shared.model.AudioStream
+import com.android.systemui.volume.panel.component.volume.domain.interactor.CastVolumeInteractor
+import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.AudioStreamSliderViewModel
+import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.CastVolumeSliderViewModel
+import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.SliderViewModel
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.transformLatest
+
+/**
+ * Controls the behaviour of the whole audio
+ * [com.android.systemui.volume.panel.shared.model.VolumePanelUiComponent].
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+@VolumePanelScope
+class AudioVolumeComponentViewModel
+@Inject
+constructor(
+ @VolumePanelScope private val scope: CoroutineScope,
+ castVolumeInteractor: CastVolumeInteractor,
+ private val streamSliderViewModelFactory: AudioStreamSliderViewModel.Factory,
+ private val castVolumeSliderViewModelFactory: CastVolumeSliderViewModel.Factory,
+) {
+
+ private val remoteSessionsViewModels: Flow<List<SliderViewModel>> =
+ castVolumeInteractor.remoteRoutingSessions.transformLatest { routingSessions ->
+ coroutineScope {
+ emit(
+ routingSessions.map { routingSession ->
+ castVolumeSliderViewModelFactory.create(routingSession, this)
+ }
+ )
+ }
+ }
+ private val streamViewModels: Flow<List<SliderViewModel>> =
+ flowOf(
+ listOf(
+ AudioStream(AudioManager.STREAM_MUSIC),
+ AudioStream(AudioManager.STREAM_VOICE_CALL),
+ AudioStream(AudioManager.STREAM_RING),
+ AudioStream(AudioManager.STREAM_NOTIFICATION),
+ AudioStream(AudioManager.STREAM_ALARM),
+ )
+ )
+ .transformLatest { streams ->
+ coroutineScope {
+ emit(
+ streams.map { stream ->
+ streamSliderViewModelFactory.create(
+ AudioStreamSliderViewModel.FactoryAudioStreamWrapper(stream),
+ this,
+ )
+ }
+ )
+ }
+ }
+
+ val sliderViewModels: StateFlow<List<SliderViewModel>> =
+ combine(remoteSessionsViewModels, streamViewModels) {
+ remoteSessionsViewModels,
+ streamViewModels ->
+ remoteSessionsViewModels + streamViewModels
+ }
+ .stateIn(scope, SharingStarted.Eagerly, emptyList())
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/VolumePanelComponent.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/VolumePanelComponent.kt
index 841daf85ebcc..f31ee865eaac 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/VolumePanelComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/dagger/VolumePanelComponent.kt
@@ -16,8 +16,11 @@
package com.android.systemui.volume.panel.dagger
+import com.android.systemui.volume.panel.component.anc.AncModule
import com.android.systemui.volume.panel.component.bottombar.BottomBarModule
import com.android.systemui.volume.panel.component.captioning.CaptioningModule
+import com.android.systemui.volume.panel.component.mediaoutput.MediaOutputModule
+import com.android.systemui.volume.panel.component.volume.VolumeSlidersModule
import com.android.systemui.volume.panel.dagger.factory.VolumePanelComponentFactory
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
import com.android.systemui.volume.panel.domain.DomainModule
@@ -45,7 +48,10 @@ import kotlinx.coroutines.CoroutineScope
UiModule::class,
// Components modules
BottomBarModule::class,
+ AncModule::class,
+ VolumeSlidersModule::class,
CaptioningModule::class,
+ MediaOutputModule::class,
]
)
interface VolumePanelComponent {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/domain/DomainModule.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/DomainModule.kt
index defa92def893..57ea9972012f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/domain/DomainModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/DomainModule.kt
@@ -50,7 +50,10 @@ interface DomainModule {
@VolumePanelScope
fun provideEnabledComponents(): Collection<VolumePanelComponentKey> {
return setOf(
+ VolumePanelComponents.ANC,
VolumePanelComponents.CAPTIONING,
+ VolumePanelComponents.VOLUME_SLIDERS,
+ VolumePanelComponents.MEDIA_OUTPUT,
VolumePanelComponents.BOTTOM_BAR,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/UiModule.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/UiModule.kt
index a3f052d88b7d..ec4da0692841 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/UiModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/UiModule.kt
@@ -37,13 +37,18 @@ interface UiModule {
@Provides
@VolumePanelScope
@HeaderComponents
- fun provideHeaderComponents(): Collection<VolumePanelComponentKey> = setOf()
+ fun provideHeaderComponents(): Collection<VolumePanelComponentKey> {
+ return setOf(
+ VolumePanelComponents.MEDIA_OUTPUT,
+ )
+ }
@Provides
@VolumePanelScope
@FooterComponents
fun provideFooterComponents(): Collection<VolumePanelComponentKey> {
- return setOf(
+ return listOf(
+ VolumePanelComponents.ANC,
VolumePanelComponents.CAPTIONING,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/activity/VolumePanelActivity.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/activity/VolumePanelActivity.kt
index 1b2265b9891e..53e1b8b5bb70 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/activity/VolumePanelActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/activity/VolumePanelActivity.kt
@@ -20,8 +20,8 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
-import androidx.core.view.WindowCompat
import com.android.systemui.compose.ComposeFacade
+import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.volume.panel.shared.flag.VolumePanelFlag
import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelViewModel
import javax.inject.Inject
@@ -32,6 +32,7 @@ class VolumePanelActivity
constructor(
private val volumePanelViewModelFactory: Provider<VolumePanelViewModel.Factory>,
private val volumePanelFlag: VolumePanelFlag,
+ private val configurationController: ConfigurationController,
) : ComponentActivity() {
private val viewModel: VolumePanelViewModel by
@@ -43,7 +44,11 @@ constructor(
volumePanelFlag.assertNewVolumePanel()
- WindowCompat.setDecorFitsSystemWindows(window, false)
ComposeFacade.setVolumePanelActivityContent(this, viewModel) { finish() }
}
+
+ override fun onContentChanged() {
+ super.onContentChanged()
+ configurationController.onConfigurationChanged(resources.configuration)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelState.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelState.kt
index f67db965e28a..f57e2931c9b3 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelState.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelState.kt
@@ -22,10 +22,13 @@ import android.content.res.Configuration.Orientation
/**
* State of the Volume Panel itself.
*
- * @property orientation is current Volume Panel orientation.
+ * @property orientation is current Volume Panel orientation
+ * @property isLargeScreen is true when Volume Panel should use wide-screen layout and false the
+ * otherwise
*/
data class VolumePanelState(
@Orientation val orientation: Int,
+ val isLargeScreen: Boolean,
val isVisible: Boolean,
) {
init {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt
index d87a79ed90f4..5ae827ff4e3d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt
@@ -21,6 +21,7 @@ import android.content.res.Resources
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.onConfigChanged
import com.android.systemui.volume.panel.dagger.VolumePanelComponent
@@ -72,7 +73,11 @@ class VolumePanelViewModel(
.distinctUntilChanged(),
mutablePanelVisibility,
) { configuration, isVisible ->
- VolumePanelState(orientation = configuration.orientation, isVisible = isVisible)
+ VolumePanelState(
+ orientation = configuration.orientation,
+ isVisible = isVisible,
+ isLargeScreen = resources.getBoolean(R.bool.volume_panel_is_large_screen),
+ )
}
.stateIn(
scope,
@@ -80,6 +85,7 @@ class VolumePanelViewModel(
VolumePanelState(
orientation = resources.configuration.orientation,
isVisible = mutablePanelVisibility.value,
+ isLargeScreen = resources.getBoolean(R.bool.volume_panel_is_large_screen)
),
)
val componentsLayout: Flow<ComponentsLayout> =
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index 139d190ae63c..65dede83f3d6 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -25,6 +25,7 @@ import static android.service.notification.NotificationListenerService.REASON_GR
import static android.service.notification.NotificationStats.DISMISSAL_BUBBLE;
import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
+import static com.android.server.notification.Flags.screenshareNotificationHiding;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
@@ -69,6 +70,7 @@ import com.android.systemui.statusbar.notification.collection.render.Notificatio
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider;
import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.wm.shell.bubbles.Bubble;
import com.android.wm.shell.bubbles.BubbleEntry;
@@ -102,6 +104,7 @@ public class BubblesManager {
private final NotificationVisibilityProvider mVisibilityProvider;
private final VisualInterruptionDecisionProvider mVisualInterruptionDecisionProvider;
private final NotificationLockscreenUserManager mNotifUserManager;
+ private final SensitiveNotificationProtectionController mSensitiveNotifProtectionController;
private final CommonNotifCollection mCommonNotifCollection;
private final NotifPipeline mNotifPipeline;
private final NotifPipelineFlags mNotifPipelineFlags;
@@ -111,6 +114,7 @@ public class BubblesManager {
// TODO (b/145659174): allow for multiple callbacks to support the "shadow" new notif pipeline
private final List<NotifCallback> mCallbacks = new ArrayList<>();
private final StatusBarWindowCallback mStatusBarWindowCallback;
+ private final Runnable mSensitiveStateChangedListener;
private boolean mPanelExpanded;
/**
@@ -130,6 +134,7 @@ public class BubblesManager {
VisualInterruptionDecisionProvider visualInterruptionDecisionProvider,
ZenModeController zenModeController,
NotificationLockscreenUserManager notifUserManager,
+ SensitiveNotificationProtectionController sensitiveNotificationProtectionController,
CommonNotifCollection notifCollection,
NotifPipeline notifPipeline,
SysUiState sysUiState,
@@ -149,6 +154,7 @@ public class BubblesManager {
visualInterruptionDecisionProvider,
zenModeController,
notifUserManager,
+ sensitiveNotificationProtectionController,
notifCollection,
notifPipeline,
sysUiState,
@@ -173,6 +179,7 @@ public class BubblesManager {
VisualInterruptionDecisionProvider visualInterruptionDecisionProvider,
ZenModeController zenModeController,
NotificationLockscreenUserManager notifUserManager,
+ SensitiveNotificationProtectionController sensitiveNotificationProtectionController,
CommonNotifCollection notifCollection,
NotifPipeline notifPipeline,
SysUiState sysUiState,
@@ -188,6 +195,7 @@ public class BubblesManager {
mVisibilityProvider = visibilityProvider;
mVisualInterruptionDecisionProvider = visualInterruptionDecisionProvider;
mNotifUserManager = notifUserManager;
+ mSensitiveNotifProtectionController = sensitiveNotificationProtectionController;
mCommonNotifCollection = notifCollection;
mNotifPipeline = notifPipeline;
mNotifPipelineFlags = notifPipelineFlags;
@@ -251,6 +259,22 @@ public class BubblesManager {
};
notificationShadeWindowController.registerCallback(mStatusBarWindowCallback);
+ mSensitiveStateChangedListener = new Runnable() {
+ @Override
+ public void run() {
+ if (!screenshareNotificationHiding()) {
+ return;
+ }
+ bubbles.onSensitiveNotificationProtectionStateChanged(
+ mSensitiveNotifProtectionController.isSensitiveStateActive());
+ }
+ };
+
+ if (screenshareNotificationHiding()) {
+ mSensitiveNotifProtectionController
+ .registerSensitiveStateListener(mSensitiveStateChangedListener);
+ }
+
mSysuiProxy = new Bubbles.SysuiProxy() {
@Override
public void isNotificationPanelExpand(Consumer<Boolean> callback) {
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index e8325065c219..15e0965c16fe 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -356,6 +356,12 @@ public final class WMShell implements
// TODO(b/278084491): update sysui state for changes on other displays
}
}, mSysUiMainExecutor);
+ mCommandQueue.addCallback(new CommandQueue.Callbacks() {
+ @Override
+ public void enterDesktop(int displayId) {
+ desktopMode.enterDesktop(displayId);
+ }
+ });
}
@VisibleForTesting
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index cd19259091ab..30269664a559 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -260,7 +260,7 @@ class ClockEventControllerTest : SysuiTestCase() {
@Test
fun keyguardCallback_visibilityChanged_clockDozeCalled() =
runBlocking(IMMEDIATE) {
- mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL)
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
verify(keyguardUpdateMonitor).registerCallback(capture(captor))
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index e8d86dddcda5..9d81b960336f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -35,6 +35,7 @@ import android.view.View;
import androidx.test.filters.SmallTest;
+import com.android.systemui.Flags;
import com.android.systemui.plugins.clocks.ClockFaceConfig;
import com.android.systemui.plugins.clocks.ClockTickRate;
import com.android.systemui.shared.clocks.ClockRegistry;
@@ -50,6 +51,8 @@ import org.mockito.verification.VerificationMode;
public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchControllerBaseTest {
@Test
public void testInit_viewAlreadyAttached() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
+
mController.init();
verifyAttachment(times(1));
@@ -57,6 +60,8 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro
@Test
public void testInit_viewNotYetAttached() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
+
ArgumentCaptor<View.OnAttachStateChangeListener> listenerArgumentCaptor =
ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
@@ -73,12 +78,16 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro
@Test
public void testInitSubControllers() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
+
mController.init();
verify(mKeyguardSliceViewController).init();
}
@Test
public void testInit_viewDetached() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
+
ArgumentCaptor<View.OnAttachStateChangeListener> listenerArgumentCaptor =
ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
mController.init();
@@ -92,6 +101,8 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro
@Test
public void testPluginPassesStatusBarState() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
+
ArgumentCaptor<ClockRegistry.ClockChangeListener> listenerArgumentCaptor =
ArgumentCaptor.forClass(ClockRegistry.ClockChangeListener.class);
@@ -105,6 +116,8 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro
@Test
public void testSmartspaceEnabledRemovesKeyguardStatusArea() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
+
when(mSmartspaceController.isEnabled()).thenReturn(true);
mController.init();
@@ -113,6 +126,8 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro
@Test
public void onLocaleListChangedRebuildsSmartspaceView() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
+
when(mSmartspaceController.isEnabled()).thenReturn(true);
mController.init();
@@ -123,6 +138,8 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro
@Test
public void onLocaleListChanged_rebuildsSmartspaceViews_whenDecouplingEnabled() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
+
when(mSmartspaceController.isEnabled()).thenReturn(true);
when(mSmartspaceController.isDateWeatherDecoupled()).thenReturn(true);
mController.init();
@@ -136,6 +153,8 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro
@Test
public void testSmartspaceDisabledShowsKeyguardStatusArea() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
+
when(mSmartspaceController.isEnabled()).thenReturn(false);
mController.init();
@@ -144,6 +163,8 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro
@Test
public void testRefresh() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
+
mController.refresh();
verify(mSmartspaceController).requestSmartspaceUpdate();
@@ -151,6 +172,8 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro
@Test
public void testChangeToDoubleLineClockSetsSmallClock() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
+
when(mSecureSettings.getIntForUser(Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK, 1,
UserHandle.USER_CURRENT))
.thenReturn(0);
@@ -174,11 +197,15 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro
@Test
public void testGetClock_ForwardsToClock() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
+
assertEquals(mClockController, mController.getClock());
}
@Test
public void testGetLargeClockBottom_returnsExpectedValue() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
+
when(mLargeClockFrame.getVisibility()).thenReturn(View.VISIBLE);
when(mLargeClockFrame.getHeight()).thenReturn(100);
when(mSmallClockFrame.getHeight()).thenReturn(50);
@@ -191,6 +218,8 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro
@Test
public void testGetSmallLargeClockBottom_returnsExpectedValue() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
+
when(mLargeClockFrame.getVisibility()).thenReturn(View.GONE);
when(mLargeClockFrame.getHeight()).thenReturn(100);
when(mSmallClockFrame.getHeight()).thenReturn(50);
@@ -203,12 +232,16 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro
@Test
public void testGetClockBottom_nullClock_returnsZero() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
+
when(mClockEventController.getClock()).thenReturn(null);
assertEquals(0, mController.getClockBottom(10));
}
@Test
public void testChangeLockscreenWeatherEnabledSetsWeatherViewVisible() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
+
when(mSmartspaceController.isWeatherEnabled()).thenReturn(true);
ArgumentCaptor<ContentObserver> observerCaptor =
ArgumentCaptor.forClass(ContentObserver.class);
@@ -227,6 +260,8 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro
@Test
public void testChangeClockDateWeatherEnabled_SetsDateWeatherViewVisibility() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
+
ArgumentCaptor<ClockRegistry.ClockChangeListener> listenerArgumentCaptor =
ArgumentCaptor.forClass(ClockRegistry.ClockChangeListener.class);
when(mSmartspaceController.isEnabled()).thenReturn(true);
@@ -249,11 +284,15 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro
@Test
public void testGetClock_nullClock_returnsNull() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
+
when(mClockEventController.getClock()).thenReturn(null);
assertNull(mController.getClock());
}
private void verifyAttachment(VerificationMode times) {
+ mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
+
verify(mClockRegistry, times).registerClockChangeListener(
any(ClockRegistry.ClockChangeListener.class));
verify(mClockEventController, times).registerListeners(mView);
@@ -261,6 +300,8 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro
@Test
public void testSplitShadeEnabledSetToSmartspaceController() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
+
mController.setSplitShadeEnabled(true);
verify(mSmartspaceController, times(1)).setSplitShadeEnabled(true);
verify(mSmartspaceController, times(0)).setSplitShadeEnabled(false);
@@ -268,6 +309,8 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro
@Test
public void testSplitShadeDisabledSetToSmartspaceController() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
+
mController.setSplitShadeEnabled(false);
verify(mSmartspaceController, times(1)).setSplitShadeEnabled(false);
verify(mSmartspaceController, times(0)).setSplitShadeEnabled(true);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index 4508aea81176..b4a9d40a6caf 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -40,6 +40,7 @@ import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TextView;
+import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.clocks.ClockController;
import com.android.systemui.plugins.clocks.ClockFaceController;
@@ -79,6 +80,8 @@ public class KeyguardClockSwitchTest extends SysuiTestCase {
@Before
public void setUp() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
+
MockitoAnnotations.initMocks(this);
when(mMockKeyguardSliceView.getContext()).thenReturn(mContext);
when(mMockKeyguardSliceView.findViewById(R.id.keyguard_status_area))
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
index 438f0f43acb6..cc36cfa6a30a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
@@ -18,8 +18,6 @@ package com.android.keyguard;
import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
-import static com.android.systemui.flags.Flags.ENABLE_CLOCK_KEYGUARD_PRESENTATION;
-
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
@@ -37,9 +35,7 @@ import android.view.DisplayInfo;
import androidx.test.filters.SmallTest;
-import com.android.keyguard.dagger.KeyguardStatusViewComponent;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -60,13 +56,9 @@ public class KeyguardDisplayManagerTest extends SysuiTestCase {
@Mock
private NavigationBarController mNavigationBarController;
@Mock
- private KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
- @Mock
private ConnectedDisplayKeyguardPresentation.Factory
mConnectedDisplayKeyguardPresentationFactory;
@Mock
- private KeyguardDisplayManager.KeyguardPresentation mKeyguardPresentation;
- @Mock
private ConnectedDisplayKeyguardPresentation mConnectedDisplayKeyguardPresentation;
@Mock
private KeyguardDisplayManager.DeviceStateHelper mDeviceStateHelper;
@@ -77,7 +69,6 @@ public class KeyguardDisplayManagerTest extends SysuiTestCase {
private Executor mBackgroundExecutor = Runnable::run;
private KeyguardDisplayManager mManager;
private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
- private FakeFeatureFlags mFakeFeatureFlags = new FakeFeatureFlags();
// The default and secondary displays are both in the default group
private Display mDefaultDisplay;
private Display mSecondaryDisplay;
@@ -88,15 +79,13 @@ public class KeyguardDisplayManagerTest extends SysuiTestCase {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mFakeFeatureFlags.set(ENABLE_CLOCK_KEYGUARD_PRESENTATION, false);
mManager = spy(new KeyguardDisplayManager(mContext, () -> mNavigationBarController,
- mKeyguardStatusViewComponentFactory, mDisplayTracker, mMainExecutor,
- mBackgroundExecutor, mDeviceStateHelper, mKeyguardStateController,
- mConnectedDisplayKeyguardPresentationFactory, mFakeFeatureFlags));
- doReturn(mKeyguardPresentation).when(mManager).createPresentation(any());
+ mDisplayTracker, mMainExecutor, mBackgroundExecutor, mDeviceStateHelper,
+ mKeyguardStateController, mConnectedDisplayKeyguardPresentationFactory));
doReturn(mConnectedDisplayKeyguardPresentation).when(
mConnectedDisplayKeyguardPresentationFactory).create(any());
-
+ doReturn(mConnectedDisplayKeyguardPresentation).when(mManager)
+ .createPresentation(any());
mDefaultDisplay = new Display(DisplayManagerGlobal.getInstance(), Display.DEFAULT_DISPLAY,
new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS);
mSecondaryDisplay = new Display(DisplayManagerGlobal.getInstance(),
@@ -152,9 +141,8 @@ public class KeyguardDisplayManagerTest extends SysuiTestCase {
}
@Test
- public void testShow_withClockPresentationFlagEnabled_presentationCreated() {
+ public void testShow_presentationCreated() {
when(mManager.createPresentation(any())).thenCallRealMethod();
- mFakeFeatureFlags.set(ENABLE_CLOCK_KEYGUARD_PRESENTATION, true);
mDisplayTracker.setAllDisplays(new Display[]{mDefaultDisplay, mSecondaryDisplay});
mManager.show();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
index d048cbe02fa8..36d4d122ab11 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
@@ -18,6 +18,7 @@ package com.android.keyguard
import android.testing.TestableLooper
import android.view.View
+import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -59,7 +60,9 @@ import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper
+// collectFlow in KeyguardPinBasedInputViewController.onViewAttached calls JavaAdapter.CollectFlow,
+// which calls View.onRepeatWhenAttached, which requires being run on main thread.
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
class KeyguardPinViewControllerTest : SysuiTestCase() {
private lateinit var objectKeyguardPINView: KeyguardPINView
@@ -90,6 +93,8 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {
@Mock private val mEmergencyButtonController: EmergencyButtonController? = null
private val falsingCollector: FalsingCollector = FalsingCollectorFake()
private val keyguardKeyboardInteractor = KeyguardKeyboardInteractor(FakeKeyboardRepository())
+ private val passwordTextViewLayoutParams =
+ ViewGroup.LayoutParams(/* width= */ 0, /* height= */ 0)
@Mock lateinit var postureController: DevicePostureController
@Mock lateinit var mSelectedUserInteractor: SelectedUserInteractor
@@ -104,11 +109,9 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- Mockito.`when`(mockKeyguardPinView.requireViewById<View>(R.id.bouncer_message_area))
+ `when`(mockKeyguardPinView.requireViewById<View>(R.id.bouncer_message_area))
.thenReturn(keyguardMessageArea)
- Mockito.`when`(
- keyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea::class.java))
- )
+ `when`(keyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea::class.java)))
.thenReturn(keyguardMessageAreaController)
`when`(mockKeyguardPinView.passwordTextViewId).thenReturn(R.id.pinEntry)
`when`(mockKeyguardPinView.findViewById<PasswordTextView>(R.id.pinEntry))
@@ -121,6 +124,7 @@ class KeyguardPinViewControllerTest : SysuiTestCase() {
`when`(mockKeyguardPinView.buttons).thenReturn(arrayOf())
`when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6)
`when`(featureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)).thenReturn(false)
+ `when`(passwordTextView.layoutParams).thenReturn(passwordTextViewLayoutParams)
objectKeyguardPINView =
View.inflate(mContext, R.layout.keyguard_pin_view, null)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
deleted file mode 100644
index 5102957c71ed..000000000000
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.hardware.display.DisplayManager;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.util.AttributeSet;
-import android.view.Display;
-import android.view.LayoutInflater;
-import android.view.View;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.keyguard.KeyguardDisplayManager.KeyguardPresentation;
-import com.android.keyguard.dagger.KeyguardStatusViewComponent;
-import com.android.systemui.res.R;
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class KeyguardPresentationTest extends SysuiTestCase {
-
- @Mock
- KeyguardClockSwitch mMockKeyguardClockSwitch;
- @Mock
- KeyguardSliceView mMockKeyguardSliceView;
- @Mock
- KeyguardStatusView mMockKeyguardStatusView;
- @Mock
- private KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
- @Mock
- private KeyguardStatusViewComponent mKeyguardStatusViewComponent;
- @Mock
- private KeyguardClockSwitchController mKeyguardClockSwitchController;
-
- LayoutInflater mLayoutInflater;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- when(mMockKeyguardClockSwitch.getContext()).thenReturn(mContext);
- when(mMockKeyguardSliceView.getContext()).thenReturn(mContext);
- when(mMockKeyguardStatusView.getContext()).thenReturn(mContext);
- when(mMockKeyguardStatusView.findViewById(R.id.clock)).thenReturn(mMockKeyguardStatusView);
- when(mKeyguardStatusViewComponentFactory.build(any(KeyguardStatusView.class),
- any(Display.class)))
- .thenReturn(mKeyguardStatusViewComponent);
- when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController())
- .thenReturn(mKeyguardClockSwitchController);
-
- allowTestableLooperAsMainThread();
-
- mLayoutInflater = LayoutInflater.from(mContext);
- mLayoutInflater.setPrivateFactory(new LayoutInflater.Factory2() {
-
- @Override
- public View onCreateView(View parent, String name, Context context,
- AttributeSet attrs) {
- return onCreateView(name, context, attrs);
- }
-
- @Override
- public View onCreateView(String name, Context context, AttributeSet attrs) {
- if ("com.android.keyguard.KeyguardStatusView".equals(name)) {
- return mMockKeyguardStatusView;
- } else if ("com.android.keyguard.KeyguardClockSwitch".equals(name)) {
- return mMockKeyguardClockSwitch;
- } else if ("com.android.keyguard.KeyguardSliceView".equals(name)) {
- return mMockKeyguardStatusView;
- }
- return null;
- }
- });
- }
-
- @After
- public void tearDown() {
- disallowTestableLooperAsMainThread();
- }
-
- @Test
- public void testInflation_doesntCrash() {
- final Display display = mContext.getSystemService(DisplayManager.class).getDisplay(
- Display.DEFAULT_DISPLAY);
- KeyguardPresentation keyguardPresentation = new KeyguardPresentation(mContext, display,
- mKeyguardStatusViewComponentFactory);
- keyguardPresentation.onCreate(null /*savedInstanceState */);
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
index 4a2554eb5201..9b5364ed6e6e 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
@@ -49,7 +49,9 @@ import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper
+// collectFlow in KeyguardPinBasedInputViewController.onViewAttached calls JavaAdapter.CollectFlow,
+// which calls View.onRepeatWhenAttached, which requires being run on main thread.
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
class KeyguardSimPinViewControllerTest : SysuiTestCase() {
private lateinit var simPinView: KeyguardSimPinView
private lateinit var underTest: KeyguardSimPinViewController
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
index 4f461845d905..e71490c73aa1 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
@@ -43,7 +43,9 @@ import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper
+// collectFlow in KeyguardPinBasedInputViewController.onViewAttached calls JavaAdapter.CollectFlow,
+// which calls View.onRepeatWhenAttached, which requires being run on main thread.
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
class KeyguardSimPukViewControllerTest : SysuiTestCase() {
private lateinit var simPukView: KeyguardSimPukView
private lateinit var underTest: KeyguardSimPukViewController
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
index 4d3243a2ccf6..edb910a3acc2 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
@@ -22,8 +22,10 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.pm.PackageManager;
+import android.os.Handler;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
@@ -57,17 +59,22 @@ public class KeyguardSliceViewControllerTest extends SysuiTestCase {
private ActivityStarter mActivityStarter;
private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
private DumpManager mDumpManager = new DumpManager();
-
+ private Handler mHandler;
+ private Handler mBgHandler;
private KeyguardSliceViewController mController;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ TestableLooper testableLooper = TestableLooper.get(this);
+ assert testableLooper != null;
+ mHandler = new Handler(testableLooper.getLooper());
+ mBgHandler = new Handler(testableLooper.getLooper());
when(mView.isAttachedToWindow()).thenReturn(true);
when(mView.getContext()).thenReturn(mContext);
- mController = new KeyguardSliceViewController(
- mView, mActivityStarter, mConfigurationController,
- mTunerService, mDumpManager, mDisplayTracker);
+ mController = new KeyguardSliceViewController(mHandler, mBgHandler, mView,
+ mActivityStarter, mConfigurationController, mTunerService, mDumpManager,
+ mDisplayTracker);
mController.setupUri(KeyguardSliceProvider.KEYGUARD_SLICE_URI);
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index be06cc5d3d1d..538daee52377 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -1500,7 +1500,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
verify(mHandler).postDelayed(mKeyguardUpdateMonitor.mFpCancelNotReceived,
DEFAULT_CANCEL_SIGNAL_TIMEOUT);
-
mKeyguardUpdateMonitor.onFingerprintAuthenticated(0, true);
mTestableLooper.processAllMessages();
@@ -2016,6 +2015,34 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
@Test
+ public void authenticateFingerprint_onFaceLockout_detectFingerprint() throws RemoteException {
+ // GIVEN fingerprintAuthenticate
+ mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
+ mTestableLooper.processAllMessages();
+ verifyFingerprintAuthenticateCall();
+ verifyFingerprintDetectNeverCalled();
+ clearInvocations(mFingerprintManager);
+
+ // WHEN class 3 face is locked out
+ when(mFaceAuthInteractor.isFaceAuthStrong()).thenReturn(true);
+ when(mFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()).thenReturn(true);
+ setupFingerprintAuth(/* isClass3 */ true);
+ // GIVEN primary auth is not required by StrongAuthTracker
+ primaryAuthNotRequiredByStrongAuthTracker();
+
+ // WHEN face (class 3) is locked out
+ faceAuthLockOut();
+ mTestableLooper.processAllMessages();
+
+ // THEN unlocking with fingerprint is not allowed
+ Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+ BiometricSourceType.FINGERPRINT));
+
+ // THEN fingerprint detect gets called
+ verifyFingerprintDetectCall();
+ }
+
+ @Test
public void testFingerprintSensorProperties() throws RemoteException {
mFingerprintAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(
new ArrayList<>());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/CameraProtectionLoaderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/CameraProtectionLoaderImplTest.kt
index a19a0c7d12a3..d2a17c2ccbb4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/CameraProtectionLoaderImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/CameraProtectionLoaderImplTest.kt
@@ -89,7 +89,7 @@ class CameraProtectionLoaderImplTest : SysuiTestCase() {
loader.loadCameraProtectionInfoList().map { it.toTestableVersion() }
private fun CameraProtectionInfo.toTestableVersion() =
- TestableProtectionInfo(logicalCameraId, physicalCameraId, cutoutBounds, displayUniqueId)
+ TestableProtectionInfo(logicalCameraId, physicalCameraId, bounds, displayUniqueId)
/**
* "Testable" version, because the original version contains a Path property, which doesn't
diff --git a/packages/SystemUI/tests/src/com/android/systemui/FakeCameraProtectionLoader.kt b/packages/SystemUI/tests/src/com/android/systemui/FakeCameraProtectionLoader.kt
index f769b4e5f2d0..6cb77cff4404 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/FakeCameraProtectionLoader.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/FakeCameraProtectionLoader.kt
@@ -16,6 +16,7 @@
package com.android.systemui
+import android.graphics.Rect
import com.android.systemui.res.R
class FakeCameraProtectionLoader(private val context: SysuiTestableContext) :
@@ -36,7 +37,10 @@ class FakeCameraProtectionLoader(private val context: SysuiTestableContext) :
addInnerCameraProtection()
}
- fun addOuterCameraProtection(displayUniqueId: String = "111") {
+ fun addOuterCameraProtection(
+ displayUniqueId: String = "111",
+ bounds: Rect = Rect(/* left = */ 0, /* top = */ 0, /* right = */ 10, /* bottom = */ 10)
+ ) {
context.orCreateTestableResources.addOverride(R.string.config_protectedCameraId, "1")
context.orCreateTestableResources.addOverride(
R.string.config_protectedPhysicalCameraId,
@@ -44,7 +48,7 @@ class FakeCameraProtectionLoader(private val context: SysuiTestableContext) :
)
context.orCreateTestableResources.addOverride(
R.string.config_frontBuiltInDisplayCutoutProtection,
- "M 0,0 H 10,10 V 10,10 H 0,10 Z"
+ bounds.asPath(),
)
context.orCreateTestableResources.addOverride(
R.string.config_protectedScreenUniqueId,
@@ -52,7 +56,10 @@ class FakeCameraProtectionLoader(private val context: SysuiTestableContext) :
)
}
- fun addInnerCameraProtection(displayUniqueId: String = "222") {
+ fun addInnerCameraProtection(
+ displayUniqueId: String = "222",
+ bounds: Rect = Rect(/* left = */ 0, /* top = */ 0, /* right = */ 20, /* bottom = */ 20)
+ ) {
context.orCreateTestableResources.addOverride(R.string.config_protectedInnerCameraId, "2")
context.orCreateTestableResources.addOverride(
R.string.config_protectedInnerPhysicalCameraId,
@@ -60,11 +67,13 @@ class FakeCameraProtectionLoader(private val context: SysuiTestableContext) :
)
context.orCreateTestableResources.addOverride(
R.string.config_innerBuiltInDisplayCutoutProtection,
- "M 0,0 H 20,20 V 20,20 H 0,20 Z"
+ bounds.asPath(),
)
context.orCreateTestableResources.addOverride(
R.string.config_protectedInnerScreenUniqueId,
displayUniqueId
)
}
+
+ private fun Rect.asPath() = "M $left, $top H $right V $bottom H $left Z"
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysUICutoutProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/SysUICutoutProviderTest.kt
index f37c4ae613ff..61c7e1d63e51 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysUICutoutProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysUICutoutProviderTest.kt
@@ -16,11 +16,16 @@
package com.android.systemui
+import android.graphics.Rect
import android.view.Display
import android.view.DisplayAdjustments
import android.view.DisplayCutout
+import android.view.DisplayInfo
+import android.view.Surface
+import android.view.Surface.Rotation
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -39,7 +44,7 @@ class SysUICutoutProviderTest : SysuiTestCase() {
val noCutoutDisplayContext = context.createDisplayContext(noCutoutDisplay)
val provider = SysUICutoutProvider(noCutoutDisplayContext, fakeProtectionLoader)
- val sysUICutout = provider.cutoutInfoForCurrentDisplay()
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()
assertThat(sysUICutout).isNull()
}
@@ -50,7 +55,7 @@ class SysUICutoutProviderTest : SysuiTestCase() {
val cutoutDisplayContext = context.createDisplayContext(cutoutDisplay)
val provider = SysUICutoutProvider(cutoutDisplayContext, fakeProtectionLoader)
- val sysUICutout = provider.cutoutInfoForCurrentDisplay()!!
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()!!
assertThat(sysUICutout.cutout).isEqualTo(cutoutDisplay.cutout)
}
@@ -61,7 +66,7 @@ class SysUICutoutProviderTest : SysuiTestCase() {
val cutoutDisplayContext = context.createDisplayContext(cutoutDisplay)
val provider = SysUICutoutProvider(cutoutDisplayContext, fakeProtectionLoader)
- val sysUICutout = provider.cutoutInfoForCurrentDisplay()!!
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()!!
assertThat(sysUICutout.cameraProtection).isNull()
}
@@ -72,7 +77,7 @@ class SysUICutoutProviderTest : SysuiTestCase() {
val outerDisplayContext = context.createDisplayContext(OUTER_DISPLAY)
val provider = SysUICutoutProvider(outerDisplayContext, fakeProtectionLoader)
- val sysUICutout = provider.cutoutInfoForCurrentDisplay()!!
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()!!
assertThat(sysUICutout.cameraProtection).isNotNull()
}
@@ -83,7 +88,7 @@ class SysUICutoutProviderTest : SysuiTestCase() {
val outerDisplayContext = context.createDisplayContext(OUTER_DISPLAY)
val provider = SysUICutoutProvider(outerDisplayContext, fakeProtectionLoader)
- val sysUICutout = provider.cutoutInfoForCurrentDisplay()!!
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()!!
assertThat(sysUICutout.cameraProtection).isNull()
}
@@ -94,7 +99,7 @@ class SysUICutoutProviderTest : SysuiTestCase() {
val displayContext = context.createDisplayContext(createDisplay(uniqueId = null))
val provider = SysUICutoutProvider(displayContext, fakeProtectionLoader)
- val sysUICutout = provider.cutoutInfoForCurrentDisplay()!!
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()!!
assertThat(sysUICutout.cameraProtection).isNull()
}
@@ -105,20 +110,170 @@ class SysUICutoutProviderTest : SysuiTestCase() {
val displayContext = context.createDisplayContext(createDisplay(uniqueId = ""))
val provider = SysUICutoutProvider(displayContext, fakeProtectionLoader)
- val sysUICutout = provider.cutoutInfoForCurrentDisplay()!!
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()!!
assertThat(sysUICutout.cameraProtection).isNull()
}
+ @Test
+ fun cutoutInfo_rotation0_returnsOriginalProtectionBounds() {
+ val provider =
+ setUpProviderWithCameraProtection(
+ displayWidth = 500,
+ displayHeight = 1000,
+ rotation = Surface.ROTATION_0,
+ protectionBounds =
+ Rect(/* left = */ 440, /* top = */ 10, /* right = */ 490, /* bottom = */ 110)
+ )
+
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()!!
+
+ assertThat(sysUICutout.cameraProtection!!.bounds)
+ .isEqualTo(
+ Rect(/* left = */ 440, /* top = */ 10, /* right = */ 490, /* bottom = */ 110)
+ )
+ }
+
+ @Test
+ fun cutoutInfo_rotation90_returnsRotatedProtectionBounds() {
+ val provider =
+ setUpProviderWithCameraProtection(
+ displayWidth = 500,
+ displayHeight = 1000,
+ rotation = Surface.ROTATION_90,
+ protectionBounds =
+ Rect(/* left = */ 440, /* top = */ 10, /* right = */ 490, /* bottom = */ 110)
+ )
+
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()!!
+
+ assertThat(sysUICutout.cameraProtection!!.bounds)
+ .isEqualTo(Rect(/* left = */ 10, /* top = */ 10, /* right = */ 110, /* bottom = */ 60))
+ }
+
+ @Test
+ fun cutoutInfo_withRotation_doesNotMutateOriginalBounds() {
+ val displayNaturalWidth = 500
+ val displayNaturalHeight = 1000
+ val originalProtectionBounds =
+ Rect(/* left = */ 440, /* top = */ 10, /* right = */ 490, /* bottom = */ 110)
+ // Safe copy as we don't know at which layer the mutation could happen
+ val originalProtectionBoundsCopy = Rect(originalProtectionBounds)
+ val display =
+ createDisplay(
+ uniqueId = OUTER_DISPLAY_UNIQUE_ID,
+ rotation = Surface.ROTATION_180,
+ width = displayNaturalWidth,
+ height = displayNaturalHeight,
+ )
+ fakeProtectionLoader.addOuterCameraProtection(
+ displayUniqueId = OUTER_DISPLAY_UNIQUE_ID,
+ bounds = originalProtectionBounds
+ )
+ val provider =
+ SysUICutoutProvider(context.createDisplayContext(display), fakeProtectionLoader)
+
+ // Here we get the rotated bounds once
+ provider.cutoutInfoForCurrentDisplayAndRotation()
+
+ // Rotate display back to original rotation
+ whenever(display.rotation).thenReturn(Surface.ROTATION_0)
+
+ // Now the bounds should match the original ones. We are checking for mutation since Rect
+ // is mutable and has many methods that mutate the instance, and it is easy to do it by
+ // mistake.
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()!!
+ assertThat(sysUICutout.cameraProtection!!.bounds).isEqualTo(originalProtectionBoundsCopy)
+ }
+
+ @Test
+ fun cutoutInfo_rotation180_returnsRotatedProtectionBounds() {
+ val provider =
+ setUpProviderWithCameraProtection(
+ displayWidth = 500,
+ displayHeight = 1000,
+ rotation = Surface.ROTATION_180,
+ protectionBounds =
+ Rect(/* left = */ 440, /* top = */ 10, /* right = */ 490, /* bottom = */ 110)
+ )
+
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()!!
+
+ assertThat(sysUICutout.cameraProtection!!.bounds)
+ .isEqualTo(Rect(/* left = */ 10, /* top = */ 890, /* right = */ 60, /* bottom = */ 990))
+ }
+
+ @Test
+ fun cutoutInfo_rotation270_returnsRotatedProtectionBounds() {
+ val provider =
+ setUpProviderWithCameraProtection(
+ displayWidth = 500,
+ displayHeight = 1000,
+ rotation = Surface.ROTATION_270,
+ protectionBounds =
+ Rect(/* left = */ 440, /* top = */ 10, /* right = */ 490, /* bottom = */ 110)
+ )
+
+ val sysUICutout = provider.cutoutInfoForCurrentDisplayAndRotation()!!
+
+ assertThat(sysUICutout.cameraProtection!!.bounds)
+ .isEqualTo(
+ Rect(/* left = */ 890, /* top = */ 440, /* right = */ 990, /* bottom = */ 490)
+ )
+ }
+
+ private fun setUpProviderWithCameraProtection(
+ displayWidth: Int,
+ displayHeight: Int,
+ rotation: Int = Surface.ROTATION_0,
+ protectionBounds: Rect,
+ ): SysUICutoutProvider {
+ val display =
+ createDisplay(
+ uniqueId = OUTER_DISPLAY_UNIQUE_ID,
+ rotation = rotation,
+ width =
+ if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
+ displayWidth
+ } else {
+ displayHeight
+ },
+ height =
+ if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180)
+ displayHeight
+ else displayWidth,
+ )
+ fakeProtectionLoader.addOuterCameraProtection(
+ displayUniqueId = OUTER_DISPLAY_UNIQUE_ID,
+ bounds = protectionBounds
+ )
+ return SysUICutoutProvider(context.createDisplayContext(display), fakeProtectionLoader)
+ }
+
companion object {
private const val OUTER_DISPLAY_UNIQUE_ID = "outer"
private val OUTER_DISPLAY = createDisplay(uniqueId = OUTER_DISPLAY_UNIQUE_ID)
private fun createDisplay(
+ width: Int = 500,
+ height: Int = 1000,
+ @Rotation rotation: Int = Surface.ROTATION_0,
uniqueId: String? = "uniqueId",
cutout: DisplayCutout? = mock<DisplayCutout>()
) =
mock<Display> {
+ whenever(this.getDisplayInfo(any())).thenAnswer {
+ val displayInfo = it.arguments[0] as DisplayInfo
+ displayInfo.rotation = rotation
+ displayInfo.logicalWidth = width
+ displayInfo.logicalHeight = height
+ return@thenAnswer true
+ }
+ // Setting width and height to smaller values to simulate real behavior of this API
+ // not always returning the real display size
+ whenever(this.width).thenReturn(width - 5)
+ whenever(this.height).thenReturn(height - 10)
+ whenever(this.rotation).thenReturn(rotation)
whenever(this.displayAdjustments).thenReturn(DisplayAdjustments())
whenever(this.cutout).thenReturn(cutout)
whenever(this.uniqueId).thenReturn(uniqueId)
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 c2ed7d4a9d3c..976cd5bd21c4 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
@@ -21,6 +21,7 @@ import static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.ACTIO
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
@@ -89,6 +90,7 @@ public class MenuItemAccessibilityDelegateTest extends SysuiTestCase {
mMenuView = spy(new MenuView(mContext, stubMenuViewModel, stubMenuViewAppearance,
mSecureSettings));
mMenuView.setTranslationY(halfScreenHeight);
+ doNothing().when(mMenuView).gotoEditScreen();
mMenuViewLayer = spy(new MenuViewLayer(
mContext, stubWindowManager, mAccessibilityManager,
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 ce4db8febb6c..3862b0f5cf74 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
@@ -160,6 +160,7 @@ public class MenuViewLayerTest extends SysuiTestCase {
new MenuView(mSpyContext, mMenuViewModel, menuViewAppearance, mSecureSettings));
// Ensure tests don't actually update metrics.
doNothing().when(mMenuView).incrementTexMetric(any(), anyInt());
+ doNothing().when(mMenuView).gotoEditScreen();
mMenuViewLayer = spy(new MenuViewLayer(mSpyContext, mStubWindowManager,
mStubAccessibilityManager, mMenuViewModel, menuViewAppearance, mMenuView,
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 7c97f53d539d..1ce65258ef4d 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
@@ -21,6 +21,7 @@ import static android.app.UiModeManager.MODE_NIGHT_YES;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -80,6 +81,7 @@ public class MenuViewTest extends SysuiTestCase {
mUiModeManager.setNightMode(MODE_NIGHT_YES);
mSpyContext = spy(mContext);
+ doNothing().when(mSpyContext).startActivity(any());
final SecureSettings secureSettings = TestUtils.mockSecureSettings();
final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
secureSettings);
@@ -179,8 +181,6 @@ public class MenuViewTest extends SysuiTestCase {
@Test
@EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_EDIT)
public void gotoEditScreen_sendsIntent() {
- // Notably, this shouldn't crash the settings app,
- // because the button target args are configured.
mMenuView.gotoEditScreen();
verify(mSpyContext).startActivity(any());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index 0b0410aa1670..0d464cfd71f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -180,6 +180,17 @@ public class AppOpsControllerTest extends SysuiTestCase {
assertThat(mController.getActiveAppOps()).isEmpty();
}
+ /** Regression test for b/324329757 */
+ @Test
+ public void startListening_fetchCurrentActive_nullPackageOps() {
+ when(mAppOpsManager.getPackagesForOps(AppOpsControllerImpl.OPS)).thenReturn(null);
+
+ mController.setListening(true);
+ mBgExecutor.runAllReady();
+
+ assertThat(mController.getActiveAppOps()).isEmpty();
+ }
+
/** Regression test for b/294104969. */
@Test
public void startListening_fetchesCurrentActive_oneActive() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 43f7c60721ee..2c1a87d86be9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -20,6 +20,8 @@ import android.content.pm.PackageManager
import android.hardware.biometrics.BiometricAuthenticator
import android.hardware.biometrics.BiometricConstants
import android.hardware.biometrics.BiometricManager
+import android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT
+import android.hardware.biometrics.Flags.customBiometricPrompt
import android.hardware.biometrics.PromptInfo
import android.hardware.face.FaceSensorPropertiesInternal
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
@@ -38,11 +40,11 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.widget.LockPatternUtils
-import com.android.systemui.res.R
+import com.android.systemui.Flags.FLAG_CONSTRAINT_BP
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.data.repository.FakeDisplayStateRepository
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl
import com.android.systemui.biometrics.domain.interactor.FakeCredentialInteractor
@@ -53,6 +55,7 @@ import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel
import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel
import com.android.systemui.display.data.repository.FakeDisplayRepository
import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.res.R
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.events.ANIMATING_OUT
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
@@ -145,6 +148,8 @@ open class AuthContainerViewTest : SysuiTestCase() {
@Before
fun setup() {
+ mSetFlagsRule.disableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
+ mSetFlagsRule.disableFlags(FLAG_CONSTRAINT_BP)
displayRepository = FakeDisplayRepository()
displayStateInteractor =
@@ -394,6 +399,19 @@ open class AuthContainerViewTest : SysuiTestCase() {
}
@Test
+ fun testShowBiometricUIWhenCustomBpEnabledAndNoSensors() {
+ mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
+ val container = initializeFingerprintContainer(
+ authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
+ )
+ waitForIdleSync()
+
+ assertThat(customBiometricPrompt()).isTrue()
+ assertThat(container.hasBiometricPrompt()).isTrue()
+ assertThat(container.hasCredentialView()).isFalse()
+ }
+
+ @Test
fun testCredentialViewUsesEffectiveUserId() {
whenever(userManager.getCredentialOwnerProfile(anyInt())).thenReturn(200)
whenever(lockPatternUtils.getKeyguardStoredPasswordQuality(eq(200))).thenReturn(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt
index 5eda2b28175d..8690d4eb1f3e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt
@@ -89,6 +89,26 @@ class CredentialInteractorImplTest : SysuiTestCase() {
}
}
+ @Test
+ fun testParentProfile() {
+ for (value in listOf(12, 8, 4)) {
+ whenever(userManager.getProfileParent(eq(USER_ID)))
+ .thenReturn(UserInfo(value, "test", 0))
+
+ assertThat(interactor.getParentProfileIdOrSelfId(USER_ID)).isEqualTo(value)
+ }
+ }
+
+ @Test
+ fun useCredentialOwnerWhenParentProfileIsNull() {
+ val value = 1
+
+ whenever(userManager.getProfileParent(eq(USER_ID))).thenReturn(null)
+ whenever(userManager.getCredentialOwnerProfile(eq(USER_ID))).thenReturn(value)
+
+ assertThat(interactor.getParentProfileIdOrSelfId(USER_ID)).isEqualTo(value)
+ }
+
@Test fun pinCredentialWhenGood() = pinCredential(goodCredential())
@Test fun pinCredentialWhenBad() = pinCredential(badCredential())
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 ff68fe3df9e9..140849b8e257 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
@@ -241,19 +241,27 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
viewModel.showAuthenticated(testCase.authenticatedModality, 1_000L)
- val confirmConstant by collectLastValue(viewModel.hapticsToPlay)
- assertThat(confirmConstant)
+ val confirmHaptics by collectLastValue(viewModel.hapticsToPlay)
+ assertThat(confirmHaptics?.hapticFeedbackConstant)
.isEqualTo(
if (expectConfirmation) HapticFeedbackConstants.NO_HAPTICS
else HapticFeedbackConstants.CONFIRM
)
+ assertThat(confirmHaptics?.flag)
+ .isEqualTo(
+ if (expectConfirmation) null
+ else HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING
+ )
if (expectConfirmation) {
viewModel.confirmAuthenticated()
}
- val confirmedConstant by collectLastValue(viewModel.hapticsToPlay)
- assertThat(confirmedConstant).isEqualTo(HapticFeedbackConstants.CONFIRM)
+ val confirmedHaptics by collectLastValue(viewModel.hapticsToPlay)
+ assertThat(confirmedHaptics?.hapticFeedbackConstant)
+ .isEqualTo(HapticFeedbackConstants.CONFIRM)
+ assertThat(confirmedHaptics?.flag)
+ .isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
}
@Test
@@ -265,16 +273,21 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
viewModel.confirmAuthenticated()
}
- val currentConstant by collectLastValue(viewModel.hapticsToPlay)
- assertThat(currentConstant).isEqualTo(HapticFeedbackConstants.CONFIRM)
+ val currentHaptics by collectLastValue(viewModel.hapticsToPlay)
+ assertThat(currentHaptics?.hapticFeedbackConstant)
+ .isEqualTo(HapticFeedbackConstants.CONFIRM)
+ assertThat(currentHaptics?.flag)
+ .isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
}
@Test
fun playErrorHaptic_SetsRejectConstant() = runGenericTest {
viewModel.showTemporaryError("test", "messageAfterError", false)
- val currentConstant by collectLastValue(viewModel.hapticsToPlay)
- assertThat(currentConstant).isEqualTo(HapticFeedbackConstants.REJECT)
+ val currentHaptics by collectLastValue(viewModel.hapticsToPlay)
+ assertThat(currentHaptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.REJECT)
+ assertThat(currentHaptics?.flag)
+ .isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
}
@Test
@@ -800,8 +813,9 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
hapticFeedback = true,
)
- val constant by collectLastValue(viewModel.hapticsToPlay)
- assertThat(constant).isEqualTo(HapticFeedbackConstants.REJECT)
+ val haptics by collectLastValue(viewModel.hapticsToPlay)
+ assertThat(haptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.REJECT)
+ assertThat(haptics?.flag).isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
}
@Test
@@ -813,8 +827,8 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
hapticFeedback = false,
)
- val constant by collectLastValue(viewModel.hapticsToPlay)
- assertThat(constant).isEqualTo(HapticFeedbackConstants.NO_HAPTICS)
+ val haptics by collectLastValue(viewModel.hapticsToPlay)
+ assertThat(haptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.NO_HAPTICS)
}
private suspend fun TestScope.showTemporaryErrors(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
index f770a3885aad..6e00b70b5410 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
@@ -21,6 +21,8 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyDouble;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.testing.AndroidTestingRunner;
@@ -40,6 +42,8 @@ import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -71,6 +75,10 @@ public class BrightLineFalsingManagerTest extends SysuiTestCase {
private KeyguardStateController mKeyguardStateController;
@Mock
private AccessibilityManager mAccessibilityManager;
+ @Captor
+ private ArgumentCaptor<FalsingDataProvider.SessionListener> mSessionListenerArgumentCaptor;
+ @Captor
+ private ArgumentCaptor<HistoryTracker.BeliefListener> mBeliefListenerArgumentCaptor;
private final FalsingClassifier.Result mPassedResult = FalsingClassifier.Result.passed(1);
private final FalsingClassifier.Result mFalsedResult =
@@ -194,4 +202,28 @@ public class BrightLineFalsingManagerTest extends SysuiTestCase {
when(mFalsingDataProvider.isFromTrackpad()).thenReturn(true);
assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isFalse();
}
+
+ @Test
+ public void testAddAndRemoveFalsingBeliefListener() {
+ verify(mHistoryTracker, never()).addBeliefListener(any());
+
+ // Session started
+ final FalsingDataProvider.SessionListener sessionListener = captureSessionListener();
+ sessionListener.onSessionStarted();
+
+ // Verify belief listener added when session started
+ verify(mHistoryTracker).addBeliefListener(mBeliefListenerArgumentCaptor.capture());
+ verify(mHistoryTracker, never()).removeBeliefListener(any());
+
+ // Session ended
+ sessionListener.onSessionEnded();
+
+ // Verify belief listener removed when session ended
+ verify(mHistoryTracker).removeBeliefListener(mBeliefListenerArgumentCaptor.getValue());
+ }
+
+ private FalsingDataProvider.SessionListener captureSessionListener() {
+ verify(mFalsingDataProvider).addSessionListener(mSessionListenerArgumentCaptor.capture());
+ return mSessionListenerArgumentCaptor.getValue();
+ }
}
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 fcb18f52086d..3f13033217b3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
@@ -33,6 +33,7 @@ import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.communal.domain.interactor.CommunalInteractor;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerFake;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -85,6 +86,8 @@ public class FalsingCollectorImplTest extends SysuiTestCase {
private BatteryController mBatteryController;
@Mock
private SelectedUserInteractor mSelectedUserInteractor;
+ @Mock
+ private CommunalInteractor mCommunalInteractor;
private final DockManagerFake mDockManager = new DockManagerFake();
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private final FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock);
@@ -102,7 +105,8 @@ public class FalsingCollectorImplTest extends SysuiTestCase {
mStatusBarStateController, mKeyguardStateController,
() -> mShadeInteractor, mBatteryController,
mDockManager, mFakeExecutor,
- mJavaAdapter, mFakeSystemClock, () -> mSelectedUserInteractor
+ mJavaAdapter, mFakeSystemClock, () -> mSelectedUserInteractor,
+ () -> mCommunalInteractor
);
mFalsingCollector.init();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java
index 18515825967f..c65a1176a55b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java
@@ -27,16 +27,20 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
+import android.app.KeyguardManager;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.ClipboardManager;
import android.os.PersistableBundle;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.provider.Settings;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
@@ -59,6 +63,8 @@ public class ClipboardListenerTest extends SysuiTestCase {
@Mock
private ClipboardManager mClipboardManager;
@Mock
+ private KeyguardManager mKeyguardManager;
+ @Mock
private ClipboardOverlayController mOverlayController;
@Mock
private ClipboardToast mClipboardToast;
@@ -96,7 +102,7 @@ public class ClipboardListenerTest extends SysuiTestCase {
when(mClipboardManager.getPrimaryClipSource()).thenReturn(mSampleSource);
mClipboardListener = new ClipboardListener(getContext(), mOverlayControllerProvider,
- mClipboardToast, mClipboardManager, mUiEventLogger);
+ mClipboardToast, mClipboardManager, mKeyguardManager, mUiEventLogger);
}
@@ -191,6 +197,34 @@ public class ClipboardListenerTest extends SysuiTestCase {
}
@Test
+ @EnableFlags(Flags.FLAG_CLIPBOARD_NONINTERACTIVE_ON_LOCKSCREEN)
+ public void test_deviceLocked_showsToast() {
+ when(mKeyguardManager.isDeviceLocked()).thenReturn(true);
+
+ mClipboardListener.start();
+ mClipboardListener.onPrimaryClipChanged();
+
+ verify(mUiEventLogger, times(1)).log(
+ ClipboardOverlayEvent.CLIPBOARD_TOAST_SHOWN, 0, mSampleSource);
+ verify(mClipboardToast, times(1)).showCopiedToast();
+ verifyZeroInteractions(mOverlayControllerProvider);
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_CLIPBOARD_NONINTERACTIVE_ON_LOCKSCREEN)
+ public void test_deviceLocked_legacyBehavior_showsInteractiveUI() {
+ when(mKeyguardManager.isDeviceLocked()).thenReturn(true);
+
+ mClipboardListener.start();
+ mClipboardListener.onPrimaryClipChanged();
+
+ verify(mUiEventLogger, times(1)).log(
+ ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ENTERED, 0, mSampleSource);
+ verify(mOverlayController).setClipData(mSampleClipData, mSampleSource);
+ verifyZeroInteractions(mClipboardToast);
+ }
+
+ @Test
public void test_nullClipData_showsNothing() {
when(mClipboardManager.getPrimaryClip()).thenReturn(null);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamMediaEntryComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamMediaEntryComplicationTest.java
index 2bf9ab264bcf..05b4a41d1b40 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamMediaEntryComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamMediaEntryComplicationTest.java
@@ -37,7 +37,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.complication.dagger.DreamMediaEntryComplicationComponent;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.media.controls.ui.MediaCarouselController;
+import com.android.systemui.media.controls.ui.controller.MediaCarouselController;
import com.android.systemui.media.dream.MediaDreamComplication;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractorTest.kt
index a53f8d43f323..5581f0c0314f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractorTest.kt
@@ -23,6 +23,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
+import com.android.systemui.biometrics.domain.faceHelpMessageDeferral
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.biometrics.shared.model.SensorStrength
import com.android.systemui.coroutines.collectLastValue
@@ -39,11 +40,13 @@ import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationS
import com.android.systemui.keyguard.shared.model.HelpFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -57,6 +60,7 @@ class BiometricMessageInteractorTest : SysuiTestCase() {
private val fingerprintAuthRepository = kosmos.deviceEntryFingerprintAuthRepository
private val faceAuthRepository = kosmos.fakeDeviceEntryFaceAuthRepository
private val biometricSettingsRepository = kosmos.biometricSettingsRepository
+ private val faceHelpMessageDeferral = kosmos.faceHelpMessageDeferral
@Test
fun fingerprintErrorMessage() =
@@ -266,11 +270,38 @@ class BiometricMessageInteractorTest : SysuiTestCase() {
)
)
- // THEN fingerprintFailedMessage is updated
+ // THEN fingerprintHelpMessage is updated
assertThat(faceHelpMessage).isNotNull()
}
@Test
+ fun faceHelpMessageShouldDefer() =
+ testScope.runTest {
+ val faceHelpMessage by collectLastValue(underTest.faceMessage)
+
+ // GIVEN face is allowed
+ biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(true)
+
+ // GIVEN face only enrolled
+ biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true)
+ biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
+
+ // WHEN all face help messages should be deferred
+ whenever(faceHelpMessageDeferral.shouldDefer(anyInt())).thenReturn(true)
+
+ // WHEN authentication status help
+ faceAuthRepository.setAuthenticationStatus(
+ HelpFaceAuthenticationStatus(
+ msg = "Move left",
+ msgId = FaceManager.FACE_ACQUIRED_TOO_RIGHT,
+ )
+ )
+
+ // THEN fingerprintHelpMessage is NOT updated
+ assertThat(faceHelpMessage).isNull()
+ }
+
+ @Test
fun faceHelpMessage_coEx() =
testScope.runTest {
val faceHelpMessage by collectLastValue(underTest.faceMessage)
@@ -291,7 +322,7 @@ class BiometricMessageInteractorTest : SysuiTestCase() {
)
)
- // THEN fingerprintFailedMessage is NOT updated
+ // THEN fingerprintHelpMessage is NOT updated
assertThat(faceHelpMessage).isNull()
}
@@ -337,7 +368,7 @@ class BiometricMessageInteractorTest : SysuiTestCase() {
testScope.runTest {
val faceErrorMessage by collectLastValue(underTest.faceMessage)
- // WHEN authentication status error is FACE_ERROR_HW_UNAVAILABLE
+ // WHEN authentication status error is FACE_ERROR_TIMEOUT
faceAuthRepository.setAuthenticationStatus(
ErrorFaceAuthenticationStatus(msgId = FaceManager.FACE_ERROR_TIMEOUT, msg = "test")
)
@@ -349,4 +380,23 @@ class BiometricMessageInteractorTest : SysuiTestCase() {
assertThat(faceErrorMessage).isInstanceOf(FaceTimeoutMessage::class.java)
assertThat(faceErrorMessage?.message).isEqualTo("test")
}
+
+ @Test
+ fun faceTimeoutDeferredErrorMessage() =
+ testScope.runTest {
+ whenever(faceHelpMessageDeferral.getDeferredMessage()).thenReturn("deferredMessage")
+ val faceErrorMessage by collectLastValue(underTest.faceMessage)
+
+ // WHEN authentication status error is FACE_ERROR_TIMEOUT
+ faceAuthRepository.setAuthenticationStatus(
+ ErrorFaceAuthenticationStatus(msgId = FaceManager.FACE_ERROR_TIMEOUT, msg = "test")
+ )
+
+ // GIVEN face is allowed
+ biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(true)
+
+ // THEN faceErrorMessage is updated to deferred message instead of timeout message
+ assertThat(faceErrorMessage).isNotInstanceOf(FaceTimeoutMessage::class.java)
+ assertThat(faceErrorMessage?.message).isEqualTo("deferredMessage")
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagDependenciesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagDependenciesTest.kt
index 936aa8bb819c..91da88e480d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagDependenciesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagDependenciesTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.flags
import android.testing.AndroidTestingRunner
+import android.util.Log
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import java.io.PrintWriter
@@ -41,14 +42,21 @@ class FlagDependenciesTest : SysuiTestCase() {
FlagDependencies(TestFeatureFlags(teamfood = teamfood), TestHandler())
private class TestHandler : FlagDependenciesBase.Handler {
+ override val enableDependencies: Boolean
+ get() = true
override fun warnAboutBadFlagConfiguration(
all: List<FlagDependenciesBase.Dependency>,
unmet: List<FlagDependenciesBase.Dependency>
) {
- val title = "${unmet.size} invalid of ${all.size} flag dependencies"
+ val title = "Invalid flag dependencies: ${unmet.size}"
val details = unmet.joinToString("\n")
fail("$title:\n$details")
}
+
+ override fun onCollected(all: List<FlagDependenciesBase.Dependency>) {
+ Log.d("FlagDependencies", "All: ${all.size}")
+ all.forEach { Log.d("FlagDependencies", " $it") }
+ }
}
private class TestFeatureFlags(val teamfood: Boolean) : FeatureFlagsClassic {
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 3f454925186c..77b30402c040 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -65,6 +65,8 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.globalactions.domain.interactor.GlobalActionsInteractor;
+import com.android.systemui.kosmos.KosmosJavaAdapter;
import com.android.systemui.plugins.GlobalActions;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.settings.UserTracker;
@@ -140,6 +142,8 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
@Captor private ArgumentCaptor<OnBackInvokedCallback> mOnBackInvokedCallback;
private TestableLooper mTestableLooper;
+ private KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
+ private GlobalActionsInteractor mInteractor;
@Before
public void setUp() throws Exception {
@@ -154,6 +158,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
mGlobalSettings = new FakeGlobalSettings();
mSecureSettings = new FakeSettings();
+ mInteractor = mKosmos.getGlobalActionsInteractor();
mGlobalActionsDialogLite = new GlobalActionsDialogLite(mContext,
mWindowManagerFuncs,
@@ -189,7 +194,8 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
mShadeController,
mKeyguardUpdateMonitor,
mDialogTransitionAnimator,
- mSelectedUserInteractor);
+ mSelectedUserInteractor,
+ mInteractor);
mGlobalActionsDialogLite.setZeroDialogPressDelayForTesting();
ColorExtractor.GradientColors backdropColors = new ColorExtractor.GradientColors();
@@ -623,6 +629,18 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase {
assertThat(bugReportAction.showBeforeProvisioning()).isFalse();
}
+ @Test
+ public void testInteractor_onShow() {
+ mGlobalActionsDialogLite.onShow(null);
+ assertThat(mInteractor.isVisible().getValue()).isTrue();
+ }
+
+ @Test
+ public void testInteractor_onDismiss() {
+ mGlobalActionsDialogLite.onDismiss(mGlobalActionsDialogLite.mDialog);
+ assertThat(mInteractor.isVisible().getValue()).isFalse();
+ }
+
private UserInfo mockCurrentUser(int flags) {
return new UserInfo(10, "A User", flags);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/data/repository/GlobalActionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/globalactions/data/repository/GlobalActionsRepositoryTest.kt
new file mode 100644
index 000000000000..e437c10c7b73
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/data/repository/GlobalActionsRepositoryTest.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2024 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.globalactions.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class GlobalActionsRepositoryTest : SysuiTestCase() {
+ private lateinit var underTest: GlobalActionsRepository
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+
+ @Before
+ fun setUp() {
+ underTest = kosmos.globalActionsRepository
+ }
+
+ @Test
+ fun isVisible_initialValueFalse() {
+ testScope.runTest {
+ val isVisible by collectLastValue(underTest.isVisible)
+ runCurrent()
+
+ assertThat(isVisible).isFalse()
+ }
+ }
+
+ @Test
+ fun isVisible_onChange() {
+ testScope.runTest {
+ val isVisible by collectLastValue(underTest.isVisible)
+ runCurrent()
+
+ underTest.setVisible(true)
+ assertThat(isVisible).isTrue()
+
+ underTest.setVisible(false)
+ assertThat(isVisible).isFalse()
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/domain/interactor/GlobalActionsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/globalactions/domain/interactor/GlobalActionsInteractorTest.kt
new file mode 100644
index 000000000000..9275512009b2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/domain/interactor/GlobalActionsInteractorTest.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2024 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.globalactions.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class GlobalActionsInteractorTest : SysuiTestCase() {
+ private lateinit var underTest: GlobalActionsInteractor
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+
+ @Before
+ fun setup() {
+ underTest = kosmos.globalActionsInteractor
+ }
+
+ @Test
+ fun OnDismissed() {
+ testScope.runTest {
+ val isVisible by collectLastValue(underTest.isVisible)
+ underTest.onDismissed()
+ runCurrent()
+
+ assertThat(isVisible).isFalse()
+ }
+ }
+
+ @Test
+ fun OnShown() {
+ testScope.runTest {
+ val isVisible by collectLastValue(underTest.isVisible)
+ underTest.onShown()
+ runCurrent()
+
+ assertThat(isVisible).isTrue()
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index 1a6da7608849..915522de62d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -53,11 +53,9 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
-import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.testKosmos
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
@@ -103,8 +101,6 @@ class CustomizationProviderTest : SysuiTestCase() {
private lateinit var underTest: CustomizationProvider
private lateinit var testScope: TestScope
- private val kosmos = testKosmos()
-
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -189,7 +185,6 @@ class CustomizationProviderTest : SysuiTestCase() {
},
)
.keyguardInteractor,
- shadeInteractor = kosmos.shadeInteractor,
lockPatternUtils = lockPatternUtils,
keyguardStateController = keyguardStateController,
userTracker = userTracker,
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 2732047b4eba..0957748c9938 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -21,6 +21,7 @@ import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
import static android.view.WindowManagerPolicyConstants.OFF_BECAUSE_OF_TIMEOUT;
import static android.view.WindowManagerPolicyConstants.OFF_BECAUSE_OF_USER;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
@@ -650,6 +651,25 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
}
@Test
+ public void testBouncerPrompt_deviceLockedByAdaptiveAuth() {
+ // GIVEN no trust agents enabled and biometrics aren't enrolled
+ when(mUpdateMonitor.isTrustUsuallyManaged(anyInt())).thenReturn(false);
+ when(mUpdateMonitor.isUnlockingWithBiometricsPossible(anyInt())).thenReturn(false);
+
+ // WHEN the strong auth reason is SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST
+ KeyguardUpdateMonitor.StrongAuthTracker strongAuthTracker =
+ mock(KeyguardUpdateMonitor.StrongAuthTracker.class);
+ when(mUpdateMonitor.getStrongAuthTracker()).thenReturn(strongAuthTracker);
+ when(strongAuthTracker.hasUserAuthenticatedSinceBoot()).thenReturn(true);
+ when(strongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn(
+ SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST);
+
+ // THEN the bouncer prompt reason should return PROMPT_REASON_ADAPTIVE_AUTH_REQUEST
+ assertEquals(KeyguardSecurityView.PROMPT_REASON_ADAPTIVE_AUTH_REQUEST,
+ mViewMediator.mViewMediatorCallback.getBouncerPromptReason());
+ }
+
+ @Test
public void testBouncerPrompt_deviceRestartedDueToMainlineUpdate() {
// GIVEN biometrics enrolled
when(mUpdateMonitor.isUnlockingWithBiometricsPossible(anyInt())).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
index cf8fe79f70f0..2b51863117e9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
@@ -2,6 +2,9 @@ package com.android.systemui.keyguard
import android.content.ComponentCallbacks2
import android.graphics.HardwareRenderer
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -23,6 +26,7 @@ import kotlinx.coroutines.test.UnconfinedTestDispatcher
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.mockito.Mock
@@ -48,10 +52,11 @@ class ResourceTrimmerTest : SysuiTestCase() {
@Mock private lateinit var globalWindowManager: GlobalWindowManager
private lateinit var resourceTrimmer: ResourceTrimmer
+ @Rule @JvmField public val setFlagsRule = SetFlagsRule()
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- featureFlags.set(Flags.TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK, true)
featureFlags.set(Flags.TRIM_FONT_CACHES_AT_UNLOCK, true)
keyguardRepository.setDozeAmount(0f)
keyguardRepository.setKeyguardGoingAway(false)
@@ -76,6 +81,7 @@ class ResourceTrimmerTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
fun noChange_noOutputChanges() =
testScope.runTest {
testScope.runCurrent()
@@ -83,6 +89,7 @@ class ResourceTrimmerTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
fun dozeAodDisabled_sleep_trimsMemory() =
testScope.runTest {
powerInteractor.setAsleepForTest()
@@ -93,6 +100,27 @@ class ResourceTrimmerTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
+ fun dozeAodDisabled_flagDisabled_sleep_doesntTrimMemory() =
+ testScope.runTest {
+ powerInteractor.setAsleepForTest()
+ testScope.runCurrent()
+ verifyZeroInteractions(globalWindowManager)
+ }
+
+ @Test
+ @DisableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
+ fun dozeEnabled_flagDisabled_sleepWithFullDozeAmount_doesntTrimMemory() =
+ testScope.runTest {
+ keyguardRepository.setDreaming(true)
+ keyguardRepository.setDozeAmount(1f)
+ powerInteractor.setAsleepForTest()
+ testScope.runCurrent()
+ verifyZeroInteractions(globalWindowManager)
+ }
+
+ @Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
fun dozeEnabled_sleepWithFullDozeAmount_trimsMemory() =
testScope.runTest {
keyguardRepository.setDreaming(true)
@@ -105,6 +133,7 @@ class ResourceTrimmerTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
fun dozeEnabled_sleepWithoutFullDozeAmount_doesntTrimMemory() =
testScope.runTest {
keyguardRepository.setDreaming(true)
@@ -115,6 +144,7 @@ class ResourceTrimmerTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
fun aodEnabled_sleepWithFullDozeAmount_trimsMemoryOnce() {
testScope.runTest {
keyguardRepository.setDreaming(true)
@@ -141,6 +171,7 @@ class ResourceTrimmerTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
fun aodEnabled_deviceWakesHalfWayThrough_doesNotTrimMemory() {
testScope.runTest {
keyguardRepository.setDreaming(true)
@@ -172,6 +203,7 @@ class ResourceTrimmerTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
fun keyguardTransitionsToGone_trimsFontCache() =
testScope.runTest {
keyguardTransitionRepository.sendTransitionSteps(
@@ -186,6 +218,7 @@ class ResourceTrimmerTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
fun keyguardTransitionsToGone_flagDisabled_doesNotTrimFontCache() =
testScope.runTest {
featureFlags.set(Flags.TRIM_FONT_CACHES_AT_UNLOCK, false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
index 9df00d3682a4..b0d8de359cd7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
@@ -19,17 +19,23 @@ package com.android.systemui.keyguard.domain.interactor
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.KeyguardBlueprintRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor.Companion.SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID
+import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor.Companion.WEATHER_CLOCK_BLUEPRINT_ID
import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint
import com.android.systemui.keyguard.ui.view.layout.blueprints.SplitShadeKeyguardBlueprint
import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition
import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Config
import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Type
+import com.android.systemui.plugins.clocks.ClockConfig
+import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.statusbar.policy.SplitShadeStateController
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
@@ -38,6 +44,7 @@ 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.reset
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -54,6 +61,8 @@ class KeyguardBlueprintInteractorTest : SysuiTestCase() {
@Mock private lateinit var splitShadeStateController: SplitShadeStateController
@Mock private lateinit var keyguardBlueprintRepository: KeyguardBlueprintRepository
+ @Mock private lateinit var clockInteractor: KeyguardClockInteractor
+ @Mock private lateinit var clockController: ClockController
@Before
fun setup() {
@@ -61,6 +70,8 @@ class KeyguardBlueprintInteractorTest : SysuiTestCase() {
testScope = TestScope(StandardTestDispatcher())
whenever(keyguardBlueprintRepository.configurationChange).thenReturn(configurationFlow)
whenever(keyguardBlueprintRepository.refreshTransition).thenReturn(refreshTransition)
+ whenever(clockInteractor.currentClock).thenReturn(MutableStateFlow(clockController))
+ clockInteractor.currentClock
underTest =
KeyguardBlueprintInteractor(
@@ -68,6 +79,7 @@ class KeyguardBlueprintInteractorTest : SysuiTestCase() {
testScope.backgroundScope,
mContext,
splitShadeStateController,
+ clockInteractor,
)
}
@@ -102,6 +114,77 @@ class KeyguardBlueprintInteractorTest : SysuiTestCase() {
}
@Test
+ fun composeLockscreenOff_DoesAppliesSplitShadeWeatherClockBlueprint() {
+ testScope.runTest {
+ mSetFlagsRule.disableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
+ whenever(clockController.config)
+ .thenReturn(
+ ClockConfig(
+ id = "DIGITAL_CLOCK_WEATHER",
+ name = "clock",
+ description = "clock",
+ )
+ )
+ whenever(splitShadeStateController.shouldUseSplitNotificationShade(any()))
+ .thenReturn(true)
+
+ reset(keyguardBlueprintRepository)
+ configurationFlow.tryEmit(Unit)
+ runCurrent()
+
+ verify(keyguardBlueprintRepository, never())
+ .applyBlueprint(SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID)
+ }
+ }
+
+ @Test
+ fun testDoesAppliesSplitShadeWeatherClockBlueprint() {
+ testScope.runTest {
+ mSetFlagsRule.enableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
+ whenever(clockController.config)
+ .thenReturn(
+ ClockConfig(
+ id = "DIGITAL_CLOCK_WEATHER",
+ name = "clock",
+ description = "clock",
+ )
+ )
+ whenever(splitShadeStateController.shouldUseSplitNotificationShade(any()))
+ .thenReturn(true)
+
+ reset(keyguardBlueprintRepository)
+ configurationFlow.tryEmit(Unit)
+ runCurrent()
+
+ verify(keyguardBlueprintRepository)
+ .applyBlueprint(SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID)
+ }
+ }
+
+ @Test
+ fun testAppliesWeatherClockBlueprint() {
+ testScope.runTest {
+ mSetFlagsRule.enableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
+ whenever(clockController.config)
+ .thenReturn(
+ ClockConfig(
+ id = "DIGITAL_CLOCK_WEATHER",
+ name = "clock",
+ description = "clock",
+ )
+ )
+ whenever(splitShadeStateController.shouldUseSplitNotificationShade(any()))
+ .thenReturn(false)
+
+ reset(keyguardBlueprintRepository)
+ configurationFlow.tryEmit(Unit)
+ runCurrent()
+
+ verify(keyguardBlueprintRepository).applyBlueprint(WEATHER_CLOCK_BLUEPRINT_ID)
+ }
+ }
+
+ @Test
fun testRefreshBlueprint() {
underTest.refreshBlueprint()
verify(keyguardBlueprintRepository).refreshBlueprint()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index 45b2a4266ff6..d2a8444e40ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -46,9 +46,7 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
-import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.testKosmos
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
@@ -244,8 +242,6 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
private lateinit var userTracker: UserTracker
- private val kosmos = testKosmos()
-
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -315,7 +311,6 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
featureFlags = featureFlags,
)
.keyguardInteractor,
- shadeInteractor = kosmos.shadeInteractor,
lockPatternUtils = lockPatternUtils,
keyguardStateController = keyguardStateController,
userTracker = userTracker,
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 5b93df5a0bad..69cd173f4253 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
@@ -57,7 +57,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceTimeBy
-import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -130,7 +129,6 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
val glanceableHubTransitions =
GlanceableHubTransitions(
- scope = testScope,
bgDispatcher = kosmos.testDispatcher,
transitionInteractor = transitionInteractor,
transitionRepository = transitionRepository,
@@ -148,6 +146,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
keyguardInteractor = keyguardInteractor,
transitionRepository = transitionRepository,
transitionInteractor = transitionInteractor,
+ glanceableHubTransitions = glanceableHubTransitions,
)
.apply { start() }
@@ -1097,6 +1096,41 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
}
@Test
+ fun primaryBouncerToGlanceableHubWhileDreaming() =
+ testScope.runTest {
+ // GIVEN a prior transition has run to PRIMARY_BOUNCER
+ bouncerRepository.setPrimaryShow(true)
+ runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER)
+
+ // GIVEN that we are dreaming and occluded
+ keyguardRepository.setDreaming(true)
+ keyguardRepository.setKeyguardOccluded(true)
+
+ // GIVEN the device is idle on the glanceable hub
+ val idleTransitionState =
+ MutableStateFlow<ObservableCommunalTransitionState>(
+ ObservableCommunalTransitionState.Idle(CommunalSceneKey.Communal)
+ )
+ communalInteractor.setTransitionState(idleTransitionState)
+ runCurrent()
+
+ // WHEN the primaryBouncer stops showing
+ bouncerRepository.setPrimaryShow(false)
+ runCurrent()
+
+ // THEN a transition to LOCKSCREEN should occur
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = FromPrimaryBouncerTransitionInteractor::class.simpleName,
+ from = KeyguardState.PRIMARY_BOUNCER,
+ to = KeyguardState.GLANCEABLE_HUB,
+ animatorAssertion = { it.isNotNull() },
+ )
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
fun primaryBouncerToDreamingLockscreenHosted() =
testScope.runTest {
// GIVEN device dreaming with the lockscreen hosted dream and not dozing
@@ -1372,6 +1406,44 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
}
@Test
+ fun dreamingToGlanceableHub() =
+ testScope.runTest {
+ // GIVEN a prior transition has run to DREAMING
+ keyguardRepository.setDreaming(true)
+ runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.DREAMING)
+ runCurrent()
+
+ // WHEN a transition to the glanceable hub starts
+ val currentScene = CommunalSceneKey.Blank
+ val targetScene = CommunalSceneKey.Communal
+
+ val progress = MutableStateFlow(0f)
+ val transitionState =
+ MutableStateFlow<ObservableCommunalTransitionState>(
+ ObservableCommunalTransitionState.Transition(
+ fromScene = currentScene,
+ toScene = targetScene,
+ progress = progress,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ communalInteractor.setTransitionState(transitionState)
+ progress.value = .1f
+ runCurrent()
+
+ assertThat(transitionRepository)
+ .startedTransition(
+ ownerName = FromDreamingTransitionInteractor::class.simpleName,
+ from = KeyguardState.DREAMING,
+ to = KeyguardState.GLANCEABLE_HUB,
+ animatorAssertion = { it.isNull() }, // transition should be manually animated
+ )
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
fun lockscreenToOccluded() =
testScope.runTest {
// GIVEN a prior transition has run to LOCKSCREEN
@@ -1739,26 +1811,40 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
@Test
fun glanceableHubToDreaming() =
testScope.runTest {
- // GIVEN a device that is not dreaming or dozing
- keyguardRepository.setDreamingWithOverlay(false)
+ // GIVEN that we are dreaming and not dozing
+ keyguardRepository.setDreaming(true)
keyguardRepository.setDozeTransitionModel(
DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
)
runCurrent()
// GIVEN a prior transition has run to GLANCEABLE_HUB
- runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.GLANCEABLE_HUB)
+ runTransitionAndSetWakefulness(KeyguardState.DREAMING, KeyguardState.GLANCEABLE_HUB)
+ runCurrent()
- // WHEN the device begins to dream
- keyguardRepository.setDreamingWithOverlay(true)
- advanceTimeBy(100L)
+ // WHEN a transition away from glanceable hub starts
+ val currentScene = CommunalSceneKey.Communal
+ val targetScene = CommunalSceneKey.Blank
+
+ val transitionState =
+ MutableStateFlow<ObservableCommunalTransitionState>(
+ ObservableCommunalTransitionState.Transition(
+ fromScene = currentScene,
+ toScene = targetScene,
+ progress = flowOf(0f, 0.1f),
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
+ )
+ )
+ communalInteractor.setTransitionState(transitionState)
+ runCurrent()
assertThat(transitionRepository)
.startedTransition(
ownerName = FromGlanceableHubTransitionInteractor::class.simpleName,
from = KeyguardState.GLANCEABLE_HUB,
to = KeyguardState.DREAMING,
- animatorAssertion = { it.isNotNull() },
+ animatorAssertion = { it.isNull() }, // transition should be manually animated
)
coroutineContext.cancelChildren()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt
index 0e9197ef8ac1..f0607f4b70e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt
@@ -22,7 +22,8 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
@@ -51,8 +52,8 @@ class KeyguardTransitionAnimationFlowTest : SysuiTestCase() {
underTest =
animationFlow.setup(
duration = 1000.milliseconds,
- from = KeyguardState.GONE,
- to = KeyguardState.DREAMING,
+ from = GONE,
+ to = DREAMING,
)
}
@@ -192,17 +193,65 @@ class KeyguardTransitionAnimationFlowTest : SysuiTestCase() {
runCurrent()
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
- assertThat(animationValues).isEqualTo(StateToValue(TransitionState.STARTED, 0f))
+ assertThat(animationValues)
+ .isEqualTo(
+ StateToValue(
+ from = GONE,
+ to = DREAMING,
+ transitionState = TransitionState.STARTED,
+ value = 0f
+ )
+ )
repository.sendTransitionStep(step(0.3f, TransitionState.RUNNING))
- assertThat(animationValues).isEqualTo(StateToValue(TransitionState.RUNNING, 0.6f))
+ assertThat(animationValues)
+ .isEqualTo(
+ StateToValue(
+ from = GONE,
+ to = DREAMING,
+ transitionState = TransitionState.RUNNING,
+ value = 0.6f
+ )
+ )
repository.sendTransitionStep(step(0.6f, TransitionState.RUNNING))
- assertThat(animationValues).isEqualTo(StateToValue(TransitionState.RUNNING, 1.2f))
+ assertThat(animationValues)
+ .isEqualTo(
+ StateToValue(
+ from = GONE,
+ to = DREAMING,
+ transitionState = TransitionState.RUNNING,
+ value = 1.2f
+ )
+ )
repository.sendTransitionStep(step(0.8f, TransitionState.RUNNING))
- assertThat(animationValues).isEqualTo(StateToValue(TransitionState.RUNNING, 1.6f))
+ assertThat(animationValues)
+ .isEqualTo(
+ StateToValue(
+ from = GONE,
+ to = DREAMING,
+ transitionState = TransitionState.RUNNING,
+ value = 1.6f
+ )
+ )
repository.sendTransitionStep(step(1f, TransitionState.RUNNING))
- assertThat(animationValues).isEqualTo(StateToValue(TransitionState.RUNNING, 2f))
+ assertThat(animationValues)
+ .isEqualTo(
+ StateToValue(
+ from = GONE,
+ to = DREAMING,
+ transitionState = TransitionState.RUNNING,
+ value = 2f
+ )
+ )
repository.sendTransitionStep(step(1f, TransitionState.FINISHED))
- assertThat(animationValues).isEqualTo(StateToValue(TransitionState.FINISHED, null))
+ assertThat(animationValues)
+ .isEqualTo(
+ StateToValue(
+ from = GONE,
+ to = DREAMING,
+ transitionState = TransitionState.FINISHED,
+ value = null
+ )
+ )
}
@Test
@@ -251,8 +300,8 @@ class KeyguardTransitionAnimationFlowTest : SysuiTestCase() {
state: TransitionState = TransitionState.RUNNING
): TransitionStep {
return TransitionStep(
- from = KeyguardState.GONE,
- to = KeyguardState.DREAMING,
+ from = GONE,
+ to = DREAMING,
value = value,
transitionState = state,
ownerName = "GoneToDreamingTransitionViewModelTest"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
index ad86ee9f07d2..9c7f254a7b85 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
@@ -38,6 +38,7 @@ import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSec
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultUdfpsAccessibilityOverlaySection
+import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSliceViewSection
import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection
import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines
import com.android.systemui.util.mockito.whenever
@@ -71,6 +72,7 @@ class DefaultKeyguardBlueprintTest : SysuiTestCase() {
@Mock private lateinit var communalTutorialIndicatorSection: CommunalTutorialIndicatorSection
@Mock private lateinit var clockSection: ClockSection
@Mock private lateinit var smartspaceSection: SmartspaceSection
+ @Mock private lateinit var keyguardSliceViewSection: KeyguardSliceViewSection
@Mock
private lateinit var udfpsAccessibilityOverlaySection: DefaultUdfpsAccessibilityOverlaySection
@Before
@@ -92,6 +94,7 @@ class DefaultKeyguardBlueprintTest : SysuiTestCase() {
communalTutorialIndicatorSection,
clockSection,
smartspaceSection,
+ keyguardSliceViewSection,
udfpsAccessibilityOverlaySection,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
index 08d44c1855e2..aa7f9a99c758 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
@@ -67,12 +67,15 @@ class ClockSectionTest : SysuiTestCase() {
context.resources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) +
Utils.getStatusBarHeaderHeightKeyguard(context)
- private val LARGE_CLOCK_TOP =
+ private val LARGE_CLOCK_TOP_WITHOUT_SMARTSPACE =
context.resources.getDimensionPixelSize(R.dimen.status_bar_height) +
context.resources.getDimensionPixelSize(
com.android.systemui.customization.R.dimen.small_clock_padding_top
) +
- context.resources.getDimensionPixelSize(R.dimen.keyguard_smartspace_top_offset) +
+ context.resources.getDimensionPixelSize(R.dimen.keyguard_smartspace_top_offset)
+
+ private val LARGE_CLOCK_TOP =
+ LARGE_CLOCK_TOP_WITHOUT_SMARTSPACE +
SMART_SPACE_DATE_WEATHER_HEIGHT +
ENHANCED_SMART_SPACE_HEIGHT
@@ -81,36 +84,30 @@ class ClockSectionTest : SysuiTestCase() {
com.android.systemui.customization.R.dimen.small_clock_height
)
+ private var DIMENSION_BY_IDENTIFIER_NAME: List<Pair<String, Int>> = listOf()
+
@Before
fun setup() {
+ DIMENSION_BY_IDENTIFIER_NAME =
+ listOf(
+ "date_weather_view_height" to SMART_SPACE_DATE_WEATHER_HEIGHT,
+ "enhanced_smartspace_height" to ENHANCED_SMART_SPACE_HEIGHT,
+ )
+
MockitoAnnotations.initMocks(this)
val remoteResources = mock<Resources>()
- whenever(
- remoteResources.getIdentifier(
- anyString(),
- eq("dimen"),
- anyString(),
- )
- )
- .then { invocation ->
- val name = invocation.arguments[0] as String
- val index = DIMENSION_BY_IDENTIFIER_NAME.indexOfFirst { (key, _) -> key == name }
- if (index == -1) {
- error(
- "No entry for a dimension named \"$name\", please add it to the list above."
- )
- }
- index
- }
- whenever(
- remoteResources.getDimensionPixelSize(
- anyInt(),
- )
- )
- .then { invocation ->
- val id = invocation.arguments[0] as Int
- DIMENSION_BY_IDENTIFIER_NAME[id].second
- }
+ whenever(remoteResources.getIdentifier(anyString(), eq("dimen"), anyString())).then {
+ invocation ->
+ val name = invocation.arguments[0] as String
+ val index = DIMENSION_BY_IDENTIFIER_NAME.indexOfFirst { (key, _) -> key == name }
+ // increment index so that the not-found sentinel value lines up w/ what is
+ // returned by getIdentifier when a resource is not found
+ index + 1
+ }
+ whenever(remoteResources.getDimensionPixelSize(anyInt())).then { invocation ->
+ val id = invocation.arguments[0] as Int
+ DIMENSION_BY_IDENTIFIER_NAME[id - 1].second
+ }
val packageManager = mock<PackageManager>()
whenever(packageManager.getResourcesForApplication(anyString())).thenReturn(remoteResources)
mContext.setMockPackageManager(packageManager)
@@ -159,6 +156,36 @@ class ClockSectionTest : SysuiTestCase() {
}
@Test
+ fun testApplyDefaultConstraints_LargeClock_MissingSmartspace_SplitShade() {
+ DIMENSION_BY_IDENTIFIER_NAME = listOf() // Remove Smartspace from mock
+ setLargeClock(true)
+ setSplitShade(true)
+ val cs = ConstraintSet()
+ underTest.applyDefaultConstraints(cs)
+
+ val expectedLargeClockTopMargin = LARGE_CLOCK_TOP_WITHOUT_SMARTSPACE
+ assertLargeClockTop(cs, expectedLargeClockTopMargin)
+
+ val expectedSmallClockTopMargin = SMALL_CLOCK_TOP_SPLIT_SHADE
+ assertSmallClockTop(cs, expectedSmallClockTopMargin)
+ }
+
+ @Test
+ fun testApplyDefaultConstraints_LargeClock_MissingSmartspace_NonSplitShade() {
+ DIMENSION_BY_IDENTIFIER_NAME = listOf() // Remove Smartspace from mock
+ setLargeClock(true)
+ setSplitShade(false)
+ val cs = ConstraintSet()
+ underTest.applyDefaultConstraints(cs)
+
+ val expectedLargeClockTopMargin = LARGE_CLOCK_TOP_WITHOUT_SMARTSPACE
+ assertLargeClockTop(cs, expectedLargeClockTopMargin)
+
+ val expectedSmallClockTopMargin = SMALL_CLOCK_TOP_NON_SPLIT_SHADE
+ assertSmallClockTop(cs, expectedSmallClockTopMargin)
+ }
+
+ @Test
fun testApplyDefaultConstraints_SmallClock_SplitShade() {
setLargeClock(false)
setSplitShade(true)
@@ -249,10 +276,5 @@ class ClockSectionTest : SysuiTestCase() {
companion object {
private val SMART_SPACE_DATE_WEATHER_HEIGHT = 10
private val ENHANCED_SMART_SPACE_HEIGHT = 11
- private val DIMENSION_BY_IDENTIFIER_NAME =
- listOf(
- "date_weather_view_height" to SMART_SPACE_DATE_WEATHER_HEIGHT,
- "enhanced_smartspace_height" to ENHANCED_SMART_SPACE_HEIGHT,
- )
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
index 1205dceb49e9..711f90f043ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
@@ -23,7 +23,6 @@ import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.SysuiTestCase
-import com.android.systemui.keyguard.ui.viewmodel.AodAlphaViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
import com.android.systemui.res.R
import com.android.systemui.statusbar.KeyguardIndicationController
@@ -40,7 +39,6 @@ import org.mockito.MockitoAnnotations
class DefaultIndicationAreaSectionTest : SysuiTestCase() {
@Mock private lateinit var keyguardIndicationAreaViewModel: KeyguardIndicationAreaViewModel
- @Mock private lateinit var aodAlphaViewModel: AodAlphaViewModel
@Mock private lateinit var indicationController: KeyguardIndicationController
private lateinit var underTest: DefaultIndicationAreaSection
@@ -52,7 +50,6 @@ class DefaultIndicationAreaSectionTest : SysuiTestCase() {
DefaultIndicationAreaSection(
context,
keyguardIndicationAreaViewModel,
- aodAlphaViewModel,
indicationController,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
index deb3a83fcbee..8eccde7d6cb8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
@@ -88,6 +88,8 @@ class SmartspaceSectionTest : SysuiTestCase() {
whenever(keyguardClockViewModel.hasCustomWeatherDataDisplay)
.thenReturn(hasCustomWeatherDataDisplay)
whenever(keyguardClockViewModel.clockShouldBeCentered).thenReturn(clockShouldBeCentered)
+ whenever(keyguardSmartspaceViewModel.isSmartspaceEnabled).thenReturn(true)
+
constraintSet = ConstraintSet()
}
@@ -103,7 +105,6 @@ class SmartspaceSectionTest : SysuiTestCase() {
@Test
fun testAddViews_smartspaceEnabled_dateWeatherDecoupled() {
- whenever(keyguardSmartspaceViewModel.isSmartspaceEnabled).thenReturn(true)
whenever(keyguardSmartspaceViewModel.isDateWeatherDecoupled).thenReturn(true)
underTest.addViews(constraintLayout)
assert(smartspaceView.parent == constraintLayout)
@@ -113,7 +114,6 @@ class SmartspaceSectionTest : SysuiTestCase() {
@Test
fun testAddViews_smartspaceEnabled_notDateWeatherDecoupled() {
- whenever(keyguardSmartspaceViewModel.isSmartspaceEnabled).thenReturn(true)
whenever(keyguardSmartspaceViewModel.isDateWeatherDecoupled).thenReturn(false)
underTest.addViews(constraintLayout)
assert(smartspaceView.parent == constraintLayout)
@@ -123,7 +123,6 @@ class SmartspaceSectionTest : SysuiTestCase() {
@Test
fun testConstraintsWhenNotHasCustomWeatherDataDisplay() {
- whenever(keyguardSmartspaceViewModel.isSmartspaceEnabled).thenReturn(true)
whenever(keyguardSmartspaceViewModel.isDateWeatherDecoupled).thenReturn(true)
hasCustomWeatherDataDisplay.value = false
underTest.addViews(constraintLayout)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt
index bfa84335d670..716c40d59ccf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt
@@ -59,7 +59,14 @@ class GoneToAodTransitionViewModelTest : SysuiTestCase() {
// The animation should only start > .4f way through
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
assertThat(enterFromTopTranslationY)
- .isEqualTo(StateToValue(TransitionState.STARTED, pixels))
+ .isEqualTo(
+ StateToValue(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ transitionState = TransitionState.STARTED,
+ value = pixels
+ )
+ )
repository.sendTransitionStep(step(.55f))
assertThat(enterFromTopTranslationY!!.value ?: -1f).isIn(Range.closed(pixels, 0f))
@@ -70,7 +77,14 @@ class GoneToAodTransitionViewModelTest : SysuiTestCase() {
// At the end, the translation should be complete and set to zero
repository.sendTransitionStep(step(1f))
assertThat(enterFromTopTranslationY)
- .isEqualTo(StateToValue(TransitionState.RUNNING, 0f))
+ .isEqualTo(
+ StateToValue(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ transitionState = TransitionState.RUNNING,
+ value = 0f
+ )
+ )
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 729086388a4f..2ec2fe3d0eb7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -54,7 +54,6 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
-import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -219,7 +218,6 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
quickAffordanceInteractor =
KeyguardQuickAffordanceInteractor(
keyguardInteractor = keyguardInteractor,
- shadeInteractor = kosmos.shadeInteractor,
lockPatternUtils = lockPatternUtils,
keyguardStateController = keyguardStateController,
userTracker = userTracker,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
index eded4dcb9bd6..1f14afa1d00b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
@@ -44,6 +44,8 @@ import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepo
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger
@@ -75,6 +77,7 @@ import org.mockito.ArgumentMatchers
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
+import kotlin.test.assertEquals
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -130,6 +133,8 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() {
@Mock
private lateinit var lockscreenToPrimaryBouncerTransitionViewModel:
LockscreenToPrimaryBouncerTransitionViewModel
+ @Mock
+ private lateinit var transitionInteractor: KeyguardTransitionInteractor
private lateinit var underTest: KeyguardQuickAffordancesCombinedViewModel
@@ -146,6 +151,8 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() {
// the viewModel does a `map { 1 - it }` on this value, which is why it's different
private val intendedShadeAlphaMutableStateFlow: MutableStateFlow<Float> = MutableStateFlow(0f)
+ private val intendedFinishedKeyguardStateFlow = MutableStateFlow(KeyguardState.LOCKSCREEN)
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -242,6 +249,7 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() {
intendedAlphaMutableStateFlow.value = 1f
intendedShadeAlphaMutableStateFlow.value = 0f
+ intendedFinishedKeyguardStateFlow.value = KeyguardState.LOCKSCREEN
whenever(aodToLockscreenTransitionViewModel.shortcutsAlpha)
.thenReturn(intendedAlphaMutableStateFlow)
whenever(dozingToLockscreenTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow())
@@ -263,15 +271,15 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() {
whenever(lockscreenToOccludedTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow())
whenever(lockscreenToPrimaryBouncerTransitionViewModel.shortcutsAlpha)
.thenReturn(emptyFlow())
- whenever(shadeInteractor.qsExpansion).thenReturn(intendedShadeAlphaMutableStateFlow)
- whenever(shadeInteractor.anyExpansion).thenReturn(MutableStateFlow(0f))
+ whenever(shadeInteractor.anyExpansion).thenReturn(intendedShadeAlphaMutableStateFlow)
+ whenever(transitionInteractor.finishedKeyguardState)
+ .thenReturn(intendedFinishedKeyguardStateFlow)
underTest =
KeyguardQuickAffordancesCombinedViewModel(
quickAffordanceInteractor =
KeyguardQuickAffordanceInteractor(
keyguardInteractor = keyguardInteractor,
- shadeInteractor = shadeInteractor,
lockPatternUtils = lockPatternUtils,
keyguardStateController = keyguardStateController,
userTracker = userTracker,
@@ -306,7 +314,8 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() {
lockscreenToGoneTransitionViewModel = lockscreenToGoneTransitionViewModel,
lockscreenToOccludedTransitionViewModel = lockscreenToOccludedTransitionViewModel,
lockscreenToPrimaryBouncerTransitionViewModel =
- lockscreenToPrimaryBouncerTransitionViewModel
+ lockscreenToPrimaryBouncerTransitionViewModel,
+ transitionInteractor = transitionInteractor,
)
}
@@ -684,6 +693,30 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() {
)
}
+ @Test
+ fun shadeExpansionAlpha_changes_whenOnLockscreen() =
+ testScope.runTest {
+ intendedFinishedKeyguardStateFlow.value = KeyguardState.LOCKSCREEN
+ intendedShadeAlphaMutableStateFlow.value = 0.25f
+ val underTest = collectLastValue(underTest.transitionAlpha)
+ assertEquals(0.75f, underTest())
+
+ intendedShadeAlphaMutableStateFlow.value = 0.3f
+ assertEquals(0.7f, underTest())
+ }
+
+ @Test
+ fun shadeExpansionAlpha_alwaysZero_whenNotOnLockscreen() =
+ testScope.runTest {
+ intendedFinishedKeyguardStateFlow.value = KeyguardState.GONE
+ intendedShadeAlphaMutableStateFlow.value = 0.5f
+ val underTest = collectLastValue(underTest.transitionAlpha)
+ assertEquals(0f, underTest())
+
+ intendedShadeAlphaMutableStateFlow.value = 0.25f
+ assertEquals(0f, underTest())
+ }
+
private suspend fun setUpQuickAffordanceModel(
position: KeyguardQuickAffordancePosition,
testConfig: TestConfig,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/MediaTestUtils.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/MediaTestUtils.kt
index 3437365d9902..4e976d0597cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/MediaTestUtils.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/MediaTestUtils.kt
@@ -17,7 +17,7 @@
package com.android.systemui.media.controls
import com.android.internal.logging.InstanceId
-import com.android.systemui.media.controls.models.player.MediaData
+import com.android.systemui.media.controls.shared.model.MediaData
class MediaTestUtils {
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java
index fb101dda6aaf..bb5b57287a9a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataCombineLatestTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline;
+package com.android.systemui.media.controls.domain.pipeline;
import static com.google.common.truth.Truth.assertThat;
@@ -33,8 +33,8 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.InstanceId;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.media.controls.models.player.MediaData;
-import com.android.systemui.media.controls.models.player.MediaDeviceData;
+import com.android.systemui.media.controls.shared.model.MediaData;
+import com.android.systemui.media.controls.shared.model.MediaDeviceData;
import org.junit.Before;
import org.junit.Rule;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterTest.kt
index 94b9fa4a6582..59eb7bb73de7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
import android.app.smartspace.SmartspaceAction
import android.os.Bundle
@@ -25,10 +25,10 @@ import com.android.internal.logging.InstanceId
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.media.controls.MediaTestUtils
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.recommendation.EXTRA_KEY_TRIGGER_RESUME
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
-import com.android.systemui.media.controls.ui.MediaPlayerData
+import com.android.systemui.media.controls.shared.model.EXTRA_KEY_TRIGGER_RESUME
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.ui.controller.MediaPlayerData
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.settings.UserTracker
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManagerTest.kt
index 59d81049ad46..61bfdb548b4f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManagerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
import android.app.IUriGrantsManager
import android.app.Notification
@@ -51,13 +51,13 @@ import com.android.systemui.InstanceIdSequenceFake
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.recommendation.EXTRA_KEY_TRIGGER_SOURCE
-import com.android.systemui.media.controls.models.recommendation.EXTRA_VALUE_TRIGGER_PERIODIC
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaDataProvider
-import com.android.systemui.media.controls.resume.MediaResumeListener
-import com.android.systemui.media.controls.resume.ResumeMediaBrowser
+import com.android.systemui.media.controls.domain.resume.MediaResumeListener
+import com.android.systemui.media.controls.domain.resume.ResumeMediaBrowser
+import com.android.systemui.media.controls.shared.model.EXTRA_KEY_TRIGGER_SOURCE
+import com.android.systemui.media.controls.shared.model.EXTRA_VALUE_TRIGGER_PERIODIC
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaDataProvider
import com.android.systemui.media.controls.util.MediaControllerFactory
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.controls.util.MediaUiEventLogger
@@ -1476,7 +1476,7 @@ class MediaDataManagerTest : SysuiTestCase() {
}
@Test
- fun testOnMediaDataTimedOut_doesNotUpdateLastActiveTime() {
+ fun testOnMediaDataTimedOut_updatesLastActiveTime() {
// GIVEN that the manager has a notification
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
@@ -1487,7 +1487,7 @@ class MediaDataManagerTest : SysuiTestCase() {
val currentTime = clock.elapsedRealtime()
mediaDataManager.setTimedOut(KEY, true, true)
- // THEN the last active time is not changed
+ // THEN the last active time is changed
verify(listener)
.onMediaDataLoaded(
eq(KEY),
@@ -1497,11 +1497,11 @@ class MediaDataManagerTest : SysuiTestCase() {
eq(0),
eq(false)
)
- assertThat(mediaDataCaptor.value.lastActive).isLessThan(currentTime)
+ assertThat(mediaDataCaptor.value.lastActive).isAtLeast(currentTime)
}
@Test
- fun testOnActiveMediaConverted_doesNotUpdateLastActiveTime() {
+ fun testOnActiveMediaConverted_updatesLastActiveTime() {
// GIVEN that the manager has a notification with a resume action
addNotificationAndLoad()
val data = mediaDataCaptor.value
@@ -1514,6 +1514,42 @@ class MediaDataManagerTest : SysuiTestCase() {
val currentTime = clock.elapsedRealtime()
mediaDataManager.onNotificationRemoved(KEY)
+ // THEN the last active time is changed
+ verify(listener)
+ .onMediaDataLoaded(
+ eq(PACKAGE_NAME),
+ eq(KEY),
+ capture(mediaDataCaptor),
+ eq(true),
+ eq(0),
+ eq(false)
+ )
+ assertThat(mediaDataCaptor.value.resumption).isTrue()
+ assertThat(mediaDataCaptor.value.lastActive).isAtLeast(currentTime)
+
+ // Log as a conversion event, not as a new resume control
+ verify(logger).logActiveConvertedToResume(anyInt(), eq(PACKAGE_NAME), eq(instanceId))
+ verify(logger, never()).logResumeMediaAdded(anyInt(), eq(PACKAGE_NAME), any())
+ }
+
+ @Test
+ fun testOnInactiveMediaConverted_doesNotUpdateLastActiveTime() {
+ // GIVEN that the manager has a notification with a resume action
+ addNotificationAndLoad()
+ val data = mediaDataCaptor.value
+ val instanceId = data.instanceId
+ assertThat(data.resumption).isFalse()
+ mediaDataManager.onMediaDataLoaded(
+ KEY,
+ null,
+ data.copy(resumeAction = Runnable {}, active = false)
+ )
+
+ // WHEN the notification is removed
+ clock.advanceTime(100)
+ val currentTime = clock.elapsedRealtime()
+ mediaDataManager.onNotificationRemoved(KEY)
+
// THEN the last active time is not changed
verify(listener)
.onMediaDataLoaded(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
index e3c4c2858b3d..7f3d79f7e288 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
import android.bluetooth.BluetoothLeBroadcast
import android.bluetooth.BluetoothLeBroadcastMetadata
@@ -27,20 +27,25 @@ import android.media.RoutingSessionInfo
import android.media.session.MediaController
import android.media.session.MediaController.PlaybackInfo
import android.media.session.MediaSession
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager
+import com.android.settingslib.flags.Flags
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
import com.android.settingslib.media.PhoneMediaDevice
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.media.controls.MediaTestUtils
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.player.MediaDeviceData
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.MediaDeviceData
+import com.android.systemui.media.controls.util.LocalMediaManagerFactory
import com.android.systemui.media.controls.util.MediaControllerFactory
import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionManager
import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionManagerFactory
@@ -82,6 +87,7 @@ private const val NORMAL_APP_NAME = "NORMAL_APP_NAME"
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
public class MediaDeviceManagerTest : SysuiTestCase() {
+ @get:Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
private lateinit var manager: MediaDeviceManager
@Mock private lateinit var controllerFactory: MediaControllerFactory
@@ -667,7 +673,28 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
}
@Test
- fun onBroadcastStarted_currentMediaDeviceDataIsBroadcasting() {
+ fun onBroadcastStarted_flagOff_currentMediaDeviceDataIsBroadcasting() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
+ mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+ val broadcastCallback = setupBroadcastCallback()
+ setupLeAudioConfiguration(true)
+ setupBroadcastPackage(BROADCAST_APP_NAME)
+ broadcastCallback.onBroadcastStarted(1, 1)
+
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+
+ val data = captureDeviceData(KEY)
+ assertThat(data.showBroadcastButton).isFalse()
+ assertThat(data.enabled).isTrue()
+ assertThat(data.name).isEqualTo(DEVICE_NAME)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
+ @DisableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+ fun onBroadcastStarted_legacy_currentMediaDeviceDataIsBroadcasting() {
val broadcastCallback = setupBroadcastCallback()
setupLeAudioConfiguration(true)
setupBroadcastPackage(BROADCAST_APP_NAME)
@@ -685,7 +712,9 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
}
@Test
- fun onBroadcastStarted_currentMediaDeviceDataIsNotBroadcasting() {
+ @EnableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
+ @DisableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+ fun onBroadcastStarted_legacy_currentMediaDeviceDataIsNotBroadcasting() {
val broadcastCallback = setupBroadcastCallback()
setupLeAudioConfiguration(true)
setupBroadcastPackage(NORMAL_APP_NAME)
@@ -702,6 +731,62 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
+ @DisableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+ fun onBroadcastStopped_legacy_bluetoothLeBroadcastIsDisabledAndBroadcastingButtonIsGone() {
+ val broadcastCallback = setupBroadcastCallback()
+ setupLeAudioConfiguration(false)
+ broadcastCallback.onBroadcastStopped(1, 1)
+
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+
+ val data = captureDeviceData(KEY)
+ assertThat(data.showBroadcastButton).isFalse()
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
+ @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+ fun onBroadcastStarted_currentMediaDeviceDataIsBroadcasting() {
+ val broadcastCallback = setupBroadcastCallback()
+ setupLeAudioConfiguration(true)
+ setupBroadcastPackage(BROADCAST_APP_NAME)
+ broadcastCallback.onBroadcastStarted(1, 1)
+
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+
+ val data = captureDeviceData(KEY)
+ assertThat(data.showBroadcastButton).isFalse()
+ assertThat(data.enabled).isFalse()
+ assertThat(data.name).isEqualTo(context.getString(R.string.audio_sharing_description))
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
+ @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+ fun onBroadcastStarted_currentMediaDeviceDataIsNotBroadcasting() {
+ val broadcastCallback = setupBroadcastCallback()
+ setupLeAudioConfiguration(true)
+ setupBroadcastPackage(NORMAL_APP_NAME)
+ broadcastCallback.onBroadcastStarted(1, 1)
+
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+
+ val data = captureDeviceData(KEY)
+ assertThat(data.showBroadcastButton).isFalse()
+ assertThat(data.enabled).isFalse()
+ assertThat(data.name).isEqualTo(context.getString(R.string.audio_sharing_description))
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
+ @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
fun onBroadcastStopped_bluetoothLeBroadcastIsDisabledAndBroadcastingButtonIsGone() {
val broadcastCallback = setupBroadcastCallback()
setupLeAudioConfiguration(false)
@@ -713,6 +798,8 @@ public class MediaDeviceManagerTest : SysuiTestCase() {
val data = captureDeviceData(KEY)
assertThat(data.showBroadcastButton).isFalse()
+ assertThat(data.name?.equals(context.getString(R.string.audio_sharing_description)))
+ .isFalse()
}
private fun captureCallback(): LocalMediaManager.DeviceCallback {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaSessionBasedFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt
index 3099609d42f0..5a3c220b3d23 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaSessionBasedFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
import android.media.session.MediaController
import android.media.session.MediaController.PlaybackInfo
@@ -25,7 +25,7 @@ import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.media.controls.MediaTestUtils
-import com.android.systemui.media.controls.models.player.MediaData
+import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
index 8baa06ac0141..3cc65c9524a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.pipeline
+package com.android.systemui.media.controls.domain.pipeline
import android.media.MediaMetadata
import android.media.session.MediaController
@@ -24,8 +24,8 @@ import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.media.controls.MediaTestUtils
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
import com.android.systemui.media.controls.util.MediaControllerFactory
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.plugins.statusbar.StatusBarStateController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/MediaResumeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/MediaResumeListenerTest.kt
index 530b86eb4978..55ff231ab6b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/MediaResumeListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/MediaResumeListenerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.resume
+package com.android.systemui.media.controls.domain.resume
import android.app.PendingIntent
import android.content.ComponentName
@@ -34,10 +34,10 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dump.DumpManager
import com.android.systemui.media.controls.MediaTestUtils
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.player.MediaDeviceData
-import com.android.systemui.media.controls.pipeline.MediaDataManager
-import com.android.systemui.media.controls.pipeline.RESUME_MEDIA_TIMEOUT
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.RESUME_MEDIA_TIMEOUT
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.MediaDeviceData
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.settings.UserTracker
import com.android.systemui.tuner.TunerService
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserTest.kt
index b45e66bfc31b..8dfa5b8e640c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.resume
+package com.android.systemui.media.controls.domain.resume
import android.content.ComponentName
import android.content.Context
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/shared/SmartspaceMediaDataTest.kt
index f7c20ac35957..473dc4712c25 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaDataTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/shared/SmartspaceMediaDataTest.kt
@@ -14,14 +14,16 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.models.recommendation
+package com.android.systemui.media.controls.shared
import android.app.smartspace.SmartspaceAction
import android.graphics.drawable.Icon
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.media.controls.shared.model.NUM_REQUIRED_RECOMMENDATIONS
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.res.R
import com.google.common.truth.Truth.assertThat
import org.junit.Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaPlayerDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaPlayerDataTest.kt
index 32b822d798f8..b509e779a8c3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaPlayerDataTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaPlayerDataTest.kt
@@ -20,7 +20,9 @@ import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.media.controls.MediaTestUtils
-import com.android.systemui.media.controls.models.player.MediaData
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.ui.controller.MediaControlPanel
+import com.android.systemui.media.controls.ui.controller.MediaPlayerData
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/AnimationBindHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/AnimationBindHandlerTest.kt
index 99f56b16ab8b..eb885fd4ae41 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/AnimationBindHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/AnimationBindHandlerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.animation
import android.graphics.drawable.Animatable2
import android.graphics.drawable.Drawable
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/ColorSchemeTransitionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransitionTest.kt
index a94374680b91..aa297b537c49 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/ColorSchemeTransitionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransitionTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.animation
import android.animation.ValueAnimator
import android.graphics.Color
@@ -22,8 +22,8 @@ import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.media.controls.models.GutsViewHolder
-import com.android.systemui.media.controls.models.player.MediaViewHolder
+import com.android.systemui.media.controls.ui.view.GutsViewHolder
+import com.android.systemui.media.controls.ui.view.MediaViewHolder
import com.android.systemui.monet.ColorScheme
import com.android.systemui.surfaceeffects.ripple.MultiRippleController
import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MetadataAnimationHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/MetadataAnimationHandlerTest.kt
index 323b7818ed3d..711669eb2dd0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MetadataAnimationHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/MetadataAnimationHandlerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.animation
import android.animation.Animator
import android.test.suitebuilder.annotation.SmallTest
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/SeekBarObserverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/binder/SeekBarObserverTest.kt
index 4ec29ceb56d3..8a6b2722b1a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/SeekBarObserverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/binder/SeekBarObserverTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.models.player
+package com.android.systemui.media.controls.ui.binder
import android.animation.Animator
import android.animation.ObjectAnimator
@@ -25,7 +25,9 @@ import android.widget.SeekBar
import android.widget.TextView
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.media.controls.ui.SquigglyProgress
+import com.android.systemui.media.controls.ui.drawable.SquigglyProgress
+import com.android.systemui.media.controls.ui.view.MediaViewHolder
+import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel
import com.android.systemui.res.R
import com.google.common.truth.Truth.assertThat
import org.junit.Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerTest.kt
index 50f0eb4eca2c..9f5260c252e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import android.provider.Settings
import android.test.suitebuilder.annotation.SmallTest
@@ -25,6 +25,8 @@ import android.view.View.VISIBLE
import android.widget.FrameLayout
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
index f3b9102da780..f755199b4c72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import android.app.PendingIntent
import android.content.res.ColorStateList
@@ -38,11 +38,13 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.media.controls.MediaTestUtils
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
-import com.android.systemui.media.controls.pipeline.EMPTY_SMARTSPACE_MEDIA_DATA
-import com.android.systemui.media.controls.pipeline.MediaDataManager
-import com.android.systemui.media.controls.ui.MediaHierarchyManager.Companion.LOCATION_QS
+import com.android.systemui.media.controls.domain.pipeline.EMPTY_SMARTSPACE_MEDIA_DATA
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager.Companion.LOCATION_QS
+import com.android.systemui.media.controls.ui.view.MediaHostState
+import com.android.systemui.media.controls.ui.view.MediaScrollView
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.plugins.ActivityStarter
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
index 59965022d7cc..2e7829d4ea7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import android.animation.Animator
import android.animation.AnimatorSet
@@ -40,6 +40,9 @@ import android.media.MediaMetadata
import android.media.session.MediaSession
import android.media.session.PlaybackState
import android.os.Bundle
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.provider.Settings
import android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS
import android.testing.AndroidTestingRunner
@@ -61,23 +64,24 @@ import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
import com.android.internal.widget.CachingIconView
import com.android.systemui.ActivityIntentHelper
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.bluetooth.BroadcastDialogController
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.media.controls.MediaTestUtils
-import com.android.systemui.media.controls.models.GutsViewHolder
-import com.android.systemui.media.controls.models.player.MediaAction
-import com.android.systemui.media.controls.models.player.MediaButton
-import com.android.systemui.media.controls.models.player.MediaData
-import com.android.systemui.media.controls.models.player.MediaDeviceData
-import com.android.systemui.media.controls.models.player.MediaViewHolder
-import com.android.systemui.media.controls.models.player.SeekBarObserver
-import com.android.systemui.media.controls.models.player.SeekBarViewModel
-import com.android.systemui.media.controls.models.recommendation.KEY_SMARTSPACE_APP_NAME
-import com.android.systemui.media.controls.models.recommendation.RecommendationViewHolder
-import com.android.systemui.media.controls.models.recommendation.SmartspaceMediaData
-import com.android.systemui.media.controls.pipeline.EMPTY_SMARTSPACE_MEDIA_DATA
-import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.EMPTY_SMARTSPACE_MEDIA_DATA
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import com.android.systemui.media.controls.shared.model.KEY_SMARTSPACE_APP_NAME
+import com.android.systemui.media.controls.shared.model.MediaAction
+import com.android.systemui.media.controls.shared.model.MediaButton
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.MediaDeviceData
+import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
+import com.android.systemui.media.controls.ui.binder.SeekBarObserver
+import com.android.systemui.media.controls.ui.view.GutsViewHolder
+import com.android.systemui.media.controls.ui.view.MediaViewHolder
+import com.android.systemui.media.controls.ui.view.RecommendationViewHolder
+import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.media.dialog.MediaOutputDialogFactory
@@ -88,6 +92,7 @@ import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffectView
import com.android.systemui.surfaceeffects.ripple.MultiRippleView
import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseAnimationConfig
import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseView
@@ -137,6 +142,7 @@ private const val APP_NAME = "APP_NAME"
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class MediaControlPanelTest : SysuiTestCase() {
+ @get:Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
private lateinit var player: MediaControlPanel
@@ -190,6 +196,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
private lateinit var dismissText: TextView
private lateinit var multiRippleView: MultiRippleView
private lateinit var turbulenceNoiseView: TurbulenceNoiseView
+ private lateinit var loadingEffectView: LoadingEffectView
private lateinit var session: MediaSession
private lateinit var device: MediaDeviceData
@@ -402,6 +409,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
multiRippleView = MultiRippleView(context, null)
turbulenceNoiseView = TurbulenceNoiseView(context, null)
+ loadingEffectView = LoadingEffectView(context, null)
whenever(viewHolder.player).thenReturn(view)
whenever(viewHolder.appIcon).thenReturn(appIcon)
@@ -447,6 +455,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
whenever(viewHolder.multiRippleView).thenReturn(multiRippleView)
whenever(viewHolder.turbulenceNoiseView).thenReturn(turbulenceNoiseView)
+ whenever(viewHolder.loadingEffectView).thenReturn(loadingEffectView)
}
/** Initialize elements for the recommendation view holder */
@@ -1238,6 +1247,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
fun bindBroadcastButton() {
initMediaViewHolderMocks()
initDeviceMediaData(true, APP_NAME)
@@ -2429,6 +2439,7 @@ public class MediaControlPanelTest : SysuiTestCase() {
mainExecutor.execute {
assertThat(turbulenceNoiseView.visibility).isEqualTo(View.VISIBLE)
+ assertThat(loadingEffectView.visibility).isEqualTo(View.INVISIBLE)
clock.advanceTime(
MediaControlPanel.TURBULENCE_NOISE_PLAY_DURATION +
@@ -2436,6 +2447,40 @@ public class MediaControlPanelTest : SysuiTestCase() {
)
assertThat(turbulenceNoiseView.visibility).isEqualTo(View.INVISIBLE)
+ assertThat(loadingEffectView.visibility).isEqualTo(View.INVISIBLE)
+ }
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_SHADERLIB_LOADING_EFFECT_REFACTOR)
+ fun playTurbulenceNoise_newLoadingEffect_finishesAfterDuration() {
+ val semanticActions =
+ MediaButton(
+ playOrPause =
+ MediaAction(
+ icon = null,
+ action = {},
+ contentDescription = "play",
+ background = null
+ )
+ )
+ val data = mediaData.copy(semanticActions = semanticActions)
+ player.attachPlayer(viewHolder)
+ player.bindPlayer(data, KEY)
+
+ viewHolder.actionPlayPause.callOnClick()
+
+ mainExecutor.execute {
+ assertThat(loadingEffectView.visibility).isEqualTo(View.VISIBLE)
+ assertThat(turbulenceNoiseView.visibility).isEqualTo(View.INVISIBLE)
+
+ clock.advanceTime(
+ MediaControlPanel.TURBULENCE_NOISE_PLAY_DURATION +
+ TurbulenceNoiseAnimationConfig.DEFAULT_EASING_DURATION_IN_MILLIS.toLong()
+ )
+
+ assertThat(loadingEffectView.visibility).isEqualTo(View.INVISIBLE)
+ assertThat(turbulenceNoiseView.visibility).isEqualTo(View.INVISIBLE)
}
}
@@ -2458,6 +2503,30 @@ public class MediaControlPanelTest : SysuiTestCase() {
viewHolder.action0.callOnClick()
assertThat(turbulenceNoiseView.visibility).isEqualTo(View.INVISIBLE)
+ assertThat(loadingEffectView.visibility).isEqualTo(View.INVISIBLE)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_SHADERLIB_LOADING_EFFECT_REFACTOR)
+ fun playTurbulenceNoise_newLoadingEffect_whenPlaybackStateIsNotPlaying_doesNotPlayTurbulence() {
+ val semanticActions =
+ MediaButton(
+ custom0 =
+ MediaAction(
+ icon = null,
+ action = {},
+ contentDescription = "custom0",
+ background = null
+ ),
+ )
+ val data = mediaData.copy(semanticActions = semanticActions)
+ player.attachPlayer(viewHolder)
+ player.bindPlayer(data, KEY)
+
+ viewHolder.action0.callOnClick()
+
+ assertThat(loadingEffectView.visibility).isEqualTo(View.INVISIBLE)
+ assertThat(turbulenceNoiseView.visibility).isEqualTo(View.INVISIBLE)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
index 87d093fe2dff..45f49f01a43e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import android.graphics.Rect
import android.provider.Settings
@@ -24,14 +24,19 @@ import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardViewController
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.setCommunalAvailable
import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq
import com.android.systemui.dreams.DreamOverlayStateController
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.kosmos.testScope
-import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import com.android.systemui.media.controls.ui.view.MediaCarouselScrollHandler
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.media.dream.MediaDreamComplication
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -505,6 +510,10 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
@Test
fun testCommunalLocation() =
testScope.runTest {
+ mSetFlagsRule.enableFlags(Flags.FLAG_COMMUNAL_HUB)
+ kosmos.setCommunalAvailable(true)
+ runCurrent()
+
communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
runCurrent()
verify(mediaCarouselController)
@@ -530,6 +539,66 @@ class MediaHierarchyManagerTest : SysuiTestCase() {
}
@Test
+ fun testCommunalLocation_showsOverLockscreen() =
+ testScope.runTest {
+ mSetFlagsRule.enableFlags(Flags.FLAG_COMMUNAL_HUB)
+ kosmos.setCommunalAvailable(true)
+ runCurrent()
+
+ // Device is on lock screen.
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+
+ // UMO goes to communal even over the lock screen.
+ communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
+ runCurrent()
+ verify(mediaCarouselController)
+ .onDesiredLocationChanged(
+ eq(MediaHierarchyManager.LOCATION_COMMUNAL_HUB),
+ nullable(),
+ eq(false),
+ anyLong(),
+ anyLong()
+ )
+ }
+
+ @Test
+ fun testCommunalLocation_showsUntilQsExpands() =
+ testScope.runTest {
+ mSetFlagsRule.enableFlags(Flags.FLAG_COMMUNAL_HUB)
+ kosmos.setCommunalAvailable(true)
+ runCurrent()
+
+ // Device is on lock screen.
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+
+ communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
+ runCurrent()
+ verify(mediaCarouselController)
+ .onDesiredLocationChanged(
+ eq(MediaHierarchyManager.LOCATION_COMMUNAL_HUB),
+ nullable(),
+ eq(false),
+ anyLong(),
+ anyLong()
+ )
+ clearInvocations(mediaCarouselController)
+
+ // Start opening the shade.
+ mediaHierarchyManager.qsExpansion = 0.1f
+ runCurrent()
+
+ // UMO goes to the shade instead.
+ verify(mediaCarouselController)
+ .onDesiredLocationChanged(
+ eq(MediaHierarchyManager.LOCATION_QS),
+ any(MediaHostState::class.java),
+ eq(false),
+ anyLong(),
+ anyLong()
+ )
+ }
+
+ @Test
fun testQsExpandedChanged_noQqsMedia() {
// When we are looking at QQS with active media
whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt
index b701d7f315bc..a73bb2cdf79a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import android.content.res.Configuration
import android.content.res.Configuration.ORIENTATION_LANDSCAPE
@@ -24,8 +24,9 @@ import android.view.View
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.media.controls.models.player.MediaViewHolder
-import com.android.systemui.media.controls.models.recommendation.RecommendationViewHolder
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaViewHolder
+import com.android.systemui.media.controls.ui.view.RecommendationViewHolder
import com.android.systemui.media.controls.util.MediaFlags
import com.android.systemui.res.R
import com.android.systemui.util.animation.MeasurementInput
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/SquigglyProgressTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/drawable/SquigglyProgressTest.kt
index d6cff81c0aaa..0319aaaedd27 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/SquigglyProgressTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/drawable/SquigglyProgressTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.drawable
import android.graphics.Canvas
import android.graphics.Color
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandlerTest.kt
index 74b3fce12790..120836959fdc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandlerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.view
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/MediaViewHolderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/view/MediaViewHolderTest.kt
index c829d4cbfb71..d3c703ccce46 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/MediaViewHolderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/view/MediaViewHolderTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.models.player
+package com.android.systemui.media.controls.ui.view
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/SeekBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt
index e3c8b052327c..e1c2d3f115ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/SeekBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/viewmodel/SeekBarViewModelTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.models.player
+package com.android.systemui.media.controls.ui.viewmodel
import android.media.MediaMetadata
import android.media.session.MediaController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index f7873aa3e40e..ca403e0addec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -19,6 +19,7 @@ package com.android.systemui.media.dialog;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -47,6 +48,7 @@ import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.settingslib.media.LocalMediaManager;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.broadcast.BroadcastSender;
@@ -126,6 +128,13 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase {
mNotifCollection, mDialogTransitionAnimator,
mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
mKeyguardManager, mFlags, mUserTracker);
+
+ // Using a fake package will cause routing operations to fail, so we intercept
+ // scanning-related operations.
+ mMediaOutputController.mLocalMediaManager = mock(LocalMediaManager.class);
+ doNothing().when(mMediaOutputController.mLocalMediaManager).startScan();
+ doNothing().when(mMediaOutputController.mLocalMediaManager).stopScan();
+
mMediaOutputBaseDialogImpl = new MediaOutputBaseDialogImpl(mContext, mBroadcastSender,
mMediaOutputController);
mMediaOutputBaseDialogImpl.onCreate(new Bundle());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
index 2b62f03c5c02..d9ddc8e6b23b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
@@ -35,6 +35,9 @@ import android.media.session.MediaSessionManager;
import android.media.session.PlaybackState;
import android.os.PowerExemptionManager;
import android.os.UserHandle;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.FeatureFlagUtils;
@@ -61,6 +64,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.Co
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
@@ -76,6 +80,9 @@ public class MediaOutputDialogTest extends SysuiTestCase {
private static final String TEST_PACKAGE = "test_package";
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
// Mock
private final MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class);
private MediaController mMediaController = mock(MediaController.class);
@@ -170,6 +177,7 @@ public class MediaOutputDialogTest extends SysuiTestCase {
}
@Test
+ @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
public void getStopButtonVisibility_remoteBLEDevice_returnVisible() {
when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
mLocalBluetoothLeBroadcast);
@@ -181,6 +189,7 @@ public class MediaOutputDialogTest extends SysuiTestCase {
}
@Test
+ @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
public void getStopButtonVisibility_remoteNonBLEDevice_returnGone() {
when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
mLocalBluetoothLeBroadcast);
@@ -201,6 +210,7 @@ public class MediaOutputDialogTest extends SysuiTestCase {
}
@Test
+ @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
public void isBroadcastSupported_flagOnAndConnectBleDevice_returnsTrue() {
when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
mLocalBluetoothLeBroadcast);
@@ -213,6 +223,7 @@ public class MediaOutputDialogTest extends SysuiTestCase {
}
@Test
+ @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
public void isBroadcastSupported_flagOnAndNoBleDevice_returnsFalse() {
when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
mLocalBluetoothLeBroadcast);
@@ -225,6 +236,7 @@ public class MediaOutputDialogTest extends SysuiTestCase {
}
@Test
+ @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
public void isBroadcastSupported_notSupportBroadcastAndflagOn_returnsFalse() {
FeatureFlagUtils.setEnabled(mContext,
FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true);
@@ -233,6 +245,7 @@ public class MediaOutputDialogTest extends SysuiTestCase {
}
@Test
+ @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
public void isBroadcastSupported_flagOffAndConnectToBleDevice_returnsTrue() {
when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
mLocalBluetoothLeBroadcast);
@@ -245,6 +258,7 @@ public class MediaOutputDialogTest extends SysuiTestCase {
}
@Test
+ @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
public void isBroadcastSupported_flagOffAndNoBleDevice_returnsTrue() {
when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
mLocalBluetoothLeBroadcast);
@@ -257,6 +271,7 @@ public class MediaOutputDialogTest extends SysuiTestCase {
}
@Test
+ @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
public void isBroadcastSupported_noBleDeviceAndEnabledBroadcast_returnsTrue() {
when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
mLocalBluetoothLeBroadcast);
@@ -269,6 +284,7 @@ public class MediaOutputDialogTest extends SysuiTestCase {
}
@Test
+ @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
public void isBroadcastSupported_noBleDeviceAndDisabledBroadcast_returnsFalse() {
when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
mLocalBluetoothLeBroadcast);
@@ -281,6 +297,7 @@ public class MediaOutputDialogTest extends SysuiTestCase {
}
@Test
+ @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
public void getBroadcastIconVisibility_isBroadcasting_returnVisible() {
when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
mLocalBluetoothLeBroadcast);
@@ -292,6 +309,7 @@ public class MediaOutputDialogTest extends SysuiTestCase {
}
@Test
+ @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
public void getBroadcastIconVisibility_noBroadcasting_returnGone() {
when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
mLocalBluetoothLeBroadcast);
@@ -303,6 +321,7 @@ public class MediaOutputDialogTest extends SysuiTestCase {
}
@Test
+ @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
public void getBroadcastIconVisibility_remoteNonLeDevice_returnGone() {
when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(
mLocalBluetoothLeBroadcast);
@@ -355,6 +374,7 @@ public class MediaOutputDialogTest extends SysuiTestCase {
}
@Test
+ @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
public void getStopButtonText_supportsBroadcast_returnsBroadcastText() {
String stopText = mContext.getText(R.string.media_output_broadcast).toString();
MediaDevice mMediaDevice = mock(MediaDevice.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaComplicationViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaComplicationViewControllerTest.java
index ce885c0bba2a..a82884377602 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaComplicationViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaComplicationViewControllerTest.java
@@ -25,7 +25,7 @@ import android.widget.FrameLayout;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.media.controls.ui.MediaHost;
+import com.android.systemui.media.controls.ui.view.MediaHost;
import com.android.systemui.util.animation.UniqueObjectHostView;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
index 8a316642a3b0..ff7c970960e9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
@@ -33,8 +33,8 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.complication.DreamMediaEntryComplication;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.media.controls.models.player.MediaData;
-import com.android.systemui.media.controls.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.shared.model.MediaData;
import org.junit.Before;
import org.junit.Test;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index e2be4cbab14d..27f59d292927 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -102,7 +102,6 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
whenever(mediaTttFlags.isMediaTttEnabled()).thenReturn(true)
- whenever(mediaTttFlags.isMediaTttReceiverSuccessRippleEnabled()).thenReturn(true)
fakeAppIconDrawable = context.getDrawable(R.drawable.ic_cake)!!
whenever(packageManager.getApplicationIcon(PACKAGE_NAME)).thenReturn(fakeAppIconDrawable)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
index bfb18c88bf9b..52859cdeb406 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
@@ -22,6 +22,8 @@ import static android.view.Display.INVALID_DISPLAY;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.wm.shell.Flags.enableTaskbarNavbarUnification;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -289,32 +291,43 @@ public class NavigationBarControllerImplTest extends SysuiTestCase {
verify(mCommandQueue, never()).addCallback(any(TaskbarDelegate.class));
}
-
@Test
public void testConfigurationChange_taskbarNotInitialized() {
Configuration configuration = mContext.getResources().getConfiguration();
- when(Utilities.isLargeScreen(any())).thenReturn(true);
+ mNavigationBarController.mIsLargeScreen = true;
mNavigationBarController.onConfigChanged(configuration);
verify(mTaskbarDelegate, never()).onConfigurationChanged(configuration);
}
@Test
- public void testConfigurationChangeUnfolding_taskbarInitialized() {
+ public void testConfigurationChange_taskbarInitialized() {
Configuration configuration = mContext.getResources().getConfiguration();
- when(Utilities.isLargeScreen(any())).thenReturn(true);
+ mNavigationBarController.mIsLargeScreen = true;
when(mTaskbarDelegate.isInitialized()).thenReturn(true);
mNavigationBarController.onConfigChanged(configuration);
verify(mTaskbarDelegate, times(1)).onConfigurationChanged(configuration);
}
@Test
- public void testConfigurationChangeFolding_taskbarInitialized() {
+ public void testShouldRenderTaskbar_taskbarNotRenderedOnPhone() {
+ mNavigationBarController.mIsLargeScreen = false;
+ mNavigationBarController.mIsPhone = true;
+ assertFalse(mNavigationBarController.supportsTaskbar());
+ }
+
+ @Test
+ public void testShouldRenderTaskbar_taskbarRenderedOnTabletOrUnfolded() {
+ mNavigationBarController.mIsLargeScreen = true;
+ mNavigationBarController.mIsPhone = false;
+ assertTrue(mNavigationBarController.supportsTaskbar());
+ }
+
+ @Test
+ public void testShouldRenderTaskbar_taskbarRenderedInFoldedState() {
assumeTrue(enableTaskbarNavbarUnification());
- Configuration configuration = mContext.getResources().getConfiguration();
- when(Utilities.isLargeScreen(any())).thenReturn(false);
- when(mTaskbarDelegate.isInitialized()).thenReturn(true);
- mNavigationBarController.onConfigChanged(configuration);
- verify(mTaskbarDelegate, times(1)).onConfigurationChanged(configuration);
+ mNavigationBarController.mIsLargeScreen = false;
+ mNavigationBarController.mIsPhone = false;
+ assertTrue(mNavigationBarController.supportsTaskbar());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 0d1e87433c60..31746a2a46a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -184,6 +184,8 @@ public class NavigationBarTest extends SysuiTestCase {
@Mock
private NavBarButtonClickLogger mNavBarButtonClickLogger;
@Mock
+ private NavbarOrientationTrackingLogger mNavbarOrientationTrackingLogger;
+ @Mock
private ViewTreeObserver mViewTreeObserver;
NavBarHelper mNavBarHelper;
@Mock
@@ -599,7 +601,8 @@ public class NavigationBarTest extends SysuiTestCase {
mWakefulnessLifecycle,
mTaskStackChangeListeners,
new FakeDisplayTracker(mContext),
- mNavBarButtonClickLogger));
+ mNavBarButtonClickLogger,
+ mNavbarOrientationTrackingLogger));
}
private void processAllMessages() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
index 563a3fe9fc7f..e4a4836bcd46 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
@@ -20,6 +20,7 @@ import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
+import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
import static com.google.common.truth.Truth.assertThat;
@@ -57,7 +58,7 @@ import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlagsClassic;
-import com.android.systemui.media.controls.ui.MediaHost;
+import com.android.systemui.media.controls.ui.view.MediaHost;
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.dagger.QSComponent;
import com.android.systemui.qs.external.TileServiceRequestController;
@@ -511,6 +512,28 @@ public class QSImplTest extends SysuiTestCase {
);
}
+ @Test
+ public void testSceneContainerFlagsEnabled_statusBarStateIsShade() {
+ when(mSceneContainerFlags.isEnabled()).thenReturn(true);
+
+ mUnderTest.onStateChanged(KEYGUARD);
+ assertThat(mUnderTest.getStatusBarState()).isEqualTo(SHADE);
+
+ mUnderTest.onStateChanged(SHADE_LOCKED);
+ assertThat(mUnderTest.getStatusBarState()).isEqualTo(SHADE);
+ }
+
+ @Test
+ public void testSceneContainerFlagsEnabled_isKeyguardState_alwaysFalse() {
+ when(mSceneContainerFlags.isEnabled()).thenReturn(true);
+
+ mUnderTest.onStateChanged(KEYGUARD);
+ assertThat(mUnderTest.isKeyguardState()).isFalse();
+
+ when(mStatusBarStateController.getCurrentOrUpcomingState()).thenReturn(KEYGUARD);
+ assertThat(mUnderTest.isKeyguardState()).isFalse();
+ }
+
private QSImpl instantiate() {
setupQsComponent();
setUpViews();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index da8d29c622d1..65ede89a1514 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -45,7 +45,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.media.controls.ui.MediaHost;
+import com.android.systemui.media.controls.ui.view.MediaHost;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.logging.QSLogger;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
index 1f7a02962ce2..85d7d9865c7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
@@ -10,8 +10,8 @@ import com.android.internal.logging.UiEventLogger
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.controls.ui.MediaHost
-import com.android.systemui.media.controls.ui.MediaHostState
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.qs.customize.QSCustomizerController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
index 2db79c2dc527..2c1430844d12 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
@@ -25,8 +25,8 @@ import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.controls.ui.MediaHost
-import com.android.systemui.media.controls.ui.MediaHostState
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.qs.customize.QSCustomizerController
import com.android.systemui.qs.logging.QSLogger
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
index 83f8f180ae9d..06833213671d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
@@ -30,7 +30,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
-import com.android.systemui.qs.tiles.dialog.InternetDialogFactory
+import com.android.systemui.qs.tiles.dialog.InternetDialogManager
import com.android.systemui.statusbar.connectivity.AccessPointController
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.ethernet.domain.EthernetInteractor
@@ -83,7 +83,7 @@ class InternetTileNewImplTest : SysuiTestCase() {
@Mock private lateinit var sbStateController: StatusBarStateController
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var logger: QSLogger
- @Mock private lateinit var dialogFactory: InternetDialogFactory
+ @Mock private lateinit var dialogManager: InternetDialogManager
@Mock private lateinit var accessPointController: AccessPointController
@Before
@@ -118,7 +118,7 @@ class InternetTileNewImplTest : SysuiTestCase() {
activityStarter,
logger,
viewModel,
- dialogFactory,
+ dialogManager,
accessPointController
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java
index c1f19645e4a7..288faccd2f92 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java
@@ -17,7 +17,6 @@
package com.android.systemui.qs.tiles;
import static com.google.common.truth.Truth.assertThat;
-
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -38,7 +37,7 @@ import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
-import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
+import com.android.systemui.qs.tiles.dialog.InternetDialogManager;
import com.android.systemui.statusbar.connectivity.AccessPointController;
import com.android.systemui.statusbar.connectivity.IconState;
import com.android.systemui.statusbar.connectivity.NetworkController;
@@ -63,7 +62,7 @@ public class InternetTileTest extends SysuiTestCase {
@Mock
private AccessPointController mAccessPointController;
@Mock
- private InternetDialogFactory mInternetDialogFactory;
+ private InternetDialogManager mInternetDialogManager;
@Mock
private QsEventLogger mUiEventLogger;
@@ -89,7 +88,7 @@ public class InternetTileTest extends SysuiTestCase {
mock(QSLogger.class),
mNetworkController,
mAccessPointController,
- mInternetDialogFactory
+ mInternetDialogManager
);
mTile.initialize();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
index 077ec4b7102c..74f50dff99ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
@@ -5,7 +5,6 @@ import static android.provider.Settings.Global.AIRPLANE_MODE_ON;
import static android.telephony.SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
import static android.telephony.SignalStrength.SIGNAL_STRENGTH_GREAT;
import static android.telephony.SignalStrength.SIGNAL_STRENGTH_POOR;
-
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.settingslib.wifi.WifiUtils.getHotspotIconResource;
import static com.android.systemui.qs.tiles.dialog.InternetDialogController.TOAST_PARAMS_HORIZONTAL_WEIGHT;
@@ -13,9 +12,7 @@ import static com.android.systemui.qs.tiles.dialog.InternetDialogController.TOAS
import static com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_MAX;
import static com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_MIN;
import static com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_UNREACHABLE;
-
import static com.google.common.truth.Truth.assertThat;
-
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -101,7 +98,7 @@ import java.util.Map;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-public class InternetDialogControllerTest extends SysuiTestCase {
+public class InternetDialogDelegateControllerTest extends SysuiTestCase {
private static final int SUB_ID = 1;
private static final int SUB_ID2 = 2;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java
index c9e6274e5660..db9f5cfc9ac6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java
@@ -1,9 +1,7 @@
package com.android.systemui.qs.tiles.dialog;
import static com.android.systemui.qs.tiles.dialog.InternetDialogController.MAX_WIFI_ENTRY_COUNT;
-
import static com.google.common.truth.Truth.assertThat;
-
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -34,6 +32,7 @@ import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.res.R;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -55,7 +54,7 @@ import java.util.List;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-public class InternetDialogTest extends SysuiTestCase {
+public class InternetDialogDelegateTest extends SysuiTestCase {
private static final String MOBILE_NETWORK_TITLE = "Mobile Title";
private static final String MOBILE_NETWORK_SUMMARY = "Mobile Summary";
@@ -78,9 +77,13 @@ public class InternetDialogTest extends SysuiTestCase {
private KeyguardStateController mKeyguard;
@Mock
private DialogTransitionAnimator mDialogTransitionAnimator;
+ @Mock
+ private SystemUIDialog.Factory mSystemUIDialogFactory;
+ @Mock
+ private SystemUIDialog mSystemUIDialog;
private FakeExecutor mBgExecutor = new FakeExecutor(new FakeSystemClock());
- private InternetDialog mInternetDialog;
+ private InternetDialogDelegate mInternetDialogDelegate;
private View mDialogView;
private View mSubTitle;
private LinearLayout mEthernet;
@@ -117,21 +120,30 @@ public class InternetDialogTest extends SysuiTestCase {
.spyStatic(WifiEnterpriseRestrictionUtils.class)
.startMocking();
when(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).thenReturn(true);
-
+ when(mSystemUIDialogFactory.create(any(SystemUIDialog.Delegate.class)))
+ .thenReturn(mSystemUIDialog);
createInternetDialog();
}
private void createInternetDialog() {
- mInternetDialog = new InternetDialog(mContext, mock(InternetDialogFactory.class),
- mInternetDialogController, true, true, true, mock(UiEventLogger.class),
- mDialogTransitionAnimator, mHandler,
- mBgExecutor, mKeyguard);
- mInternetDialog.mAdapter = mInternetAdapter;
- mInternetDialog.mConnectedWifiEntry = mInternetWifiEntry;
- mInternetDialog.mWifiEntriesCount = mWifiEntries.size();
- mInternetDialog.show();
-
- mDialogView = mInternetDialog.mDialogView;
+ mInternetDialogDelegate = new InternetDialogDelegate(
+ mContext,
+ mock(InternetDialogManager.class),
+ mInternetDialogController,
+ true,
+ true,
+ true,
+ mock(UiEventLogger.class),
+ mDialogTransitionAnimator,
+ mHandler,
+ mBgExecutor,
+ mKeyguard,
+ mSystemUIDialogFactory);
+ mInternetDialogDelegate.mAdapter = mInternetAdapter;
+ mInternetDialogDelegate.mConnectedWifiEntry = mInternetWifiEntry;
+ mInternetDialogDelegate.mWifiEntriesCount = mWifiEntries.size();
+
+ mDialogView = mInternetDialogDelegate.mDialogView;
mSubTitle = mDialogView.requireViewById(R.id.internet_dialog_subtitle);
mEthernet = mDialogView.requireViewById(R.id.ethernet_layout);
mMobileDataLayout = mDialogView.requireViewById(R.id.mobile_network_layout);
@@ -148,7 +160,7 @@ public class InternetDialogTest extends SysuiTestCase {
@After
public void tearDown() {
- mInternetDialog.dismissDialog();
+ mInternetDialogDelegate.dismissDialog();
mMockitoSession.finishMocking();
}
@@ -160,9 +172,9 @@ public class InternetDialogTest extends SysuiTestCase {
@Test
public void hideWifiViews_WifiViewsGone() {
- mInternetDialog.hideWifiViews();
+ mInternetDialogDelegate.hideWifiViews();
- assertThat(mInternetDialog.mIsProgressBarVisible).isFalse();
+ assertThat(mInternetDialogDelegate.mIsProgressBarVisible).isFalse();
assertThat(mWifiToggle.getVisibility()).isEqualTo(View.GONE);
assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
@@ -173,7 +185,7 @@ public class InternetDialogTest extends SysuiTestCase {
public void updateDialog_withApmOn_internetDialogSubTitleGone() {
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
assertThat(mSubTitle.getVisibility()).isEqualTo(View.VISIBLE);
}
@@ -182,7 +194,7 @@ public class InternetDialogTest extends SysuiTestCase {
public void updateDialog_withApmOff_internetDialogSubTitleVisible() {
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
assertThat(mSubTitle.getVisibility()).isEqualTo(View.VISIBLE);
}
@@ -192,7 +204,7 @@ public class InternetDialogTest extends SysuiTestCase {
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
when(mInternetDialogController.hasEthernet()).thenReturn(true);
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
assertThat(mEthernet.getVisibility()).isEqualTo(View.VISIBLE);
}
@@ -202,7 +214,7 @@ public class InternetDialogTest extends SysuiTestCase {
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
when(mInternetDialogController.hasEthernet()).thenReturn(false);
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
assertThat(mEthernet.getVisibility()).isEqualTo(View.GONE);
}
@@ -212,7 +224,7 @@ public class InternetDialogTest extends SysuiTestCase {
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
when(mInternetDialogController.hasEthernet()).thenReturn(true);
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
assertThat(mEthernet.getVisibility()).isEqualTo(View.VISIBLE);
}
@@ -222,7 +234,7 @@ public class InternetDialogTest extends SysuiTestCase {
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
when(mInternetDialogController.hasEthernet()).thenReturn(false);
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
assertThat(mEthernet.getVisibility()).isEqualTo(View.GONE);
}
@@ -234,7 +246,7 @@ public class InternetDialogTest extends SysuiTestCase {
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
when(mInternetDialogController.hasActiveSubId()).thenReturn(false);
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.GONE);
}
@@ -246,7 +258,7 @@ public class InternetDialogTest extends SysuiTestCase {
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
when(mInternetDialogController.isWifiEnabled()).thenReturn(false);
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.GONE);
@@ -255,7 +267,7 @@ public class InternetDialogTest extends SysuiTestCase {
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
when(mInternetDialogController.isWifiEnabled()).thenReturn(true);
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.VISIBLE);
}
@@ -265,7 +277,7 @@ public class InternetDialogTest extends SysuiTestCase {
when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(false);
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.GONE);
}
@@ -274,10 +286,10 @@ public class InternetDialogTest extends SysuiTestCase {
public void updateDialog_apmOnAndWifiOnHasCarrierNetwork_showAirplaneSummary() {
when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true);
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
- mInternetDialog.mConnectedWifiEntry = null;
+ mInternetDialogDelegate.mConnectedWifiEntry = null;
doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.VISIBLE);
@@ -287,10 +299,10 @@ public class InternetDialogTest extends SysuiTestCase {
public void updateDialog_apmOffAndWifiOnHasCarrierNetwork_notShowApmSummary() {
when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true);
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
- mInternetDialog.mConnectedWifiEntry = null;
+ mInternetDialogDelegate.mConnectedWifiEntry = null;
doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.GONE);
}
@@ -300,7 +312,7 @@ public class InternetDialogTest extends SysuiTestCase {
when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true);
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.GONE);
}
@@ -310,7 +322,7 @@ public class InternetDialogTest extends SysuiTestCase {
when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(false);
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.GONE);
}
@@ -322,7 +334,7 @@ public class InternetDialogTest extends SysuiTestCase {
when(mInternetDialogController.isMobileDataEnabled()).thenReturn(true);
mMobileToggleSwitch.setChecked(false);
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
assertThat(mMobileToggleSwitch.isChecked()).isTrue();
}
@@ -334,20 +346,20 @@ public class InternetDialogTest extends SysuiTestCase {
when(mInternetDialogController.isMobileDataEnabled()).thenReturn(false);
mMobileToggleSwitch.setChecked(false);
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
assertThat(mMobileToggleSwitch.isChecked()).isFalse();
}
@Test
public void updateDialog_wifiOnAndHasInternetWifi_showConnectedWifi() {
- mInternetDialog.dismissDialog();
+ mInternetDialogDelegate.dismissDialog();
doReturn(true).when(mInternetDialogController).hasActiveSubId();
createInternetDialog();
// The preconditions WiFi ON and Internet WiFi are already in setUp()
doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE);
LinearLayout secondaryLayout = mDialogView.requireViewById(
@@ -358,10 +370,10 @@ public class InternetDialogTest extends SysuiTestCase {
@Test
public void updateDialog_wifiOnAndNoConnectedWifi_hideConnectedWifi() {
// The precondition WiFi ON is already in setUp()
- mInternetDialog.mConnectedWifiEntry = null;
+ mInternetDialogDelegate.mConnectedWifiEntry = null;
doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
}
@@ -369,10 +381,10 @@ public class InternetDialogTest extends SysuiTestCase {
@Test
public void updateDialog_wifiOnAndNoWifiEntry_showWifiListAndSeeAllArea() {
// The precondition WiFi ON is already in setUp()
- mInternetDialog.mConnectedWifiEntry = null;
- mInternetDialog.mWifiEntriesCount = 0;
+ mInternetDialogDelegate.mConnectedWifiEntry = null;
+ mInternetDialogDelegate.mWifiEntriesCount = 0;
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
// Show a blank block to fix the dialog height even if there is no WiFi list
@@ -384,10 +396,10 @@ public class InternetDialogTest extends SysuiTestCase {
@Test
public void updateDialog_wifiOnAndOneWifiEntry_showWifiListAndSeeAllArea() {
// The precondition WiFi ON is already in setUp()
- mInternetDialog.mConnectedWifiEntry = null;
- mInternetDialog.mWifiEntriesCount = 1;
+ mInternetDialogDelegate.mConnectedWifiEntry = null;
+ mInternetDialogDelegate.mWifiEntriesCount = 1;
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
// Show a blank block to fix the dialog height even if there is no WiFi list
@@ -399,9 +411,9 @@ public class InternetDialogTest extends SysuiTestCase {
@Test
public void updateDialog_wifiOnAndHasConnectedWifi_showAllWifiAndSeeAllArea() {
// The preconditions WiFi ON and WiFi entries are already in setUp()
- mInternetDialog.mWifiEntriesCount = 0;
+ mInternetDialogDelegate.mWifiEntriesCount = 0;
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE);
// Show a blank block to fix the dialog height even if there is no WiFi list
@@ -413,11 +425,11 @@ public class InternetDialogTest extends SysuiTestCase {
@Test
public void updateDialog_wifiOnAndHasMaxWifiList_showWifiListAndSeeAll() {
// The preconditions WiFi ON and WiFi entries are already in setUp()
- mInternetDialog.mConnectedWifiEntry = null;
- mInternetDialog.mWifiEntriesCount = MAX_WIFI_ENTRY_COUNT;
- mInternetDialog.mHasMoreWifiEntries = true;
+ mInternetDialogDelegate.mConnectedWifiEntry = null;
+ mInternetDialogDelegate.mWifiEntriesCount = MAX_WIFI_ENTRY_COUNT;
+ mInternetDialogDelegate.mHasMoreWifiEntries = true;
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
@@ -428,10 +440,10 @@ public class InternetDialogTest extends SysuiTestCase {
@Test
public void updateDialog_wifiOnAndHasBothWifiEntry_showBothWifiEntryAndSeeAll() {
// The preconditions WiFi ON and WiFi entries are already in setUp()
- mInternetDialog.mWifiEntriesCount = MAX_WIFI_ENTRY_COUNT - 1;
- mInternetDialog.mHasMoreWifiEntries = true;
+ mInternetDialogDelegate.mWifiEntriesCount = MAX_WIFI_ENTRY_COUNT - 1;
+ mInternetDialogDelegate.mHasMoreWifiEntries = true;
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
@@ -443,9 +455,9 @@ public class InternetDialogTest extends SysuiTestCase {
public void updateDialog_deviceLockedAndNoConnectedWifi_showWifiToggle() {
// The preconditions WiFi entries are already in setUp()
when(mInternetDialogController.isDeviceLocked()).thenReturn(true);
- mInternetDialog.mConnectedWifiEntry = null;
+ mInternetDialogDelegate.mConnectedWifiEntry = null;
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
// Show WiFi Toggle without background
assertThat(mWifiToggle.getVisibility()).isEqualTo(View.VISIBLE);
@@ -461,7 +473,7 @@ public class InternetDialogTest extends SysuiTestCase {
// The preconditions WiFi ON and WiFi entries are already in setUp()
when(mInternetDialogController.isDeviceLocked()).thenReturn(true);
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
// Show WiFi Toggle with highlight background
assertThat(mWifiToggle.getVisibility()).isEqualTo(View.VISIBLE);
@@ -474,11 +486,11 @@ public class InternetDialogTest extends SysuiTestCase {
@Test
public void updateDialog_disallowChangeWifiState_disableWifiSwitch() {
- mInternetDialog.dismissDialog();
+ mInternetDialogDelegate.dismissDialog();
when(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).thenReturn(false);
createInternetDialog();
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
// Disable Wi-Fi switch and show restriction message in summary.
assertThat(mWifiToggleSwitch.isEnabled()).isFalse();
@@ -488,11 +500,11 @@ public class InternetDialogTest extends SysuiTestCase {
@Test
public void updateDialog_allowChangeWifiState_enableWifiSwitch() {
- mInternetDialog.dismissDialog();
+ mInternetDialogDelegate.dismissDialog();
when(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).thenReturn(true);
createInternetDialog();
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
// Enable Wi-Fi switch and hide restriction message in summary.
assertThat(mWifiToggleSwitch.isEnabled()).isTrue();
@@ -501,14 +513,14 @@ public class InternetDialogTest extends SysuiTestCase {
@Test
public void updateDialog_showSecondaryDataSub() {
- mInternetDialog.dismissDialog();
+ mInternetDialogDelegate.dismissDialog();
doReturn(1).when(mInternetDialogController).getActiveAutoSwitchNonDdsSubId();
doReturn(true).when(mInternetDialogController).hasActiveSubId();
doReturn(false).when(mInternetDialogController).isAirplaneModeEnabled();
createInternetDialog();
clearInvocations(mInternetDialogController);
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
LinearLayout primaryLayout = mDialogView.requireViewById(
R.id.mobile_network_layout);
@@ -523,7 +535,7 @@ public class InternetDialogTest extends SysuiTestCase {
ArgumentCaptor<AlertDialog> dialogArgumentCaptor =
ArgumentCaptor.forClass(AlertDialog.class);
verify(mDialogTransitionAnimator).showFromDialog(dialogArgumentCaptor.capture(),
- eq(mInternetDialog), eq(null), eq(false));
+ eq(mSystemUIDialog), eq(null), eq(false));
AlertDialog dialog = dialogArgumentCaptor.getValue();
dialog.show();
dialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick();
@@ -541,7 +553,7 @@ public class InternetDialogTest extends SysuiTestCase {
public void updateDialog_wifiOn_hideWifiScanNotify() {
// The preconditions WiFi ON and WiFi entries are already in setUp()
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE);
}
@@ -551,7 +563,7 @@ public class InternetDialogTest extends SysuiTestCase {
when(mInternetDialogController.isWifiEnabled()).thenReturn(false);
when(mInternetDialogController.isWifiScanEnabled()).thenReturn(false);
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE);
}
@@ -562,7 +574,7 @@ public class InternetDialogTest extends SysuiTestCase {
when(mInternetDialogController.isWifiScanEnabled()).thenReturn(true);
when(mInternetDialogController.isDeviceLocked()).thenReturn(true);
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE);
}
@@ -573,7 +585,7 @@ public class InternetDialogTest extends SysuiTestCase {
when(mInternetDialogController.isWifiScanEnabled()).thenReturn(true);
when(mInternetDialogController.isDeviceLocked()).thenReturn(false);
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.VISIBLE);
TextView wifiScanNotifyText = mDialogView.requireViewById(R.id.wifi_scan_notify_text);
@@ -586,7 +598,7 @@ public class InternetDialogTest extends SysuiTestCase {
when(mInternetDialogController.isWifiEnabled()).thenReturn(false);
mWifiToggleSwitch.setChecked(true);
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
assertThat(mWifiToggleSwitch.isChecked()).isFalse();
}
@@ -596,7 +608,7 @@ public class InternetDialogTest extends SysuiTestCase {
when(mInternetDialogController.isWifiEnabled()).thenReturn(true);
mWifiToggleSwitch.setChecked(false);
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
assertThat(mWifiToggleSwitch.isChecked()).isTrue();
}
@@ -611,20 +623,20 @@ public class InternetDialogTest extends SysuiTestCase {
@Test
public void onWifiScan_isScanTrue_setProgressBarVisibleTrue() {
- mInternetDialog.mIsProgressBarVisible = false;
+ mInternetDialogDelegate.mIsProgressBarVisible = false;
- mInternetDialog.onWifiScan(true);
+ mInternetDialogDelegate.onWifiScan(true);
- assertThat(mInternetDialog.mIsProgressBarVisible).isTrue();
+ assertThat(mInternetDialogDelegate.mIsProgressBarVisible).isTrue();
}
@Test
public void onWifiScan_isScanFalse_setProgressBarVisibleFalse() {
- mInternetDialog.mIsProgressBarVisible = true;
+ mInternetDialogDelegate.mIsProgressBarVisible = true;
- mInternetDialog.onWifiScan(false);
+ mInternetDialogDelegate.onWifiScan(false);
- assertThat(mInternetDialog.mIsProgressBarVisible).isFalse();
+ assertThat(mInternetDialogDelegate.mIsProgressBarVisible).isFalse();
}
@Test
@@ -633,42 +645,47 @@ public class InternetDialogTest extends SysuiTestCase {
// Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT.
setNetworkVisible(false, false, false);
- assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT);
+ assertThat(mInternetDialogDelegate.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT);
// If the Connected Wi-Fi is displayed then reduce one of the Wi-Fi list max count.
setNetworkVisible(false, false, true);
- assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
+ assertThat(mInternetDialogDelegate.getWifiListMaxCount())
+ .isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
// Only one of Ethernet, MobileData is displayed.
// Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT.
setNetworkVisible(true, false, false);
- assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT);
+ assertThat(mInternetDialogDelegate.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT);
setNetworkVisible(false, true, false);
- assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT);
+ assertThat(mInternetDialogDelegate.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT);
// If the Connected Wi-Fi is displayed then reduce one of the Wi-Fi list max count.
setNetworkVisible(true, false, true);
- assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
+ assertThat(mInternetDialogDelegate.getWifiListMaxCount())
+ .isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
setNetworkVisible(false, true, true);
- assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
+ assertThat(mInternetDialogDelegate.getWifiListMaxCount())
+ .isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
// Both of Ethernet, MobileData, ConnectedWiFi is displayed.
// Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT - 1.
setNetworkVisible(true, true, false);
- assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
+ assertThat(mInternetDialogDelegate.getWifiListMaxCount())
+ .isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
// If the Connected Wi-Fi is displayed then reduce one of the Wi-Fi list max count.
setNetworkVisible(true, true, true);
- assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 2);
+ assertThat(mInternetDialogDelegate.getWifiListMaxCount())
+ .isEqualTo(MAX_WIFI_ENTRY_COUNT - 2);
}
@Test
@@ -676,9 +693,9 @@ public class InternetDialogTest extends SysuiTestCase {
when(mInternetDialogController.getConfiguratorQrCodeGeneratorIntentOrNull(any()))
.thenReturn(null);
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
- assertThat(mInternetDialog.mShareWifiButton.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mInternetDialogDelegate.mShareWifiButton.getVisibility()).isEqualTo(View.GONE);
}
@Test
@@ -686,9 +703,10 @@ public class InternetDialogTest extends SysuiTestCase {
when(mInternetDialogController.getConfiguratorQrCodeGeneratorIntentOrNull(any()))
.thenReturn(new Intent());
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
- assertThat(mInternetDialog.mShareWifiButton.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mInternetDialogDelegate.mShareWifiButton.getVisibility())
+ .isEqualTo(View.VISIBLE);
}
private void setNetworkVisible(boolean ethernetVisible, boolean mobileDataVisible,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt
new file mode 100644
index 000000000000..0c324706857f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.screenshot
+
+import android.content.Intent
+import android.os.Process.myUserHandle
+import android.platform.test.annotations.EnableFlags
+import android.testing.AndroidTestingRunner
+import android.testing.TestableContext
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.settings.DisplayTracker
+import com.android.systemui.shared.system.ActivityManagerWrapper
+import com.android.systemui.statusbar.phone.CentralSurfaces
+import com.android.systemui.util.mockito.mock
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestCoroutineScheduler
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.verify
+
+@RunWith(AndroidTestingRunner::class)
+class ActionIntentExecutorTest : SysuiTestCase() {
+
+ private val scheduler = TestCoroutineScheduler()
+ private val mainDispatcher = StandardTestDispatcher(scheduler)
+ private val testScope = TestScope(mainDispatcher)
+ private val testableContext = TestableContext(mContext)
+
+ private val activityManagerWrapper = mock<ActivityManagerWrapper>()
+ private val displayTracker = mock<DisplayTracker>()
+ private val keyguardController = mock<ScreenshotKeyguardController>()
+
+ private val actionIntentExecutor =
+ ActionIntentExecutor(
+ testableContext,
+ activityManagerWrapper,
+ testScope,
+ mainDispatcher,
+ displayTracker,
+ keyguardController,
+ )
+
+ @Test
+ @EnableFlags(Flags.FLAG_SCREENSHOT_ACTION_DISMISS_SYSTEM_WINDOWS)
+ fun launchIntent_callsCloseSystemWindows() =
+ testScope.runTest {
+ val intent = Intent(Intent.ACTION_EDIT).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK }
+ val userHandle = myUserHandle()
+
+ actionIntentExecutor.launchIntent(intent, null, userHandle, false)
+ scheduler.advanceUntilIdle()
+
+ verify(activityManagerWrapper)
+ .closeSystemWindows(CentralSurfaces.SYSTEM_DIALOG_REASON_SCREENSHOT)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt
index 091531e435e4..2f911fffe335 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt
@@ -73,6 +73,17 @@ class ScreenshotSoundControllerTest : SysuiTestCase() {
}
@Test
+ fun playCameraSound_illegalStateException_doesNotThrow() = runTest {
+ whenever(mediaPlayer.start()).thenThrow(IllegalStateException())
+
+ val controller = createController()
+ controller.playCameraSound().await()
+
+ verify(mediaPlayer).start()
+ verify(mediaPlayer).release()
+ }
+
+ @Test
fun playCameraSound_soundLoadingSuccessful_mediaPlayerReleases() = runTest {
val controller = createController()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
index 032ec7440923..774aa517672e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
@@ -371,7 +371,6 @@ class UserTrackerImplTest : SysuiTestCase() {
val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
- captor.value.onBeforeUserSwitching(newID)
captor.value.onUserSwitching(newID, userSwitchingReply)
assertThat(callback.calledOnUserChanging).isEqualTo(0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
index 1dc5f7dbf6fe..665fc750c742 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -26,17 +26,20 @@ import android.view.View
import android.view.WindowManager
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.data.repository.FakeCommunalRepository
import com.android.systemui.communal.data.repository.fakeCommunalRepository
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.setCommunalAvailable
import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.compose.ComposeFacade
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.testKosmos
@@ -45,6 +48,7 @@ import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.launch
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import org.junit.After
import org.junit.Assert.assertThrows
@@ -114,6 +118,14 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() {
BOTTOM_SWIPE_REGION_WIDTH
)
+ // Make communal available so that communalInteractor.desiredScene accurately reflects
+ // scene changes instead of just returning Blank.
+ mSetFlagsRule.enableFlags(Flags.FLAG_COMMUNAL_HUB)
+ with(kosmos.testScope) {
+ launch { kosmos.setCommunalAvailable(true) }
+ testScheduler.runCurrent()
+ }
+
initAndAttachContainerView()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index cc27cbd9d809..950a9dbc2ff3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -96,6 +96,7 @@ import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardViewConfigurator;
+import com.android.systemui.keyguard.data.repository.FakeKeyguardClockRepository;
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor;
@@ -103,7 +104,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.domain.interactor.NaturalScrollingSettingObserver;
-import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl;
+import com.android.systemui.keyguard.ui.view.KeyguardRootView;
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingLockscreenHostedTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
@@ -114,9 +115,9 @@ import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransition
import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
import com.android.systemui.kosmos.KosmosJavaAdapter;
-import com.android.systemui.media.controls.pipeline.MediaDataManager;
-import com.android.systemui.media.controls.ui.KeyguardMediaController;
-import com.android.systemui.media.controls.ui.MediaHierarchyManager;
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.ui.controller.KeyguardMediaController;
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationModeController;
@@ -281,6 +282,8 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
@Mock protected UiEventLogger mUiEventLogger;
@Mock protected LockIconViewController mLockIconViewController;
@Mock protected KeyguardViewConfigurator mKeyguardViewConfigurator;
+ @Mock protected KeyguardRootView mKeyguardRootView;
+ @Mock protected View mKeyguardRootViewChild;
@Mock protected KeyguardMediaController mKeyguardMediaController;
@Mock protected NavigationModeController mNavigationModeController;
@Mock protected NavigationBarController mNavigationBarController;
@@ -352,6 +355,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
protected KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor;
protected KeyguardClockInteractor mKeyguardClockInteractor;
protected FakeKeyguardRepository mFakeKeyguardRepository;
+ protected FakeKeyguardClockRepository mFakeKeyguardClockRepository;
protected KeyguardInteractor mKeyguardInteractor;
protected ShadeAnimationInteractor mShadeAnimationInteractor;
protected KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
@@ -388,15 +392,17 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
mFeatureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false);
mFeatureFlags.set(Flags.QS_USER_DETAIL_SHORTCUT, false);
- mSetFlagsRule.disableFlags(KeyguardShadeMigrationNssl.FLAG_NAME);
mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR);
mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
+ mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
mMainDispatcher = getMainDispatcher();
KeyguardInteractorFactory.WithDependencies keyguardInteractorDeps =
KeyguardInteractorFactory.create();
mFakeKeyguardRepository = keyguardInteractorDeps.getRepository();
mKeyguardBottomAreaInteractor = new KeyguardBottomAreaInteractor(mFakeKeyguardRepository);
+ mFakeKeyguardClockRepository = new FakeKeyguardClockRepository();
+ mKeyguardClockInteractor = new KeyguardClockInteractor(mFakeKeyguardClockRepository);
mKeyguardInteractor = keyguardInteractorDeps.getKeyguardInteractor();
mShadeRepository = new FakeShadeRepository();
mShadeAnimationInteractor = new ShadeAnimationInteractorLegacyImpl(
@@ -433,8 +439,13 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
)
);
SystemClock systemClock = new FakeSystemClock();
- mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger,
- mInteractionJankMonitor, mJavaAdapter, () -> mShadeInteractor);
+ mStatusBarStateController = new StatusBarStateControllerImpl(
+ mUiEventLogger,
+ mInteractionJankMonitor,
+ mJavaAdapter,
+ () -> mShadeInteractor,
+ () -> mKosmos.getDeviceUnlockedInteractor(),
+ () -> mKosmos.getSceneInteractor());
KeyguardStatusView keyguardStatusView = new KeyguardStatusView(mContext);
keyguardStatusView.setId(R.id.keyguard_status_view);
@@ -597,9 +608,13 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
new NotificationWakeUpCoordinator(
mDumpManager,
mock(HeadsUpManager.class),
- new StatusBarStateControllerImpl(new UiEventLoggerFake(),
+ new StatusBarStateControllerImpl(
+ new UiEventLoggerFake(),
mInteractionJankMonitor,
- mJavaAdapter, () -> mShadeInteractor),
+ mJavaAdapter,
+ () -> mShadeInteractor,
+ () -> mKosmos.getDeviceUnlockedInteractor(),
+ () -> mKosmos.getSceneInteractor()),
mKeyguardBypassController,
mDozeParameters,
mScreenOffAnimationController,
@@ -837,13 +852,13 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
when(mNotificationStackScrollLayoutController.getBottom()).thenReturn(stackBottom);
when(mLockIconViewController.getTop()).thenReturn((float) (stackBottom - lockIconPadding));
+ when(mKeyguardRootViewChild.getTop()).thenReturn((int) (stackBottom - lockIconPadding));
+ when(mKeyguardRootView.findViewById(anyInt())).thenReturn(mKeyguardRootViewChild);
+ when(mKeyguardViewConfigurator.getKeyguardRootView()).thenReturn(mKeyguardRootView);
+
when(mResources.getDimensionPixelSize(R.dimen.keyguard_indication_bottom_padding))
.thenReturn(indicationPadding);
mNotificationPanelViewController.loadDimens();
-
- mNotificationPanelViewController.setAmbientIndicationTop(
- /* ambientIndicationTop= */ stackBottom - ambientPadding,
- /* ambientTextVisible= */ true);
}
protected void triggerPositionClockAndNotifications() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 2e8d46a83e1c..d24fe1b16ef9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -57,7 +57,6 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.DejankUtils;
import com.android.systemui.flags.Flags;
-import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.res.R;
@@ -204,6 +203,10 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo
when(mNotificationStackScrollLayoutController.getShelfHeight()).thenReturn(5);
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
.isEqualTo(5);
+
+ mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
+ assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
+ .isEqualTo(5);
}
@Test
@@ -217,6 +220,10 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo
when(mNotificationStackScrollLayoutController.getShelfHeight()).thenReturn(5);
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
.isEqualTo(0);
+
+ mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
+ assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
+ .isEqualTo(0);
}
@Test
@@ -230,6 +237,10 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo
when(mNotificationStackScrollLayoutController.getShelfHeight()).thenReturn(5);
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
.isEqualTo(0);
+
+ mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
+ assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
+ .isEqualTo(0);
}
@Test
@@ -243,6 +254,10 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo
when(mNotificationStackScrollLayoutController.getShelfHeight()).thenReturn(5);
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
.isEqualTo(2);
+
+ mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
+ assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
+ .isEqualTo(2);
}
@Test
@@ -256,6 +271,10 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo
when(mNotificationStackScrollLayoutController.getShelfHeight()).thenReturn(5);
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
.isEqualTo(0);
+
+ mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
+ assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
+ .isEqualTo(0);
}
@Test
@@ -363,7 +382,7 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo
@Test
public void onInterceptTouchEvent_nsslMigrationOff_userActivity() {
- mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL);
+ mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
mTouchHandler.onInterceptTouchEvent(MotionEvent.obtain(0L /* downTime */,
0L /* eventTime */, MotionEvent.ACTION_DOWN, 0f /* x */, 0f /* y */,
@@ -374,7 +393,7 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo
@Test
public void onInterceptTouchEvent_nsslMigrationOn_userActivity_not_called() {
- mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL);
+ mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
mTouchHandler.onInterceptTouchEvent(MotionEvent.obtain(0L /* downTime */,
0L /* eventTime */, MotionEvent.ACTION_DOWN, 0f /* x */, 0f /* y */,
@@ -1125,7 +1144,7 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo
@Test
public void nsslFlagEnabled_allowOnlyExternalTouches() {
- mSetFlagsRule.enableFlags(KeyguardShadeMigrationNssl.FLAG_NAME);
+ mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
// This sets the dozing state that is read when onMiddleClicked is eventually invoked.
mTouchHandler.onTouch(mock(View.class), mDownMotionEvent);
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 61fee16f0431..3808d309b02b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -180,7 +180,6 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
mTestScope.getBackgroundScope(),
mKosmos.getFakeSceneContainerConfig(),
mKosmos.getSceneDataSource()),
- powerInteractor,
mock(SceneLogger.class),
mKosmos.getDeviceUnlockedInteractor());
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 248ed249c213..b426d1de0b00 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -44,7 +44,6 @@ import com.android.systemui.flags.Flags.TRACKPAD_GESTURE_FEATURES
import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
import com.android.systemui.res.R
@@ -378,7 +377,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
@Test
fun handleDispatchTouchEvent_nsslMigrationOff_userActivity_not_called() {
- mSetFlagsRule.disableFlags(Flags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL)
+ mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
underTest.setStatusBarViewController(phoneStatusBarViewController)
interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)
@@ -388,7 +387,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
@Test
fun handleDispatchTouchEvent_nsslMigrationOn_userActivity() {
- mSetFlagsRule.enableFlags(Flags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL)
+ mSetFlagsRule.enableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
underTest.setStatusBarViewController(phoneStatusBarViewController)
interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)
@@ -430,7 +429,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
// AND the lock icon wants the touch
whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(true)
- mSetFlagsRule.enableFlags(KeyguardShadeMigrationNssl.FLAG_NAME)
+ mSetFlagsRule.enableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
// THEN touch should NOT be intercepted by NotificationShade
assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isFalse()
@@ -449,9 +448,9 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any()))
.thenReturn(false)
- mSetFlagsRule.enableFlags(KeyguardShadeMigrationNssl.FLAG_NAME)
+ mSetFlagsRule.enableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- // THEN touch should NOT be intercepted by NotificationShade
+ // THEN touch should be intercepted by NotificationShade
assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue()
}
@@ -468,9 +467,37 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any()))
.thenReturn(true)
- mSetFlagsRule.enableFlags(KeyguardShadeMigrationNssl.FLAG_NAME)
+ mSetFlagsRule.enableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- // THEN touch should NOT be intercepted by NotificationShade
+ // THEN touch should be intercepted by NotificationShade
+ assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue()
+ }
+
+ @Test
+ fun shouldInterceptTouchEvent_dozingAndPulsing_touchIntercepted() {
+ // GIVEN dozing
+ whenever(sysuiStatusBarStateController.isDozing).thenReturn(true)
+ // AND pulsing
+ whenever(dozeServiceHost.isPulsing()).thenReturn(true)
+ // AND status bar doesn't want it
+ whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT))
+ .thenReturn(false)
+ // AND shade is not fully expanded
+ whenever(notificationPanelViewController.isFullyExpanded()).thenReturn(false)
+ // AND the lock icon does NOT want the touch
+ whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(false)
+ // AND quick settings controller DOES want it
+ whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any()))
+ .thenReturn(true)
+ // AND bouncer is not showing
+ whenever(centralSurfaces.isBouncerShowing()).thenReturn(false)
+ // AND panel view controller wants it
+ whenever(notificationPanelViewController.handleExternalInterceptTouch(DOWN_EVENT))
+ .thenReturn(true)
+
+ mSetFlagsRule.enableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
+
+ // THEN touch should be intercepted by NotificationShade
assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue()
}
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 ab5e51c1b1a1..59fe813cf18e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -207,7 +207,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() {
@Test
fun testDragDownHelperCalledWhenDraggingDown() =
testScope.runTest {
- mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL)
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
whenever(dragDownHelper.isDraggingDown).thenReturn(true)
val now = SystemClock.elapsedRealtime()
val ev = MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, 0f, 0f, 0 /* meta */)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
index 4cc123407d83..81d0e06df1fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
@@ -27,7 +27,7 @@ import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
import com.android.systemui.Flags as AConfigFlags
-import com.android.systemui.Flags.FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR
+import com.android.systemui.Flags.FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX
import com.android.systemui.SysuiTestCase
import com.android.systemui.fragments.FragmentHostManager
import com.android.systemui.fragments.FragmentService
@@ -167,7 +167,7 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
fun testLargeScreen_updateResources_refactorFlagOff_splitShadeHeightIsSetBasedOnResource() {
val headerResourceHeight = 20
val headerHelperHeight = 30
- mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight())
.thenReturn(headerHelperHeight)
overrideResource(R.bool.config_use_large_screen_shade_header, true)
@@ -190,7 +190,7 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
fun testLargeScreen_updateResources_refactorFlagOn_splitShadeHeightIsSetBasedOnHelper() {
val headerResourceHeight = 20
val headerHelperHeight = 30
- mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight())
.thenReturn(headerHelperHeight)
overrideResource(R.bool.config_use_large_screen_shade_header, true)
@@ -401,7 +401,7 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
@Test
fun testSplitShadeLayout_isAlignedToGuideline() {
- mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL)
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
enableSplitShade()
underTest.updateResources()
assertThat(getConstraintSetLayout(R.id.qs_frame).endToEnd).isEqualTo(R.id.qs_edge_guideline)
@@ -411,7 +411,7 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
@Test
fun testSinglePaneLayout_childrenHaveEqualMargins() {
- mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL)
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
disableSplitShade()
underTest.updateResources()
val qsStartMargin = getConstraintSetLayout(R.id.qs_frame).startMargin
@@ -428,7 +428,7 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
@Test
fun testSplitShadeLayout_childrenHaveInsideMarginsOfZero() {
- mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL)
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
enableSplitShade()
underTest.updateResources()
assertThat(getConstraintSetLayout(R.id.qs_frame).endMargin).isEqualTo(0)
@@ -446,8 +446,8 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
@Test
fun testLargeScreenLayout_refactorFlagOff_qsAndNotifsTopMarginIsOfHeaderHeightResource() {
- mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
- mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL)
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
+ mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
setLargeScreen()
val largeScreenHeaderResourceHeight = 100
val largeScreenHeaderHelperHeight = 200
@@ -469,8 +469,8 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
@Test
fun testLargeScreenLayout_refactorFlagOn_qsAndNotifsTopMarginIsOfHeaderHeightHelper() {
- mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
- mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL)
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
+ mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
setLargeScreen()
val largeScreenHeaderResourceHeight = 100
val largeScreenHeaderHelperHeight = 200
@@ -492,7 +492,7 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
@Test
fun testSmallScreenLayout_qsAndNotifsTopMarginIsZero() {
- mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL)
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
setSmallScreen()
underTest.updateResources()
assertThat(getConstraintSetLayout(R.id.qs_frame).topMargin).isEqualTo(0)
@@ -513,7 +513,7 @@ class NotificationsQSContainerControllerLegacyTest : SysuiTestCase() {
@Test
fun testSinglePaneShadeLayout_isAlignedToParent() {
- mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL)
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
disableSplitShade()
underTest.updateResources()
assertThat(getConstraintSetLayout(R.id.qs_frame).endToEnd)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
index c32635020ddc..4ae751b4e7eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
@@ -26,11 +26,10 @@ import androidx.annotation.IdRes
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR
+import com.android.systemui.Flags.FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX
import com.android.systemui.SysuiTestCase
import com.android.systemui.fragments.FragmentHostManager
import com.android.systemui.fragments.FragmentService
-import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl
import com.android.systemui.navigationbar.NavigationModeController
import com.android.systemui.navigationbar.NavigationModeController.ModeChangedListener
import com.android.systemui.plugins.qs.QS
@@ -100,7 +99,7 @@ class NotificationsQSContainerControllerTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this)
fakeSystemClock = FakeSystemClock()
delayableExecutor = FakeExecutor(fakeSystemClock)
- mSetFlagsRule.enableFlags(KeyguardShadeMigrationNssl.FLAG_NAME)
+ mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
mContext.ensureTestableResources()
whenever(view.context).thenReturn(mContext)
whenever(view.resources).thenReturn(mContext.resources)
@@ -163,7 +162,7 @@ class NotificationsQSContainerControllerTest : SysuiTestCase() {
@Test
fun testLargeScreen_updateResources_refactorFlagOff_splitShadeHeightIsSet_basedOnResource() {
- mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
val helperHeight = 30
val resourceHeight = 20
whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(helperHeight)
@@ -184,7 +183,7 @@ class NotificationsQSContainerControllerTest : SysuiTestCase() {
@Test
fun testLargeScreen_updateResources_refactorFlagOn_splitShadeHeightIsSet_basedOnHelper() {
- mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
val helperHeight = 30
val resourceHeight = 20
whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(helperHeight)
@@ -426,7 +425,7 @@ class NotificationsQSContainerControllerTest : SysuiTestCase() {
@Test
fun testLargeScreenLayout_refactorFlagOff_qsAndNotifsTopMarginIsOfHeaderResourceHeight() {
- mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
setLargeScreen()
val largeScreenHeaderHelperHeight = 200
val largeScreenHeaderResourceHeight = 100
@@ -446,7 +445,7 @@ class NotificationsQSContainerControllerTest : SysuiTestCase() {
@Test
fun testLargeScreenLayout_refactorFlagOn_qsAndNotifsTopMarginIsOfHeaderHelperHeight() {
- mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
setLargeScreen()
val largeScreenHeaderHelperHeight = 200
val largeScreenHeaderResourceHeight = 100
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 061f88e8a592..0b49a954e848 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -54,8 +54,8 @@ import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransit
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.kosmos.KosmosJavaAdapter;
-import com.android.systemui.media.controls.pipeline.MediaDataManager;
-import com.android.systemui.media.controls.ui.MediaHierarchyManager;
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.power.domain.interactor.PowerInteractor;
@@ -208,7 +208,6 @@ public class QuickSettingsControllerBaseTest extends SysuiTestCase {
mTestScope.getBackgroundScope(),
mKosmos.getFakeSceneContainerConfig(),
mKosmos.getSceneDataSource()),
- powerInteractor,
mock(SceneLogger.class),
mKosmos.getDeviceUnlockedInteractor());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
index cc79ca4efaa1..f48993754e63 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
@@ -80,7 +80,7 @@ class ShadeControllerImplTest : SysuiTestCase() {
@Mock private lateinit var windowManager: WindowManager
@Mock private lateinit var assistManager: AssistManager
@Mock private lateinit var gutsManager: NotificationGutsManager
- @Mock private lateinit var shadeViewController: ShadeViewController
+ @Mock private lateinit var npvc: NotificationPanelViewController
@Mock private lateinit var nswvc: NotificationShadeWindowViewController
@Mock private lateinit var display: Display
@Mock private lateinit var touchLog: LogBuffer
@@ -120,7 +120,7 @@ class ShadeControllerImplTest : SysuiTestCase() {
deviceProvisionedController,
notificationShadeWindowController,
windowManager,
- Lazy { shadeViewController },
+ Lazy { npvc },
Lazy { assistManager },
Lazy { gutsManager },
)
@@ -134,9 +134,9 @@ class ShadeControllerImplTest : SysuiTestCase() {
// Trying to open it does nothing.
shadeController.animateExpandShade()
- verify(shadeViewController, never()).expandToNotifications()
+ verify(npvc, never()).expandToNotifications()
shadeController.animateExpandQs()
- verify(shadeViewController, never()).expand(ArgumentMatchers.anyBoolean())
+ verify(npvc, never()).expand(ArgumentMatchers.anyBoolean())
}
@Test
@@ -145,15 +145,15 @@ class ShadeControllerImplTest : SysuiTestCase() {
// Can now be opened.
shadeController.animateExpandShade()
- verify(shadeViewController).expandToNotifications()
+ verify(npvc).expandToNotifications()
shadeController.animateExpandQs()
- verify(shadeViewController).expandToQs()
+ verify(npvc).expandToQs()
}
@Test
fun cancelExpansionAndCollapseShade_callsCancelCurrentTouch() {
// GIVEN the shade is tracking a touch
- whenever(shadeViewController.isTracking).thenReturn(true)
+ whenever(npvc.isTracking).thenReturn(true)
// WHEN cancelExpansionAndCollapseShade is called
shadeController.cancelExpansionAndCollapseShade()
@@ -165,7 +165,7 @@ class ShadeControllerImplTest : SysuiTestCase() {
@Test
fun cancelExpansionAndCollapseShade_doesNotCallAnimateCollapseShade_whenCollapsed() {
// GIVEN the shade is tracking a touch
- whenever(shadeViewController.isTracking).thenReturn(false)
+ whenever(npvc.isTracking).thenReturn(false)
// WHEN cancelExpansionAndCollapseShade is called
shadeController.cancelExpansionAndCollapseShade()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index 64fd80d72d3f..74d017375eb5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -35,6 +35,7 @@ import com.android.systemui.plugins.PluginListener
import com.android.systemui.plugins.PluginManager
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.ThreadAssert
import java.util.function.BiConsumer
import junit.framework.Assert.assertEquals
import junit.framework.Assert.fail
@@ -69,6 +70,7 @@ class ClockRegistryTest : SysuiTestCase() {
@Mock private lateinit var mockDefaultClock: ClockController
@Mock private lateinit var mockThumbnail: Drawable
@Mock private lateinit var mockContentResolver: ContentResolver
+ @Mock private lateinit var mockThreadAssert: ThreadAssert
private lateinit var fakeDefaultProvider: FakeClockPlugin
private lateinit var pluginListener: PluginListener<ClockProviderPlugin>
private lateinit var registry: ClockRegistry
@@ -163,14 +165,12 @@ class ClockRegistryTest : SysuiTestCase() {
defaultClockProvider = fakeDefaultProvider,
keepAllLoaded = false,
subTag = "Test",
+ assert = mockThreadAssert,
) {
override fun querySettings() { }
override fun applySettings(value: ClockSettings?) {
settings = value
}
- // Unit Test does not validate threading
- override fun assertMainThread() {}
- override fun assertNotMainThread() {}
}
registry.registerListeners()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt
index ffde6015c127..9ec9b69d44c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt
@@ -25,9 +25,9 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.keyguard.domain.interactor.NaturalScrollingSettingObserver
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.statusbar.notification.row.ExpandableView
import com.android.systemui.util.mockito.mock
-import com.android.systemui.shade.data.repository.FakeShadeRepository
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -58,12 +58,11 @@ class DragDownHelperTest : SysuiTestCase() {
whenever(naturalScrollingSettingObserver.isNaturalScrollingEnabled).thenReturn(true)
dragDownHelper = DragDownHelper(
- falsingManager,
- falsingCollector,
- dragDownloadCallback,
- naturalScrollingSettingObserver,
- FakeShadeRepository(),
- mContext,
+ falsingManager,
+ dragDownloadCallback,
+ naturalScrollingSettingObserver,
+ FakeShadeRepository(),
+ mContext,
).also {
it.expandCallback = expandCallback
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerTest.kt
index 0b4de345e2d7..402d9aab66bd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerTest.kt
@@ -18,12 +18,13 @@ package com.android.systemui.statusbar
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.qs.QS
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.FakeConfigurationController
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
+import com.android.systemui.util.mockito.mock
import com.google.common.truth.Expect
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -43,13 +44,15 @@ class LockscreenShadeQsTransitionControllerTest : SysuiTestCase() {
@get:Rule val expect: Expect = Expect.create()
@Mock private lateinit var dumpManager: DumpManager
- @Mock private lateinit var qS: QS
+ private var qS: QS? = null
private lateinit var controller: LockscreenShadeQsTransitionController
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+ qS = mock()
+
setTransitionDistance(TRANSITION_DISTANCE)
setTransitionDelay(TRANSITION_DELAY)
setSquishTransitionDistance(SQUISH_TRANSITION_DISTANCE)
@@ -220,7 +223,7 @@ class LockscreenShadeQsTransitionControllerTest : SysuiTestCase() {
controller.dragDownAmount = rawDragAmount
- verify(qS)
+ verify(qS!!)
.setTransitionToFullShadeProgress(
/* isTransitioningToFullShade= */ true,
/* transitionFraction= */ controller.qsTransitionFraction,
@@ -228,6 +231,15 @@ class LockscreenShadeQsTransitionControllerTest : SysuiTestCase() {
)
}
+ @Test
+ fun nullQS_onDragAmountChanged_doesNotCrash() {
+ qS = null
+
+ val rawDragAmount = 200f
+
+ controller.dragDownAmount = rawDragAmount
+ }
+
private fun setTransitionDistance(value: Int) {
overrideResource(R.dimen.lockscreen_shade_qs_transition_distance, value)
configurationController.notifyConfigurationChanged()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 3efcf7b7c26b..86116a073d9e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -5,21 +5,22 @@ import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
-import com.android.systemui.SysUITestModule
-import com.android.systemui.TestMocksModule
import com.android.systemui.ExpandHelper
+import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FakeFeatureFlagsClassicModule
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.NaturalScrollingSettingObserver
-import com.android.systemui.media.controls.ui.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
import com.android.systemui.plugins.qs.QS
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
import com.android.systemui.res.R
-import com.android.systemui.shade.ShadeViewController
+import com.android.systemui.shade.ShadeLockscreenInteractor
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
@@ -60,8 +61,8 @@ import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
+import org.mockito.Mockito.`when` as whenever
private fun <T> anyObject(): T {
return Mockito.anyObject<T>()
@@ -82,6 +83,8 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
private val testScope
get() = testComponent.testScope
+ private val qsSceneAdapter = FakeQSSceneAdapter({ mock() })
+
lateinit var row: ExpandableNotificationRow
@Mock lateinit var centralSurfaces: CentralSurfaces
@@ -94,7 +97,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
@Mock lateinit var qS: QS
@Mock lateinit var qsTransitionController: LockscreenShadeQsTransitionController
@Mock lateinit var scrimController: ScrimController
- @Mock lateinit var shadeViewController: ShadeViewController
+ @Mock lateinit var shadeLockscreenInteractor: ShadeLockscreenInteractor
@Mock lateinit var singleShadeOverScroller: SingleShadeLockScreenOverScroller
@Mock lateinit var splitShadeOverScroller: SplitShadeLockScreenOverScroller
@Mock lateinit var stackscroller: NotificationStackScrollLayout
@@ -167,7 +170,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
keyguardTransitionControllerFactory = { notificationPanelController ->
LockscreenShadeKeyguardTransitionController(
mediaHierarchyManager = mediaHierarchyManager,
- notificationPanelController = notificationPanelController,
+ shadeLockscreenInteractor = notificationPanelController,
context = context,
configurationController = configurationController,
dumpManager = mock(),
@@ -186,13 +189,13 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
qsTransitionControllerFactory = { qsTransitionController },
shadeRepository = testComponent.shadeRepository,
shadeInteractor = testComponent.shadeInteractor,
- powerInteractor = testComponent.powerInteractor,
splitShadeStateController = ResourcesSplitShadeStateController(),
+ shadeLockscreenInteractorLazy = {shadeLockscreenInteractor},
naturalScrollingSettingObserver = naturalScrollingSettingObserver,
+ lazyQSSceneAdapter = { qsSceneAdapter }
)
transitionController.addCallback(transitionControllerCallback)
- transitionController.shadeViewController = shadeViewController
transitionController.centralSurfaces = centralSurfaces
transitionController.qS = qS
transitionController.setStackScroller(nsslController)
@@ -286,7 +289,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
fun testGoToLockedShadeCreatesQSAnimation() {
transitionController.goToLockedShade(null)
verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
- verify(shadeViewController).transitionToExpandedShade(anyLong())
+ verify(shadeLockscreenInteractor).transitionToExpandedShade(anyLong())
assertNotNull(transitionController.dragDownAnimator)
}
@@ -294,7 +297,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
fun testGoToLockedShadeDoesntCreateQSAnimation() {
transitionController.goToLockedShade(null, needsQSAnimation = false)
verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
- verify(shadeViewController).transitionToExpandedShade(anyLong())
+ verify(shadeLockscreenInteractor).transitionToExpandedShade(anyLong())
assertNull(transitionController.dragDownAnimator)
}
@@ -302,7 +305,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
fun testGoToLockedShadeAlwaysCreatesQSAnimationInSplitShade() {
enableSplitShade()
transitionController.goToLockedShade(null, needsQSAnimation = true)
- verify(shadeViewController).transitionToExpandedShade(anyLong())
+ verify(shadeLockscreenInteractor).transitionToExpandedShade(anyLong())
assertNotNull(transitionController.dragDownAnimator)
}
@@ -358,7 +361,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
fun setDragAmount_setsKeyguardTransitionProgress() {
transitionController.dragDownAmount = 10f
- verify(shadeViewController).setKeyguardTransitionProgress(anyFloat(), anyInt())
+ verify(shadeLockscreenInteractor).setKeyguardTransitionProgress(anyFloat(), anyInt())
}
@Test
@@ -370,7 +373,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
transitionController.dragDownAmount = 10f
val expectedAlpha = 1 - 10f / alphaDistance
- verify(shadeViewController).setKeyguardTransitionProgress(eq(expectedAlpha), anyInt())
+ verify(shadeLockscreenInteractor).setKeyguardTransitionProgress(eq(expectedAlpha), anyInt())
}
@Test
@@ -383,7 +386,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
transitionController.dragDownAmount = 10f
- verify(shadeViewController).setKeyguardTransitionProgress(anyFloat(), eq(0))
+ verify(shadeLockscreenInteractor).setKeyguardTransitionProgress(anyFloat(), eq(0))
}
@Test
@@ -396,7 +399,8 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
transitionController.dragDownAmount = 10f
- verify(shadeViewController).setKeyguardTransitionProgress(anyFloat(), eq(mediaTranslationY))
+ verify(shadeLockscreenInteractor)
+ .setKeyguardTransitionProgress(anyFloat(), eq(mediaTranslationY))
}
@Test
@@ -416,7 +420,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
R.dimen.lockscreen_shade_keyguard_transition_vertical_offset
)
val expectedTranslation = 10f / distance * offset
- verify(shadeViewController)
+ verify(shadeLockscreenInteractor)
.setKeyguardTransitionProgress(anyFloat(), eq(expectedTranslation.toInt()))
}
@@ -555,7 +559,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
transitionController.dragDownAmount = dragDownAmount
val expectedAlpha = 1 - dragDownAmount / alphaDistance
- verify(shadeViewController).setKeyguardStatusBarAlpha(expectedAlpha)
+ verify(shadeLockscreenInteractor).setKeyguardStatusBarAlpha(expectedAlpha)
}
@Test
@@ -564,7 +568,17 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
transitionController.dragDownAmount = 10f
- verify(shadeViewController).setKeyguardStatusBarAlpha(-1f)
+ verify(shadeLockscreenInteractor).setKeyguardStatusBarAlpha(-1f)
+ }
+
+ @Test
+ fun nullQs_canDragDownFromAdapter() {
+ transitionController.qS = null
+
+ qsSceneAdapter.isQsFullyCollapsed = true
+ assertTrue("Can't drag down on keyguard", transitionController.canDragDown())
+ qsSceneAdapter.isQsFullyCollapsed = false
+ assertFalse("Can drag down when QS is expanded", transitionController.canDragDown())
}
private fun enableSplitShade() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt
index 81d5c4d52b74..700fb1ec332c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt
@@ -9,6 +9,7 @@ import com.android.systemui.plugins.qs.QS
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.phone.ScrimController
import com.android.systemui.statusbar.policy.FakeConfigurationController
+import com.android.systemui.util.mockito.mock
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -31,7 +32,7 @@ class SplitShadeLockScreenOverScrollerTest : SysuiTestCase() {
@Mock private lateinit var scrimController: ScrimController
@Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
- @Mock private lateinit var qS: QS
+ private var qS: QS? = null
@Mock private lateinit var nsslController: NotificationStackScrollLayoutController
@Mock private lateinit var dumpManager: DumpManager
@@ -40,6 +41,7 @@ class SplitShadeLockScreenOverScrollerTest : SysuiTestCase() {
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+ qS = mock()
whenever(nsslController.height).thenReturn(1800)
@@ -92,7 +94,7 @@ class SplitShadeLockScreenOverScrollerTest : SysuiTestCase() {
setDragAmount(1000f)
whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE)
setDragAmount(999f)
- reset(qS, scrimController, nsslController)
+ reset(qS!!, scrimController, nsslController)
setDragAmount(998f)
setDragAmount(997f)
@@ -100,8 +102,15 @@ class SplitShadeLockScreenOverScrollerTest : SysuiTestCase() {
verifyNoMoreOverScrollChanges()
}
+ @Test
+ fun qsNull_applyOverscroll_doesNotCrash() {
+ qS = null
+
+ setDragAmount(100f)
+ }
+
private fun verifyOverScrollPerformed() {
- verify(qS).setOverScrollAmount(intThat { it > 0 })
+ verify(qS!!).setOverScrollAmount(intThat { it > 0 })
verify(scrimController).setNotificationsOverScrollAmount(intThat { it > 0 })
verify(nsslController).setOverScrollAmount(intThat { it > 0 })
}
@@ -109,7 +118,7 @@ class SplitShadeLockScreenOverScrollerTest : SysuiTestCase() {
private fun verifyOverScrollResetToZero() {
// Might be more than once as the animator might have multiple values close to zero that
// round down to zero.
- verify(qS, atLeast(1)).setOverScrollAmount(0)
+ verify(qS!!, atLeast(1)).setOverScrollAmount(0)
verify(scrimController, atLeast(1)).setNotificationsOverScrollAmount(0)
verify(nsslController, atLeast(1)).setOverScrollAmount(0)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
index f178046b665a..b6ee46ddafeb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
@@ -52,8 +52,8 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.ContrastColorUtil;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.NotificationContentDescription;
import org.junit.Before;
@@ -153,7 +153,7 @@ public class StatusBarIconViewTest extends SysuiTestCase {
Icon icon = Icon.createWithBitmap(bitmap);
StatusBarIcon largeIcon = new StatusBarIcon(UserHandle.ALL, "mockPackage",
icon, 0, 0, "");
- mIconView.setNotification(mock(StatusBarNotification.class));
+ mIconView.setNotification(getMockSbn());
mIconView.getIcon(largeIcon);
// no crash? good
@@ -196,7 +196,7 @@ public class StatusBarIconViewTest extends SysuiTestCase {
// the icon view layout size would be 60x150
// (the height is always 150 due to TEST_STATUS_BAR_HEIGHT)
setUpIconView(dpIconSize, dpDrawingSize, dpIconSize);
- mIconView.setNotification(mock(StatusBarNotification.class));
+ mIconView.setNotification(getMockSbn());
// the raw drawable size is 50x50. When put the drawable into iconView whose
// layout size is 60x150, the drawable size would not be constrained and thus keep 50x50
setIconDrawableWithSize(/* width= */ 50, /* height= */ 50);
@@ -215,7 +215,7 @@ public class StatusBarIconViewTest extends SysuiTestCase {
// the icon view layout size would be 60x150
// (the height is always 150 due to TEST_STATUS_BAR_HEIGHT)
setUpIconView(dpIconSize, dpDrawingSize, dpIconSize);
- mIconView.setNotification(mock(StatusBarNotification.class));
+ mIconView.setNotification(getMockSbn());
// the raw drawable size is 50x100. When put the drawable into iconView whose
// layout size is 60x150, the drawable size would not be constrained and thus keep 50x100
setIconDrawableWithSize(/* width= */ 50, /* height= */ 100);
@@ -235,7 +235,7 @@ public class StatusBarIconViewTest extends SysuiTestCase {
// the icon view layout size would be 60x150
// (the height is always 150 due to TEST_STATUS_BAR_HEIGHT)
setUpIconView(dpIconSize, dpDrawingSize, dpIconSize);
- mIconView.setNotification(mock(StatusBarNotification.class));
+ mIconView.setNotification(getMockSbn());
// the raw drawable size is 100x50. When put the drawable into iconView whose
// layout size is 60x150, the drawable size would be constrained to 60x30
setIconDrawableWithSize(/* width= */ 100, /* height= */ 50);
@@ -257,7 +257,7 @@ public class StatusBarIconViewTest extends SysuiTestCase {
// the icon view layout size would be 40x150
// (the height is always 150 due to TEST_STATUS_BAR_HEIGHT)
setUpIconView(dpIconSize, dpDrawingSize, spIconSize);
- mIconView.setNotification(mock(StatusBarNotification.class));
+ mIconView.setNotification(getMockSbn());
// the raw drawable size is 50x50. When put the drawable into iconView whose
// layout size is 40x150, the drawable size would be constrained to 40x40
setIconDrawableWithSize(/* width= */ 50, /* height= */ 50);
@@ -283,7 +283,7 @@ public class StatusBarIconViewTest extends SysuiTestCase {
// the icon view layout size would be 40x150
// (the height is always 150 due to TEST_STATUS_BAR_HEIGHT)
setUpIconView(dpIconSize, dpDrawingSize, spIconSize);
- mIconView.setNotification(mock(StatusBarNotification.class));
+ mIconView.setNotification(getMockSbn());
// the raw drawable size is 70x70. When put the drawable into iconView whose
// layout size is 40x150, the drawable size would be constrained to 40x40
setIconDrawableWithSize(/* width= */ 70, /* height= */ 70);
@@ -310,7 +310,7 @@ public class StatusBarIconViewTest extends SysuiTestCase {
// the icon view layout size would be 40x150
// (the height is always 150 due to TEST_STATUS_BAR_HEIGHT)
setUpIconView(dpIconSize, dpDrawingSize, spIconSize);
- mIconView.setNotification(mock(StatusBarNotification.class));
+ mIconView.setNotification(getMockSbn());
// the raw drawable size is 50x100. When put the drawable into iconView whose
// layout size is 40x150, the drawable size would be constrained to 40x80
setIconDrawableWithSize(/* width= */ 50, /* height= */ 100);
@@ -334,7 +334,7 @@ public class StatusBarIconViewTest extends SysuiTestCase {
// the icon view layout size would be 80x150
// (the height is always 150 due to TEST_STATUS_BAR_HEIGHT)
setUpIconView(dpIconSize, dpDrawingSize, spIconSize);
- mIconView.setNotification(mock(StatusBarNotification.class));
+ mIconView.setNotification(getMockSbn());
// the raw drawable size is 50x50. When put the drawable into iconView whose
// layout size is 80x150, the drawable size would not be constrained and thus keep 50x50
setIconDrawableWithSize(/* width= */ 50, /* height= */ 50);
@@ -357,7 +357,7 @@ public class StatusBarIconViewTest extends SysuiTestCase {
// the icon view layout size would be 80x150
// (the height is always 150 due to TEST_STATUS_BAR_HEIGHT)
setUpIconView(dpIconSize, dpDrawingSize, spIconSize);
- mIconView.setNotification(mock(StatusBarNotification.class));
+ mIconView.setNotification(getMockSbn());
// the raw drawable size is 50x100. When put the drawable into iconView whose
// layout size is 80x150, the drawable size would not be constrained and thus keep 50x100
setIconDrawableWithSize(/* width= */ 50, /* height= */ 100);
@@ -381,7 +381,7 @@ public class StatusBarIconViewTest extends SysuiTestCase {
// the icon view layout size would be 80x150
// (the height is always 150 due to TEST_STATUS_BAR_HEIGHT)
setUpIconView(dpIconSize, dpDrawingSize, spIconSize);
- mIconView.setNotification(mock(StatusBarNotification.class));
+ mIconView.setNotification(getMockSbn());
// the raw drawable size is 100x50. When put the drawable into iconView whose
// layout size is 80x150, the drawable size would not be constrained and thus keep 80x40
setIconDrawableWithSize(/* width= */ 100, /* height= */ 50);
@@ -397,6 +397,12 @@ public class StatusBarIconViewTest extends SysuiTestCase {
mIconView.getIconScale(), 0.01f);
}
+ private static StatusBarNotification getMockSbn() {
+ StatusBarNotification sbn = mock(StatusBarNotification.class);
+ when(sbn.getNotification()).thenReturn(mock(Notification.class));
+ return sbn;
+ }
+
/**
* Setup iconView dimens for testing. The result icon view layout width would
* be spIconSize and height would be 150.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
index 1396a430df61..fe16347fa298 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -14,20 +14,30 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.statusbar
import android.animation.ObjectAnimator
+import android.platform.test.annotations.DisableFlags
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
import com.android.systemui.SysuiTestCase
+import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
+import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.FakeCommandQueue
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
@@ -38,13 +48,13 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.fromLockscreenTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.fromPrimaryBouncerTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
-import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.data.repository.FakePowerRepository
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
+import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.shade.LargeScreenHeaderHelper
import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -56,8 +66,13 @@ import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository
import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
import com.android.systemui.testKosmos
+import com.android.systemui.util.kotlin.JavaAdapter
import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -71,8 +86,8 @@ import org.mockito.ArgumentMatchers.eq
import org.mockito.Mockito
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -81,7 +96,6 @@ class StatusBarStateControllerImplTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val testDispatcher = kosmos.testDispatcher
private lateinit var shadeInteractor: ShadeInteractor
private lateinit var fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor
private lateinit var fromPrimaryBouncerTransitionInteractor:
@@ -91,7 +105,7 @@ class StatusBarStateControllerImplTest : SysuiTestCase() {
private val deviceEntryUdfpsInteractor = mock<DeviceEntryUdfpsInteractor>()
private val largeScreenHeaderHelper = mock<LargeScreenHeaderHelper>()
- private lateinit var controller: StatusBarStateControllerImpl
+ private lateinit var underTest: StatusBarStateControllerImpl
private lateinit var uiEventLogger: UiEventLoggerFake
@Before
@@ -101,13 +115,15 @@ class StatusBarStateControllerImplTest : SysuiTestCase() {
whenever(interactionJankMonitor.end(anyInt())).thenReturn(true)
uiEventLogger = UiEventLoggerFake()
- controller =
+ underTest =
object :
StatusBarStateControllerImpl(
uiEventLogger,
interactionJankMonitor,
- mock(),
- { shadeInteractor }
+ JavaAdapter(testScope.backgroundScope),
+ { shadeInteractor },
+ { kosmos.deviceUnlockedInteractor },
+ { kosmos.sceneInteractor },
) {
override fun createDarkAnimator(): ObjectAnimator {
return mockDarkAnimator
@@ -115,7 +131,7 @@ class StatusBarStateControllerImplTest : SysuiTestCase() {
}
val powerInteractor =
- PowerInteractor(FakePowerRepository(), FalsingCollectorFake(), mock(), controller)
+ PowerInteractor(FakePowerRepository(), FalsingCollectorFake(), mock(), underTest)
val keyguardRepository = FakeKeyguardRepository()
val keyguardTransitionRepository = FakeKeyguardTransitionRepository()
val featureFlags = FakeFeatureFlagsClassic()
@@ -168,11 +184,12 @@ class StatusBarStateControllerImplTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_SCENE_CONTAINER)
fun testChangeState_logged() {
TestableLooper.get(this).runWithLooper {
- controller.state = StatusBarState.KEYGUARD
- controller.state = StatusBarState.SHADE
- controller.state = StatusBarState.SHADE_LOCKED
+ underTest.state = StatusBarState.KEYGUARD
+ underTest.state = StatusBarState.SHADE
+ underTest.state = StatusBarState.SHADE_LOCKED
}
val logs = uiEventLogger.logs
@@ -186,90 +203,199 @@ class StatusBarStateControllerImplTest : SysuiTestCase() {
@Test
fun testSetDozeAmountInternal_onlySetsOnce() {
val listener = mock(StatusBarStateController.StateListener::class.java)
- controller.addCallback(listener)
+ underTest.addCallback(listener)
- controller.setAndInstrumentDozeAmount(null, 0.5f, false /* animated */)
- controller.setAndInstrumentDozeAmount(null, 0.5f, false /* animated */)
+ underTest.setAndInstrumentDozeAmount(null, 0.5f, false /* animated */)
+ underTest.setAndInstrumentDozeAmount(null, 0.5f, false /* animated */)
verify(listener).onDozeAmountChanged(eq(0.5f), anyFloat())
}
@Test
+ @DisableFlags(FLAG_SCENE_CONTAINER)
fun testSetState_appliesState_sameStateButDifferentUpcomingState() {
- controller.state = StatusBarState.SHADE
- controller.setUpcomingState(StatusBarState.KEYGUARD)
+ underTest.state = StatusBarState.SHADE
+ underTest.setUpcomingState(StatusBarState.KEYGUARD)
- assertEquals(controller.state, StatusBarState.SHADE)
+ assertEquals(underTest.state, StatusBarState.SHADE)
// We should return true (state change was applied) despite going from SHADE to SHADE, since
// the upcoming state was set to KEYGUARD.
- assertTrue(controller.setState(StatusBarState.SHADE))
+ assertTrue(underTest.setState(StatusBarState.SHADE))
}
@Test
+ @DisableFlags(FLAG_SCENE_CONTAINER)
fun testSetState_appliesState_differentStateEqualToUpcomingState() {
- controller.state = StatusBarState.SHADE
- controller.setUpcomingState(StatusBarState.KEYGUARD)
+ underTest.state = StatusBarState.SHADE
+ underTest.setUpcomingState(StatusBarState.KEYGUARD)
- assertEquals(controller.state, StatusBarState.SHADE)
+ assertEquals(underTest.state, StatusBarState.SHADE)
// Make sure we apply a SHADE -> KEYGUARD state change when the upcoming state is KEYGUARD.
- assertTrue(controller.setState(StatusBarState.KEYGUARD))
+ assertTrue(underTest.setState(StatusBarState.KEYGUARD))
}
@Test
+ @DisableFlags(FLAG_SCENE_CONTAINER)
fun testSetState_doesNotApplyState_currentAndUpcomingStatesSame() {
- controller.state = StatusBarState.SHADE
- controller.setUpcomingState(StatusBarState.SHADE)
+ underTest.state = StatusBarState.SHADE
+ underTest.setUpcomingState(StatusBarState.SHADE)
- assertEquals(controller.state, StatusBarState.SHADE)
+ assertEquals(underTest.state, StatusBarState.SHADE)
// We're going from SHADE -> SHADE, and the upcoming state is also SHADE, this should not do
// anything.
- assertFalse(controller.setState(StatusBarState.SHADE))
+ assertFalse(underTest.setState(StatusBarState.SHADE))
// Double check that we can still force it to happen.
- assertTrue(controller.setState(StatusBarState.SHADE, true /* force */))
+ assertTrue(underTest.setState(StatusBarState.SHADE, true /* force */))
}
@Test
fun testSetDozeAmount_immediatelyChangesDozeAmount_lockscreenTransitionFromAod() {
// Put controller in AOD state
- controller.setAndInstrumentDozeAmount(null, 1f, false)
+ underTest.setAndInstrumentDozeAmount(null, 1f, false)
// When waking from doze, CentralSurfaces#updateDozingState will update the dozing state
// before the doze amount changes
- controller.setIsDozing(false)
+ underTest.setIsDozing(false)
// Animate the doze amount to 0f, as would normally happen
- controller.setAndInstrumentDozeAmount(null, 0f, true)
+ underTest.setAndInstrumentDozeAmount(null, 0f, true)
// Check that the doze amount is immediately set to a value slightly less than 1f. This is
// to ensure that any scrim implementation changes its opacity immediately rather than
// waiting an extra frame. Waiting an extra frame will cause a relayout (which is expensive)
// and cause us to drop a frame during the LOCKSCREEN_TRANSITION_FROM_AOD CUJ.
- assertEquals(0.99f, controller.dozeAmount, 0.009f)
+ assertEquals(0.99f, underTest.dozeAmount, 0.009f)
}
@Test
fun testSetDreamState_invokesCallback() {
val listener = mock(StatusBarStateController.StateListener::class.java)
- controller.addCallback(listener)
+ underTest.addCallback(listener)
- controller.setIsDreaming(true)
+ underTest.setIsDreaming(true)
verify(listener).onDreamingChanged(true)
Mockito.clearInvocations(listener)
- controller.setIsDreaming(false)
+ underTest.setIsDreaming(false)
verify(listener).onDreamingChanged(false)
}
@Test
fun testSetDreamState_getterReturnsCurrentState() {
- controller.setIsDreaming(true)
- assertTrue(controller.isDreaming())
+ underTest.setIsDreaming(true)
+ assertTrue(underTest.isDreaming())
- controller.setIsDreaming(false)
- assertFalse(controller.isDreaming())
+ underTest.setIsDreaming(false)
+ assertFalse(underTest.isDreaming())
}
+
+ @Test
+ @EnableSceneContainer
+ fun start_hydratesStatusBarState_whileLocked() =
+ testScope.runTest {
+ var statusBarState = underTest.state
+ val listener =
+ object : StatusBarStateController.StateListener {
+ override fun onStateChanged(newState: Int) {
+ statusBarState = newState
+ }
+ }
+ underTest.addCallback(listener)
+
+ val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
+ kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+ AuthenticationMethodModel.Password
+ )
+ kosmos.fakeDeviceEntryRepository.setUnlocked(false)
+ runCurrent()
+ kosmos.sceneInteractor.changeScene(
+ toScene = SceneKey.Lockscreen,
+ loggingReason = "reason"
+ )
+ runCurrent()
+ assertThat(kosmos.deviceUnlockedInteractor.isDeviceUnlocked.value).isFalse()
+ assertThat(currentScene).isEqualTo(SceneKey.Lockscreen)
+
+ // Call start to begin hydrating based on the scene framework:
+ underTest.start()
+
+ kosmos.sceneInteractor.changeScene(toScene = SceneKey.Bouncer, loggingReason = "reason")
+ runCurrent()
+ assertThat(currentScene).isEqualTo(SceneKey.Bouncer)
+ assertThat(statusBarState).isEqualTo(StatusBarState.KEYGUARD)
+
+ kosmos.sceneInteractor.changeScene(toScene = SceneKey.Shade, loggingReason = "reason")
+ runCurrent()
+ assertThat(currentScene).isEqualTo(SceneKey.Shade)
+ assertThat(statusBarState).isEqualTo(StatusBarState.SHADE_LOCKED)
+
+ kosmos.sceneInteractor.changeScene(
+ toScene = SceneKey.QuickSettings,
+ loggingReason = "reason"
+ )
+ runCurrent()
+ assertThat(currentScene).isEqualTo(SceneKey.QuickSettings)
+ assertThat(statusBarState).isEqualTo(StatusBarState.SHADE_LOCKED)
+
+ kosmos.sceneInteractor.changeScene(
+ toScene = SceneKey.Communal,
+ loggingReason = "reason"
+ )
+ runCurrent()
+ assertThat(currentScene).isEqualTo(SceneKey.Communal)
+ assertThat(statusBarState).isEqualTo(StatusBarState.KEYGUARD)
+
+ kosmos.sceneInteractor.changeScene(
+ toScene = SceneKey.Lockscreen,
+ loggingReason = "reason"
+ )
+ runCurrent()
+ assertThat(currentScene).isEqualTo(SceneKey.Lockscreen)
+ assertThat(statusBarState).isEqualTo(StatusBarState.KEYGUARD)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun start_hydratesStatusBarState_whileUnlocked() =
+ testScope.runTest {
+ var statusBarState = underTest.state
+ val listener =
+ object : StatusBarStateController.StateListener {
+ override fun onStateChanged(newState: Int) {
+ statusBarState = newState
+ }
+ }
+ underTest.addCallback(listener)
+
+ val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
+ kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+ AuthenticationMethodModel.Password
+ )
+ kosmos.fakeDeviceEntryRepository.setUnlocked(true)
+ runCurrent()
+ kosmos.sceneInteractor.changeScene(toScene = SceneKey.Gone, loggingReason = "reason")
+ runCurrent()
+ assertThat(kosmos.deviceUnlockedInteractor.isDeviceUnlocked.value).isTrue()
+ assertThat(currentScene).isEqualTo(SceneKey.Gone)
+
+ // Call start to begin hydrating based on the scene framework:
+ underTest.start()
+
+ kosmos.sceneInteractor.changeScene(toScene = SceneKey.Shade, loggingReason = "reason")
+ runCurrent()
+ assertThat(currentScene).isEqualTo(SceneKey.Shade)
+ assertThat(statusBarState).isEqualTo(StatusBarState.SHADE)
+
+ kosmos.sceneInteractor.changeScene(
+ toScene = SceneKey.QuickSettings,
+ loggingReason = "reason"
+ )
+ runCurrent()
+ assertThat(currentScene).isEqualTo(SceneKey.QuickSettings)
+ assertThat(statusBarState).isEqualTo(StatusBarState.SHADE)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
index ccc9dc0d9618..8a48fe10d7fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
@@ -50,8 +50,8 @@ import android.testing.AndroidTestingRunner;
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.statusbar.RankingBuilder;
import com.android.systemui.statusbar.SbnBuilder;
import com.android.systemui.util.time.FakeSystemClock;
@@ -280,6 +280,66 @@ public class NotificationEntryTest extends SysuiTestCase {
}
@Test
+ public void testIsNotificationVisibilityPrivate_true() {
+ assertTrue(mEntry.isNotificationVisibilityPrivate());
+ }
+
+ @Test
+ public void testIsNotificationVisibilityPrivate_visibilityPublic_false() {
+ Notification.Builder notification = new Notification.Builder(mContext, "")
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
+ .setSmallIcon(R.drawable.ic_person)
+ .setContentTitle("Title")
+ .setContentText("Text");
+
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setPkg(TEST_PACKAGE_NAME)
+ .setOpPkg(TEST_PACKAGE_NAME)
+ .setUid(TEST_UID)
+ .setChannel(mChannel)
+ .setId(mId++)
+ .setNotification(notification.build())
+ .setUser(new UserHandle(ActivityManager.getCurrentUser()))
+ .build();
+
+ assertFalse(entry.isNotificationVisibilityPrivate());
+ }
+
+ @Test
+ public void testIsChannelVisibilityPrivate_true() {
+ assertTrue(mEntry.isChannelVisibilityPrivate());
+ }
+
+ @Test
+ public void testIsChannelVisibilityPrivate_visibilityPublic_false() {
+ NotificationChannel channel =
+ new NotificationChannel("id", "name", NotificationChannel.USER_LOCKED_IMPORTANCE);
+ channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
+ StatusBarNotification sbn = new SbnBuilder().build();
+ Ranking ranking = new RankingBuilder()
+ .setChannel(channel)
+ .setKey(sbn.getKey())
+ .build();
+ NotificationEntry entry =
+ new NotificationEntry(sbn, ranking, mClock.uptimeMillis());
+
+ assertFalse(entry.isChannelVisibilityPrivate());
+ }
+
+ @Test
+ public void testIsChannelVisibilityPrivate_entryHasNoChannel_false() {
+ StatusBarNotification sbn = new SbnBuilder().build();
+ Ranking ranking = new RankingBuilder()
+ .setChannel(null)
+ .setKey(sbn.getKey())
+ .build();
+ NotificationEntry entry =
+ new NotificationEntry(sbn, ranking, mClock.uptimeMillis());
+
+ assertFalse(entry.isChannelVisibilityPrivate());
+ }
+
+ @Test
public void notificationDataEntry_testIsLastMessageFromReply() {
Person.Builder person = new Person.Builder()
.setName("name")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java
index 590c902ba687..b548117684ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
@@ -28,12 +29,15 @@ import static org.mockito.Mockito.when;
import android.app.Notification.MediaStyle;
import android.media.session.MediaSession;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.service.notification.NotificationListenerService;
import android.testing.AndroidTestingRunner;
import androidx.test.filters.SmallTest;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.media.controls.util.MediaFeatureFlag;
import com.android.systemui.statusbar.notification.InflationException;
@@ -153,7 +157,8 @@ public final class MediaCoordinatorTest extends SysuiTestCase {
}
@Test
- public void inflateMediaNotificationIconsMediaEnabled() throws InflationException {
+ @DisableFlags(Flags.FLAG_NOTIFICATIONS_BACKGROUND_MEDIA_ICONS)
+ public void inflateMediaNotificationIconsMediaEnabled_old() throws InflationException {
finishSetupWithMediaFeatureFlagEnabled(true);
mListener.onEntryInit(mMediaEntry);
@@ -181,7 +186,37 @@ public final class MediaCoordinatorTest extends SysuiTestCase {
}
@Test
- public void inflationException() throws InflationException {
+ @EnableFlags(Flags.FLAG_NOTIFICATIONS_BACKGROUND_MEDIA_ICONS)
+ public void inflateMediaNotificationIconsMediaEnabled_new() throws InflationException {
+ finishSetupWithMediaFeatureFlagEnabled(true);
+
+ mListener.onEntryInit(mMediaEntry);
+ mListener.onEntryAdded(mMediaEntry);
+ verify(mIconManager).createIcons(eq(mMediaEntry));
+ verify(mIconManager, never()).updateIcons(eq(mMediaEntry));
+ clearInvocations(mIconManager);
+
+ mFilter.shouldFilterOut(mMediaEntry, 0);
+ verify(mIconManager, never()).createIcons(eq(mMediaEntry));
+ verify(mIconManager, never()).updateIcons(eq(mMediaEntry));
+
+ mListener.onEntryUpdated(mMediaEntry);
+ verify(mIconManager, never()).createIcons(eq(mMediaEntry));
+ verify(mIconManager).updateIcons(eq(mMediaEntry));
+
+ mListener.onEntryRemoved(mMediaEntry, NotificationListenerService.REASON_CANCEL);
+ mListener.onEntryCleanUp(mMediaEntry);
+ clearInvocations(mIconManager);
+
+ mListener.onEntryInit(mMediaEntry);
+ mListener.onEntryAdded(mMediaEntry);
+ verify(mIconManager).createIcons(eq(mMediaEntry));
+ verify(mIconManager, never()).updateIcons(eq(mMediaEntry));
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_NOTIFICATIONS_BACKGROUND_MEDIA_ICONS)
+ public void inflationException_old() throws InflationException {
finishSetupWithMediaFeatureFlagEnabled(true);
mListener.onEntryInit(mMediaEntry);
@@ -208,6 +243,31 @@ public final class MediaCoordinatorTest extends SysuiTestCase {
verify(mIconManager, never()).updateIcons(eq(mMediaEntry));
}
+ @Test
+ @EnableFlags(Flags.FLAG_NOTIFICATIONS_BACKGROUND_MEDIA_ICONS)
+ public void inflationException_new() throws InflationException {
+ finishSetupWithMediaFeatureFlagEnabled(true);
+
+ doThrow(InflationException.class).when(mIconManager).createIcons(eq(mMediaEntry));
+
+ mListener.onEntryInit(mMediaEntry);
+ mListener.onEntryAdded(mMediaEntry);
+ verify(mIconManager).createIcons(eq(mMediaEntry));
+ verify(mIconManager, never()).updateIcons(eq(mMediaEntry));
+ clearInvocations(mIconManager);
+
+ mListener.onEntryUpdated(mMediaEntry);
+ verify(mIconManager).createIcons(eq(mMediaEntry));
+ verify(mIconManager, never()).updateIcons(eq(mMediaEntry));
+ clearInvocations(mIconManager);
+
+ doNothing().when(mIconManager).createIcons(eq(mMediaEntry));
+
+ mListener.onEntryUpdated(mMediaEntry);
+ verify(mIconManager).createIcons(eq(mMediaEntry));
+ verify(mIconManager, never()).updateIcons(eq(mMediaEntry));
+ }
+
private void finishSetupWithMediaFeatureFlagEnabled(boolean mediaFeatureFlagEnabled) {
when(mMediaFeatureFlag.getEnabled()).thenReturn(mediaFeatureFlagEnabled);
mCoordinator = new MediaCoordinator(mMediaFeatureFlag, mStatusBarService, mIconManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAlertTimeCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAlertTimeCoordinatorTest.kt
new file mode 100644
index 000000000000..7daadb07f89a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAlertTimeCoordinatorTest.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.notification.collection.coordinator
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderEntryListener
+import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener
+import com.android.systemui.statusbar.notification.collection.render.NotifRowController
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.withArgCaptor
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations.initMocks
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class RowAlertTimeCoordinatorTest : SysuiTestCase() {
+ private lateinit var coordinator: RowAlertTimeCoordinator
+ private lateinit var beforeFinalizeFilterListener: OnBeforeFinalizeFilterListener
+ private lateinit var afterRenderEntryListener: OnAfterRenderEntryListener
+
+ @Mock private lateinit var pipeline: NotifPipeline
+
+ @Before
+ fun setUp() {
+ initMocks(this)
+ coordinator = RowAlertTimeCoordinator()
+ coordinator.attach(pipeline)
+ beforeFinalizeFilterListener = withArgCaptor {
+ verify(pipeline).addOnBeforeFinalizeFilterListener(capture())
+ }
+ afterRenderEntryListener = withArgCaptor {
+ verify(pipeline).addOnAfterRenderEntryListener(capture())
+ }
+ }
+
+ @Test
+ fun testSetLastAudiblyAlerted() {
+ val entry1 = NotificationEntryBuilder().setLastAudiblyAlertedMs(10).build()
+ val entry2 = NotificationEntryBuilder().setLastAudiblyAlertedMs(20).build()
+ val summary = NotificationEntryBuilder().setLastAudiblyAlertedMs(5).build()
+ val child1 = NotificationEntryBuilder().setLastAudiblyAlertedMs(0).build()
+ val child2 = NotificationEntryBuilder().setLastAudiblyAlertedMs(8).build()
+ val group =
+ GroupEntryBuilder()
+ .setKey("group")
+ .setSummary(summary)
+ .addChild(child1)
+ .addChild(child2)
+ .build()
+
+ val entries = listOf(entry1, summary, child1, child2, entry2)
+
+ beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry1, group, entry2))
+ val actualTimesSet =
+ entries.associateWith {
+ val rowController = mock<NotifRowController>()
+ afterRenderEntryListener.onAfterRenderEntry(it, rowController)
+ withArgCaptor<Long> {
+ verify(rowController).setLastAudibleMs(capture())
+ verifyNoMoreInteractions(rowController)
+ }
+ }
+ val expectedTimesSet =
+ mapOf(
+ entry1 to 10L,
+ entry2 to 20L,
+ summary to 8L,
+ child1 to 0L,
+ child2 to 8L,
+ )
+ assertThat(actualTimesSet).containsExactlyEntriesIn(expectedTimesSet)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt
index 3f3de009fb04..a66f8ce1a92c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt
@@ -76,7 +76,7 @@ class RowAppearanceCoordinatorTest : SysuiTestCase() {
verify(pipeline).addOnAfterRenderEntryListener(capture())
}
whenever(assistantFeedbackController.getFeedbackIcon(any())).thenReturn(FeedbackIcon(1, 2))
- entry1 = NotificationEntryBuilder().setSection(section1).setLastAudiblyAlertedMs(17).build()
+ entry1 = NotificationEntryBuilder().setSection(section1).build()
entry2 = NotificationEntryBuilder().setSection(section2).build()
}
@@ -103,12 +103,6 @@ class RowAppearanceCoordinatorTest : SysuiTestCase() {
}
@Test
- fun testSetLastAudiblyAlerted() {
- afterRenderEntryListener.onAfterRenderEntry(entry1, controller1)
- verify(controller1).setLastAudiblyAlertedMs(eq(17.toLong()))
- }
-
- @Test
fun testSetFeedbackIcon() {
afterRenderEntryListener.onAfterRenderEntry(entry1, controller1)
verify(controller1).setFeedbackIcon(eq(FeedbackIcon(1, 2)))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt
index 0830191fe035..b1d2ea21f7fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt
@@ -23,6 +23,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener
+import com.android.systemui.statusbar.notification.ColorUpdateLogger
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
@@ -57,6 +58,7 @@ class ViewConfigCoordinatorTest : SysuiTestCase() {
private val lockscreenUserManager: NotificationLockscreenUserManager = mock()
private val gutsManager: NotificationGutsManager = mock()
private val keyguardUpdateMonitor: KeyguardUpdateMonitor = mock()
+ private val colorUpdateLogger: ColorUpdateLogger = mock()
@Before
fun setUp() {
@@ -66,7 +68,9 @@ class ViewConfigCoordinatorTest : SysuiTestCase() {
configurationController,
lockscreenUserManager,
gutsManager,
- keyguardUpdateMonitor)
+ keyguardUpdateMonitor,
+ colorUpdateLogger,
+ )
coordinator.attach(pipeline)
userChangedListener = withArgCaptor {
verify(lockscreenUserManager).addUserChangedListener(capture())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt
index 8b99811e3d5f..a12806b9cc99 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt
@@ -167,6 +167,20 @@ class IconManagerTest : SysuiTestCase() {
}
@Test
+ fun testUpdateIcons_sensitiveImportantConversation() {
+ val entry =
+ notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = false)
+ entry?.setSensitive(true, true)
+ entry?.channel?.isImportantConversation = true
+ entry?.let { iconManager.createIcons(it) }
+ // Updating the icons after creation shouldn't break anything
+ entry?.let { iconManager.updateIcons(it) }
+ assertThat(entry?.icons?.statusBarIcon?.sourceIcon).isEqualTo(shortcutIc)
+ assertThat(entry?.icons?.shelfIcon?.sourceIcon).isEqualTo(smallIc)
+ assertThat(entry?.icons?.aodIcon?.sourceIcon).isEqualTo(smallIc)
+ }
+
+ @Test
fun testUpdateIcons_sensitivityChange() {
val entry =
notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
index 3811f04a365d..06410cd3530e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
@@ -431,28 +431,6 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
@Test
public void publicMode_settingsDisallow() {
- mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true);
- // GIVEN an 'unfiltered-keyguard-showing' state
- setupUnfilteredState(mEntry);
-
- // WHEN the notification's user is in public mode and settings are configured to disallow
- // notifications in public mode
- when(mLockscreenUserManager.isLockscreenPublicMode(NOTIF_USER_ID)).thenReturn(true);
- when(mLockscreenUserManager.userAllowsNotificationsInPublic(NOTIF_USER_ID))
- .thenReturn(false);
-
- mEntry.setRanking(new RankingBuilder()
- .setChannel(new NotificationChannel("1", "1", 4))
- .setVisibilityOverride(VISIBILITY_NO_OVERRIDE)
- .setKey(mEntry.getKey()).build());
-
- // THEN filter out the entry
- assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry));
- }
-
- @Test
- public void publicMode_settingsDisallow_mainThread() {
- mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, false);
// GIVEN an 'unfiltered-keyguard-showing' state
setupUnfilteredState(mEntry);
@@ -473,7 +451,6 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
@Test
public void publicMode_nullChannel_allowed() {
- mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true);
// GIVEN an 'unfiltered-keyguard-showing' state
setupUnfilteredState(mEntry);
@@ -490,7 +467,6 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
@Test
public void publicMode_notifDisallowed() {
- mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true);
NotificationChannel channel = new NotificationChannel("1", "1", IMPORTANCE_HIGH);
channel.setLockscreenVisibility(VISIBILITY_SECRET);
// GIVEN an 'unfiltered-keyguard-showing' state
@@ -509,23 +485,6 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
}
@Test
- public void publicMode_notifDisallowed_mainThread() {
- mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, false);
- // GIVEN an 'unfiltered-keyguard-showing' state
- setupUnfilteredState(mEntry);
-
- // WHEN the notification's user is in public mode and settings are configured to disallow
- // notifications in public mode
- when(mLockscreenUserManager.isLockscreenPublicMode(CURR_USER_ID)).thenReturn(true);
- mEntry.setRanking(new RankingBuilder()
- .setKey(mEntry.getKey())
- .setVisibilityOverride(VISIBILITY_SECRET).build());
-
- // THEN filter out the entry
- assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry));
- }
-
- @Test
public void doesNotExceedThresholdToShow() {
// GIVEN an 'unfiltered-keyguard-showing' state
setupUnfilteredState(mEntry);
@@ -579,7 +538,6 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
@Test
public void notificationChannelVisibilityNoOverride() {
- mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true);
// GIVEN a VISIBILITY_PRIVATE notification
NotificationEntryBuilder entryBuilder = new NotificationEntryBuilder()
.setUser(new UserHandle(NOTIF_USER_ID));
@@ -602,7 +560,6 @@ public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
@Test
public void notificationChannelVisibilitySecret() {
- mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true);
// GIVEN a VISIBILITY_PRIVATE notification
NotificationEntryBuilder entryBuilder = new NotificationEntryBuilder()
.setUser(new UserHandle(NOTIF_USER_ID));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
index 8ac2a334818c..210b1a7f22f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
@@ -34,6 +34,7 @@ import com.android.systemui.plugins.PluginManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.SbnBuilder
import com.android.systemui.statusbar.SmartReplyController
+import com.android.systemui.statusbar.notification.ColorUpdateLogger
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider
@@ -82,6 +83,7 @@ class ExpandableNotificationRowControllerTest : SysuiTestCase() {
private val rivSubComponentFactory: RemoteInputViewSubcomponent.Factory = mock()
private val metricsLogger: MetricsLogger = mock()
private val logBufferLogger = NotificationRowLogger(logcatLogBuffer(), logcatLogBuffer())
+ private val colorUpdateLogger: ColorUpdateLogger = mock()
private val listContainer: NotificationListContainer = mock()
private val childrenContainer: NotificationChildrenContainer = mock()
private val smartReplyConstants: SmartReplyConstants = mock()
@@ -117,6 +119,7 @@ class ExpandableNotificationRowControllerTest : SysuiTestCase() {
activableNotificationViewController,
rivSubComponentFactory,
metricsLogger,
+ colorUpdateLogger,
logBufferLogger,
NotificationChildrenContainerLogger(logcatLogBuffer()),
listContainer,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index c71799170482..e78081fc34bd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -65,6 +65,7 @@ import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.SmartReplyController;
+import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.ConversationNotificationProcessor;
import com.android.systemui.statusbar.notification.SourceType;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -607,6 +608,7 @@ public class NotificationTestHelper {
mDismissibilityProvider,
mock(MetricsLogger.class),
new NotificationChildrenContainerLogger(logcatLogBuffer()),
+ mock(ColorUpdateLogger.class),
mock(SmartReplyConstants.class),
mock(SmartReplyController.class),
mFeatureFlags,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index 4b145d8b0dd2..5c45b2e53047 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -30,7 +30,7 @@ import android.view.ViewGroup;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.media.controls.ui.KeyguardMediaController;
+import com.android.systemui.media.controls.ui.controller.KeyguardMediaController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 354f3f679ffb..a4f88fbe1469 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -63,7 +63,7 @@ import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository;
import com.android.systemui.keyguard.shared.model.KeyguardState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
-import com.android.systemui.media.controls.ui.KeyguardMediaController;
+import com.android.systemui.media.controls.ui.controller.KeyguardMediaController;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
@@ -78,6 +78,7 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChan
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -148,6 +149,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
@Mock private PrimaryBouncerInteractor mPrimaryBouncerInteractor;
@Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
@Mock private MetricsLogger mMetricsLogger;
+ @Mock private ColorUpdateLogger mColorUpdateLogger;
@Mock private DumpManager mDumpManager;
@Mock(answer = Answers.RETURNS_SELF)
private NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder;
@@ -1007,6 +1009,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
mZenModeController,
mNotificationLockscreenUserManager,
mMetricsLogger,
+ mColorUpdateLogger,
mDumpManager,
new FalsingCollectorFake(),
new FalsingManagerFake(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
index 2b3f9d0f3c39..6fec9ad7bd66 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
@@ -23,7 +23,7 @@ import android.view.View.VISIBLE
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
-import com.android.systemui.media.controls.pipeline.MediaDataManager
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 91a9da399ee8..995da8192f7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -222,6 +222,26 @@ class StackScrollAlgorithmTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
+ fun resetViewStates_hunAnimatingAwayWhileDozing_yTranslationIsInset() {
+ whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true)
+
+ ambientState.isDozing = true
+
+ resetViewStates_hunYTranslationIs(stackScrollAlgorithm.mHeadsUpInset)
+ }
+
+ @Test
+ @EnableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
+ fun resetViewStates_hunAnimatingAwayWhileDozing_hasStackMargin_changesHunYTranslation() {
+ whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true)
+
+ ambientState.isDozing = true
+
+ resetViewStates_stackMargin_changesHunYTranslation()
+ }
+
+ @Test
fun resetViewStates_hunsOverlapping_bottomHunClipped() {
val topHun = mockExpandableNotificationRow()
val bottomHun = mockExpandableNotificationRow()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index 38698f86c659..fd295b57379c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -298,7 +298,7 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
@Test
public void notifPaddingMakesUpToFullMarginInSplitShade_refactorFlagOff_usesResource() {
- mSetFlagsRule.disableFlags(Flags.FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR);
+ mSetFlagsRule.disableFlags(Flags.FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX);
int keyguardSplitShadeTopMargin = 100;
int largeScreenHeaderHeightResource = 70;
when(mResources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin))
@@ -317,7 +317,7 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
@Test
public void notifPaddingMakesUpToFullMarginInSplitShade_refactorFlagOn_usesHelper() {
- mSetFlagsRule.enableFlags(Flags.FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR);
+ mSetFlagsRule.enableFlags(Flags.FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX);
int keyguardSplitShadeTopMargin = 100;
int largeScreenHeaderHeightHelper = 50;
int largeScreenHeaderHeightResource = 70;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java
index 41b959e98221..9d53b9c66b33 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java
@@ -119,7 +119,7 @@ public class LegacyNotificationIconAreaControllerImplTest extends SysuiTestCase
@Test
public void testAppearResetsTranslation() {
- mSetFlagsRule.disableFlags(Flags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL);
+ mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
mController.setupAodIcons(mAodIcons);
when(mDozeParameters.shouldControlScreenOff()).thenReturn(false);
mController.appearAodIcons();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index b7560ad79026..1687ccbf5826 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -22,15 +22,16 @@ import android.app.StatusBarManager.WINDOW_STATE_SHOWING
import android.app.StatusBarManager.WINDOW_STATUS_BAR
import android.view.LayoutInflater
import android.view.MotionEvent
+import android.view.View
import android.view.ViewTreeObserver
import android.view.ViewTreeObserver.OnPreDrawListener
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
import com.android.systemui.scene.ui.view.WindowRootView
import com.android.systemui.shade.ShadeControllerImpl
@@ -48,8 +49,6 @@ import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.view.ViewUtil
import com.google.common.truth.Truth.assertThat
-import java.util.Optional
-import javax.inject.Provider
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentCaptor
@@ -60,6 +59,8 @@ import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
+import java.util.Optional
+import javax.inject.Provider
@SmallTest
class PhoneStatusBarViewControllerTest : SysuiTestCase() {
@@ -98,7 +99,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
val parent = FrameLayout(mContext) // add parent to keep layout params
view =
LayoutInflater.from(mContext).inflate(R.layout.status_bar, parent, false)
- as PhoneStatusBarView
+ as PhoneStatusBarView
controller = createAndInitController(view)
}
}
@@ -231,6 +232,27 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
verify(centralSurfacesImpl).setInteracting(any(), any())
}
+ @Test
+ fun shadeIsExpandedOnStatusIconClick() {
+ val view = createViewMock()
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ controller = createAndInitController(view)
+ }
+ val statusContainer = view.requireViewById<View>(R.id.system_icons)
+ statusContainer.performClick()
+ verify(shadeViewController).expand(any())
+ }
+
+ @Test
+ fun shadeIsNotExpandedOnStatusBarGeneralClick() {
+ val view = createViewMock()
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ controller = createAndInitController(view)
+ }
+ view.performClick()
+ verify(shadeViewController, never()).expand(any())
+ }
+
private fun getCommandQueueCallback(): CommandQueue.Callbacks {
val captor = argumentCaptor<CommandQueue.Callbacks>()
verify(commandQueue).addCallback(captor.capture())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
index 269b70fe6dfb..5e8b62e799c1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
@@ -207,6 +207,72 @@ class PhoneStatusBarViewTest : SysuiTestCase() {
}
@Test
+ fun onConfigurationChanged_noRelevantChange_doesNotUpdateInsets() {
+ val previousInsets =
+ Insets.of(/* left = */ 40, /* top = */ 30, /* right = */ 20, /* bottom = */ 10)
+ whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
+ .thenReturn(previousInsets)
+ context.orCreateTestableResources.overrideConfiguration(Configuration())
+ view.onAttachedToWindow()
+
+ val newInsets = Insets.NONE
+ whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
+ .thenReturn(newInsets)
+ view.onConfigurationChanged(Configuration())
+
+ assertThat(view.paddingLeft).isEqualTo(previousInsets.left)
+ assertThat(view.paddingTop).isEqualTo(previousInsets.top)
+ assertThat(view.paddingRight).isEqualTo(previousInsets.right)
+ assertThat(view.paddingBottom).isEqualTo(0)
+ }
+
+ @Test
+ fun onConfigurationChanged_densityChanged_updatesInsets() {
+ val previousInsets =
+ Insets.of(/* left = */ 40, /* top = */ 30, /* right = */ 20, /* bottom = */ 10)
+ whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
+ .thenReturn(previousInsets)
+ val configuration = Configuration()
+ configuration.densityDpi = 123
+ context.orCreateTestableResources.overrideConfiguration(configuration)
+ view.onAttachedToWindow()
+
+ val newInsets = Insets.NONE
+ whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
+ .thenReturn(newInsets)
+ configuration.densityDpi = 456
+ view.onConfigurationChanged(configuration)
+
+ assertThat(view.paddingLeft).isEqualTo(newInsets.left)
+ assertThat(view.paddingTop).isEqualTo(newInsets.top)
+ assertThat(view.paddingRight).isEqualTo(newInsets.right)
+ assertThat(view.paddingBottom).isEqualTo(0)
+ }
+
+ @Test
+ fun onConfigurationChanged_fontScaleChanged_updatesInsets() {
+ val previousInsets =
+ Insets.of(/* left = */ 40, /* top = */ 30, /* right = */ 20, /* bottom = */ 10)
+ whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
+ .thenReturn(previousInsets)
+ val configuration = Configuration()
+ configuration.fontScale = 1f
+ context.orCreateTestableResources.overrideConfiguration(configuration)
+ view.onAttachedToWindow()
+
+ val newInsets = Insets.NONE
+ whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
+ .thenReturn(newInsets)
+ configuration.fontScale = 2f
+ view.onConfigurationChanged(configuration)
+
+ assertThat(view.paddingLeft).isEqualTo(newInsets.left)
+ assertThat(view.paddingTop).isEqualTo(newInsets.top)
+ assertThat(view.paddingRight).isEqualTo(newInsets.right)
+ assertThat(view.paddingBottom).isEqualTo(0)
+ }
+
+ @Test
fun onApplyWindowInsets_updatesLeftTopRightPaddingsBasedOnInsets() {
val insets = Insets.of(/* left = */ 90, /* top = */ 10, /* right = */ 45, /* bottom = */ 50)
whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
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 4015361dc277..bd7406ad004b 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
@@ -90,8 +90,7 @@ import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.shade.ShadeExpansionStateManager;
-import com.android.systemui.shade.ShadeViewController;
-import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.shade.ShadeLockscreenInteractor;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -125,7 +124,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
@Mock private LockPatternUtils mLockPatternUtils;
@Mock private CentralSurfaces mCentralSurfaces;
@Mock private ViewGroup mContainer;
- @Mock private ShadeViewController mShadeViewController;
+ @Mock private ShadeLockscreenInteractor mShadeLockscreenInteractor;
@Mock private BiometricUnlockController mBiometricUnlockController;
@Mock private SysuiStatusBarStateController mStatusBarStateController;
@Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -206,7 +205,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
mock(DockManager.class),
mNotificationShadeWindowController,
mKeyguardStateController,
- mock(NotificationMediaManager.class),
mKeyguardMessageAreaFactory,
Optional.of(mSysUiUnfoldComponent),
() -> mShadeController,
@@ -234,7 +232,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
.thenReturn(mOnBackInvokedDispatcher);
mStatusBarKeyguardViewManager.registerCentralSurfaces(
mCentralSurfaces,
- mShadeViewController,
+ mShadeLockscreenInteractor,
new ShadeExpansionStateManager(),
mBiometricUnlockController,
mNotificationContainer,
@@ -715,7 +713,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
mock(DockManager.class),
mock(NotificationShadeWindowController.class),
mKeyguardStateController,
- mock(NotificationMediaManager.class),
mKeyguardMessageAreaFactory,
Optional.of(mSysUiUnfoldComponent),
() -> mShadeController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
index f53fc46d8ab2..cd0652e53657 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
@@ -20,6 +20,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.log.core.FakeLogBuffer
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
@@ -28,7 +29,10 @@ import com.android.systemui.statusbar.pipeline.satellite.domain.interactor.Devic
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.mockito.MockitoAnnotations
@@ -61,6 +65,7 @@ class DeviceBasedSatelliteViewModelTest : SysuiTestCase() {
interactor,
testScope.backgroundScope,
airplaneModeRepository,
+ FakeLogBuffer.Factory.create(),
)
}
@@ -121,8 +126,9 @@ class DeviceBasedSatelliteViewModelTest : SysuiTestCase() {
assertThat(latest).isNull()
}
+ @OptIn(ExperimentalCoroutinesApi::class)
@Test
- fun icon_satelliteIsOff() =
+ fun icon_satelliteIsOn() =
testScope.runTest {
val latest by collectLastValue(underTest.icon)
@@ -133,7 +139,45 @@ class DeviceBasedSatelliteViewModelTest : SysuiTestCase() {
val i1 = mobileIconsInteractor.getMobileConnectionInteractorForSubId(1)
i1.isInService.value = false
- // THEN icon is null because we have service
+ // GIVEN apm is disabled
+ airplaneModeRepository.setIsAirplaneMode(false)
+
+ // Wait for delay to be completed
+ advanceTimeBy(10.seconds)
+
+ // THEN icon is set because we don't have service
+ assertThat(latest).isInstanceOf(Icon::class.java)
+ }
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @Test
+ fun icon_hysteresisWhenEnablingIcon() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.icon)
+
+ // GIVEN satellite is allowed
+ repo.isSatelliteAllowedForCurrentLocation.value = true
+
+ // GIVEN all icons are OOS
+ val i1 = mobileIconsInteractor.getMobileConnectionInteractorForSubId(1)
+ i1.isInService.value = false
+
+ // GIVEN apm is disabled
+ airplaneModeRepository.setIsAirplaneMode(false)
+
+ // THEN icon is null because of the hysteresis
+ assertThat(latest).isNull()
+
+ // Wait for delay to be completed
+ advanceTimeBy(10.seconds)
+
+ // THEN icon is set after the delay
assertThat(latest).isInstanceOf(Icon::class.java)
+
+ // GIVEN apm is enabled
+ airplaneModeRepository.setIsAirplaneMode(true)
+
+ // THEN icon is null immediately
+ assertThat(latest).isNull()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerFlagDisabledTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerFlagDisabledTest.kt
index 759235655eca..933b5b519672 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerFlagDisabledTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerFlagDisabledTest.kt
@@ -24,6 +24,7 @@ import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.server.notification.Flags
import com.android.systemui.SysuiTestCase
+import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.settings.FakeGlobalSettings
import com.android.systemui.util.time.FakeSystemClock
@@ -38,6 +39,8 @@ import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@DisableFlags(Flags.FLAG_SCREENSHARE_NOTIFICATION_HIDING)
class SensitiveNotificationProtectionControllerFlagDisabledTest : SysuiTestCase() {
+ private val logger = SensitiveNotificationProtectionControllerLogger(logcatLogBuffer())
+
@Mock private lateinit var handler: Handler
@Mock private lateinit var activityManager: IActivityManager
@Mock private lateinit var mediaProjectionManager: MediaProjectionManager
@@ -54,7 +57,8 @@ class SensitiveNotificationProtectionControllerFlagDisabledTest : SysuiTestCase(
mediaProjectionManager,
activityManager,
handler,
- FakeExecutor(FakeSystemClock())
+ FakeExecutor(FakeSystemClock()),
+ logger
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerTest.kt
index 1dac642836c6..4b4e315f5533 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerTest.kt
@@ -19,17 +19,25 @@ package com.android.systemui.statusbar.policy
import android.app.ActivityOptions
import android.app.IActivityManager
import android.app.Notification
+import android.app.Notification.FLAG_FOREGROUND_SERVICE
+import android.app.Notification.VISIBILITY_PRIVATE
+import android.app.Notification.VISIBILITY_PUBLIC
+import android.app.NotificationChannel
+import android.app.NotificationManager.IMPORTANCE_HIGH
+import android.app.NotificationManager.VISIBILITY_NO_OVERRIDE
import android.media.projection.MediaProjectionInfo
import android.media.projection.MediaProjectionManager
import android.platform.test.annotations.EnableFlags
import android.provider.Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS
-import android.service.notification.StatusBarNotification
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.server.notification.Flags
import com.android.systemui.SysuiTestCase
+import com.android.systemui.log.logcatLogBuffer
+import com.android.systemui.statusbar.RankingBuilder
import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.concurrency.mockExecutorHandler
import com.android.systemui.util.mockito.whenever
@@ -56,6 +64,8 @@ import org.mockito.MockitoAnnotations
@RunWithLooper
@EnableFlags(Flags.FLAG_SCREENSHARE_NOTIFICATION_HIDING)
class SensitiveNotificationProtectionControllerTest : SysuiTestCase() {
+ private val logger = SensitiveNotificationProtectionControllerLogger(logcatLogBuffer())
+
@Mock private lateinit var activityManager: IActivityManager
@Mock private lateinit var mediaProjectionManager: MediaProjectionManager
@Mock private lateinit var mediaProjectionInfo: MediaProjectionInfo
@@ -86,7 +96,8 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() {
mediaProjectionManager,
activityManager,
mockExecutorHandler(executor),
- executor
+ executor,
+ logger
)
// Process pending work (getting global setting and list of exemptions)
@@ -316,6 +327,25 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() {
assertFalse(controller.shouldProtectNotification(notificationEntry))
}
+ @Test
+ fun shouldProtectNotification_projectionActive_publicNotification_false() {
+ mediaProjectionCallback.onStart(mediaProjectionInfo)
+
+ // App marked notification visibility as public
+ val notificationEntry = setupPublicNotificationEntry(TEST_PROJECTION_PACKAGE_NAME)
+
+ assertFalse(controller.shouldProtectNotification(notificationEntry))
+ }
+
+ @Test
+ fun shouldProtectNotification_projectionActive_publicNotificationUserChannelOverride_true() {
+ mediaProjectionCallback.onStart(mediaProjectionInfo)
+
+ val notificationEntry =
+ setupPublicNotificationEntryWithUserOverriddenChannel(TEST_PROJECTION_PACKAGE_NAME)
+
+ assertTrue(controller.shouldProtectNotification(notificationEntry))
+ }
private fun setDisabledViaDeveloperOption() {
globalSettings.putInt(DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS, 1)
@@ -336,21 +366,50 @@ class SensitiveNotificationProtectionControllerTest : SysuiTestCase() {
private fun setupNotificationEntry(
packageName: String,
- isFgs: Boolean = false
+ isFgs: Boolean = false,
+ overrideVisibility: Boolean = false,
+ overrideChannelVisibility: Boolean = false,
): NotificationEntry {
- val notificationEntry = mock(NotificationEntry::class.java)
- val sbn = mock(StatusBarNotification::class.java)
- val notification = mock(Notification::class.java)
- whenever(notificationEntry.sbn).thenReturn(sbn)
- whenever(sbn.packageName).thenReturn(packageName)
- whenever(sbn.notification).thenReturn(notification)
- whenever(notification.isFgsOrUij).thenReturn(isFgs)
-
+ val notification = Notification()
+ if (isFgs) {
+ notification.flags = notification.flags or FLAG_FOREGROUND_SERVICE
+ }
+ if (overrideVisibility) {
+ // Developer has marked notification as public
+ notification.visibility = VISIBILITY_PUBLIC
+ }
+ val notificationEntry =
+ NotificationEntryBuilder().setNotification(notification).setPkg(packageName).build()
+ val channel = NotificationChannel("1", "1", IMPORTANCE_HIGH)
+ if (overrideChannelVisibility) {
+ // User doesn't allow private notifications at the channel level
+ channel.lockscreenVisibility = VISIBILITY_PRIVATE
+ }
+ notificationEntry.setRanking(
+ RankingBuilder(notificationEntry.ranking)
+ .setChannel(channel)
+ .setVisibilityOverride(VISIBILITY_NO_OVERRIDE)
+ .build()
+ )
return notificationEntry
}
private fun setupFgsNotificationEntry(packageName: String): NotificationEntry {
- return setupNotificationEntry(packageName, /* isFgs= */ true)
+ return setupNotificationEntry(packageName, isFgs = true)
+ }
+
+ private fun setupPublicNotificationEntry(packageName: String): NotificationEntry {
+ return setupNotificationEntry(packageName, overrideVisibility = true)
+ }
+
+ private fun setupPublicNotificationEntryWithUserOverriddenChannel(
+ packageName: String
+ ): NotificationEntry {
+ return setupNotificationEntry(
+ packageName,
+ overrideVisibility = true,
+ overrideChannelVisibility = true
+ )
}
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
index f1a2c281595d..ddd29c3f2803 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
@@ -79,6 +79,7 @@ public class ZenModeControllerImplTest extends SysuiTestCase {
mController = new ZenModeControllerImpl(
mContext,
Handler.createAsync(TestableLooper.get(this).getLooper()),
+ Handler.createAsync(TestableLooper.get(this).getLooper()),
mBroadcastDispatcher,
mDumpManager,
mGlobalSettings,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseControllerTest.kt
index 203096affd5c..08b49f026523 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseControllerTest.kt
@@ -166,4 +166,28 @@ class TurbulenceNoiseControllerTest : SysuiTestCase() {
assertThat(config.color).isEqualTo(expectedColor)
}
}
+
+ @Test
+ fun play_initializesShader() {
+ val expectedNoiseOffset = floatArrayOf(0.1f, 0.2f, 0.3f)
+ val config =
+ TurbulenceNoiseAnimationConfig(
+ noiseOffsetX = expectedNoiseOffset[0],
+ noiseOffsetY = expectedNoiseOffset[1],
+ noiseOffsetZ = expectedNoiseOffset[2]
+ )
+ val turbulenceNoiseView = TurbulenceNoiseView(context, null)
+ val turbulenceNoiseController = TurbulenceNoiseController(turbulenceNoiseView)
+
+ fakeExecutor.execute {
+ turbulenceNoiseController.play(SIMPLEX_NOISE, config)
+
+ assertThat(turbulenceNoiseView.noiseConfig).isNotNull()
+ val shader = turbulenceNoiseView.turbulenceNoiseShader!!
+ assertThat(shader).isNotNull()
+ assertThat(shader.noiseOffsetX).isEqualTo(expectedNoiseOffset[0])
+ assertThat(shader.noiseOffsetY).isEqualTo(expectedNoiseOffset[1])
+ assertThat(shader.noiseOffsetZ).isEqualTo(expectedNoiseOffset[2])
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
index ee2e5addd0e6..28adbceda847 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
@@ -321,4 +321,38 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
verify(displaySwitchLatencyLogger, never()).log(any())
}
}
+
+ @Test
+ fun foldToScreenOff_capturesToStateAsScreenOff() {
+ testScope.runTest {
+ areAnimationEnabled.emit(true)
+ deviceState.emit(DeviceState.UNFOLDED)
+ isAodAvailable.emit(false)
+
+ displaySwitchLatencyTracker.start()
+ deviceState.emit(DeviceState.HALF_FOLDED)
+ systemClock.advanceTime(50)
+ runCurrent()
+ deviceState.emit(DeviceState.FOLDED)
+ lastWakefulnessEvent.emit(
+ WakefulnessModel(
+ internalWakefulnessState = WakefulnessState.ASLEEP,
+ lastSleepReason = WakeSleepReason.FOLD
+ )
+ )
+ screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+ runCurrent()
+
+ verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor))
+ val loggedEvent = loggerArgumentCaptor.value
+ val expectedLoggedEvent =
+ DisplaySwitchLatencyEvent(
+ latencyMs = 0,
+ fromFoldableDeviceState = FOLDABLE_DEVICE_STATE_HALF_OPEN,
+ toFoldableDeviceState = FOLDABLE_DEVICE_STATE_CLOSED,
+ toState = SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TO_STATE__SCREEN_OFF
+ )
+ assertThat(loggedEvent).isEqualTo(expectedLoggedEvent)
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
index f9241343549c..10aab96e6036 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
@@ -246,7 +246,7 @@ class FoldAodAnimationControllerTest : SysuiTestCase() {
private fun sendFoldEvent(folded: Boolean) {
val state = if (folded) deviceStates.folded else deviceStates.unfolded
- foldStateListenerCaptor.value.onStateChanged(state)
+ foldStateListenerCaptor.value.onDeviceStateChanged(state)
}
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
index c7dc0ab8f9cf..ba72716997e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
@@ -174,7 +174,7 @@ class UnfoldLatencyTrackerTest : SysuiTestCase() {
private fun sendFoldEvent(folded: Boolean) {
val state = if (folded) deviceStates.folded else deviceStates.unfolded
- foldStateListenerCaptor.value.onStateChanged(state)
+ foldStateListenerCaptor.value.onDeviceStateChanged(state)
}
private fun sendScreenTurnedOnEvent() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt
index 01b535964ba6..e499a3c6c2eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt
@@ -17,21 +17,29 @@
package com.android.systemui.unfold.util
import android.content.Context
+import android.hardware.devicestate.DeviceState
import org.junit.Assume.assumeTrue
object FoldableTestUtils {
/** Finds device state for folded and unfolded. */
fun findDeviceStates(context: Context): FoldableDeviceStates {
+ // TODO(b/325474477): Migrate clients to updated DeviceStateManager API's
val foldedDeviceStates: IntArray = context.resources.getIntArray(
com.android.internal.R.array.config_foldedDeviceStates)
assumeTrue("Test should be launched on a foldable device",
foldedDeviceStates.isNotEmpty())
- val folded = foldedDeviceStates.maxOrNull()!!
- val unfolded = folded + 1
+ val folded =
+ DeviceState(foldedDeviceStates.maxOrNull()!! /* identifier */,
+ "" /* name */,
+ emptySet() /* properties */)
+ val unfolded =
+ DeviceState(folded.identifier + 1 /* identifier */,
+ "" /* name */,
+ emptySet() /* properties */)
return FoldableDeviceStates(folded = folded, unfolded = unfolded)
}
}
-data class FoldableDeviceStates(val folded: Int, val unfolded: Int) \ No newline at end of file
+data class FoldableDeviceStates(val folded: DeviceState, val unfolded: DeviceState) \ No newline at end of file
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 25a0bc4fba61..d2e03861b022 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -58,8 +58,10 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
import android.widget.ImageButton;
+import android.widget.SeekBar;
import androidx.test.core.view.MotionEventBuilder;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import com.android.internal.jank.InteractionJankMonitor;
@@ -111,6 +113,7 @@ public class VolumeDialogImplTest extends SysuiTestCase {
View mDrawerVibrate;
View mDrawerMute;
View mDrawerNormal;
+ ViewGroup mDialogRowsView;
CaptionsToggleImageButton mODICaptionsIcon;
private TestableLooper mTestableLooper;
@@ -222,6 +225,8 @@ public class VolumeDialogImplTest extends SysuiTestCase {
}
mODICaptionsIcon = mDialog.getDialogView().findViewById(R.id.odi_captions_icon);
+ mDialogRowsView = mDialog.getDialogView().findViewById(R.id.volume_dialog_rows);
+
Prefs.putInt(mContext,
Prefs.Key.SEEN_RINGER_GUIDANCE_COUNT,
VolumePrefs.SHOW_RINGER_TOAST_COUNT + 1);
@@ -497,6 +502,7 @@ public class VolumeDialogImplTest extends SysuiTestCase {
}
@Test
+ @FlakyTest(bugId = 326204750)
public void dialogDestroy_removesPostureControllerCallback() {
verify(mPostureController, never()).removeCallback(any());
mDialog.destroy();
@@ -671,6 +677,45 @@ public class VolumeDialogImplTest extends SysuiTestCase {
}
@Test
+ public void volumeSliderTracksTouch_logsStartAndStopTrackingUiEvents() {
+ UiEventLoggerFake logger = new UiEventLoggerFake();
+ Events.sUiEventLogger = logger;
+
+ mDialog.show(SHOW_REASON_UNKNOWN);
+ mTestableLooper.processAllMessages();
+
+ MotionEvent down = MotionEventBuilder.newBuilder()
+ .setAction(MotionEvent.ACTION_DOWN).build();
+ MotionEvent up = MotionEventBuilder.newBuilder().setAction(MotionEvent.ACTION_UP).build();
+
+ SeekBar slider =
+ mDialogRowsView.getChildAt(0).findViewById(R.id.volume_row_slider);
+ slider.onTouchEvent(down);
+ slider.onTouchEvent(up);
+ mTestableLooper.moveTimeForward(300);
+ mTestableLooper.processAllMessages();
+
+ boolean foundStartTrackingTouch = false;
+ boolean foundStopTrackingTouch = false;
+ for (UiEventLoggerFake.FakeUiEvent event : logger.getLogs()) {
+ if (event.eventId
+ == Events.VolumeDialogEvent.VOLUME_DIALOG_SLIDER_STARTED_TRACKING_TOUCH.getId()
+ ) {
+ foundStartTrackingTouch = true;
+ }
+ if (event.eventId
+ == Events.VolumeDialogEvent.VOLUME_DIALOG_SLIDER_STOPPED_TRACKING_TOUCH.getId()
+ ) {
+ foundStopTrackingTouch = true;
+ }
+ }
+ Assert.assertTrue("Did not log the event of start tracking touch.",
+ foundStartTrackingTouch);
+ Assert.assertTrue("Did not log the event of stop tracking touch.",
+ foundStopTrackingTouch);
+ }
+
+ @Test
public void turnOnDnD_volumeSliderIconChangesToDnd() {
State state = createShellState();
state.zenMode = Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
@@ -756,8 +801,9 @@ public class VolumeDialogImplTest extends SysuiTestCase {
Log.d(TAG, "teardown: entered");
setOrientation(mOriginalOrientation);
Log.d(TAG, "teardown: after setOrientation");
- mAnimatorTestRule.advanceTimeBy(mLongestHideShowAnimationDuration);
- Log.d(TAG, "teardown: after advanceTimeBy");
+ // Unclear why we used to do this, and it seems to be a source of flakes
+ // mAnimatorTestRule.advanceTimeBy(mLongestHideShowAnimationDuration);
+ Log.d(TAG, "teardown: skipped advanceTimeBy");
mTestableLooper.moveTimeForward(mLongestHideShowAnimationDuration);
Log.d(TAG, "teardown: after moveTimeForward");
mTestableLooper.processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualLocationsServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualLocationsServiceTest.kt
index 8c5df6efef33..d2387e83e3eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualLocationsServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualLocationsServiceTest.kt
@@ -12,6 +12,7 @@ import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.test.StandardTestDispatcher
@@ -38,6 +39,7 @@ class WalletContextualLocationsServiceTest : SysuiTestCase() {
private var featureFlags = FakeFeatureFlags()
private lateinit var underTest: WalletContextualLocationsService
private lateinit var testScope: TestScope
+ private lateinit var testDispatcher: CoroutineDispatcher
private var listenerRegisteredCount: Int = 0
private val listener: IWalletCardsUpdatedListener.Stub =
object : IWalletCardsUpdatedListener.Stub() {
@@ -54,8 +56,8 @@ class WalletContextualLocationsServiceTest : SysuiTestCase() {
doNothing().whenever(controller).setSuggestionCardIds(anySet())
if (Looper.myLooper() == null) Looper.prepare()
- val testDispatcher = StandardTestDispatcher()
- testScope = TestScope()
+ testDispatcher = StandardTestDispatcher()
+ testScope = TestScope(testDispatcher)
featureFlags.set(Flags.ENABLE_WALLET_CONTEXTUAL_LOYALTY_CARDS, true)
listenerRegisteredCount = 0
underTest =
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 d87df0af6ba9..a9308601a314 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -26,6 +26,7 @@ import static android.service.notification.NotificationListenerService.REASON_AP
import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.notification.Flags.FLAG_SCREENSHARE_NOTIFICATION_HIDING;
import static com.google.common.truth.Truth.assertThat;
@@ -46,6 +47,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
@@ -73,6 +75,8 @@ import android.os.PowerManager;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.service.dreams.IDreamManager;
import android.service.notification.NotificationListenerService;
import android.service.notification.ZenModeConfig;
@@ -161,6 +165,7 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
+import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository;
import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository;
@@ -257,6 +262,8 @@ public class BubblesTest extends SysuiTestCase {
private NotificationShadeWindowView mNotificationShadeWindowView;
@Mock
private AuthController mAuthController;
+ @Mock
+ private SensitiveNotificationProtectionController mSensitiveNotificationProtectionController;
private SysUiState mSysUiState;
private boolean mSysUiStateBubblesExpanded;
@@ -272,6 +279,8 @@ public class BubblesTest extends SysuiTestCase {
private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverArgumentCaptor;
@Captor
private ArgumentCaptor<KeyguardStateController.Callback> mKeyguardStateControllerCallbackCaptor;
+ @Captor
+ private ArgumentCaptor<Runnable> mSensitiveStateChangedListener;
private BubblesManager mBubblesManager;
private TestableBubbleController mBubbleController;
@@ -415,7 +424,6 @@ public class BubblesTest extends SysuiTestCase {
mTestScope.getBackgroundScope(),
mKosmos.getFakeSceneContainerConfig(),
mKosmos.getSceneDataSource()),
- powerInteractor,
mock(SceneLogger.class),
mKosmos.getDeviceUnlockedInteractor());
@@ -595,6 +603,7 @@ public class BubblesTest extends SysuiTestCase {
interruptionDecisionProvider,
mZenModeController,
mLockscreenUserManager,
+ mSensitiveNotificationProtectionController,
mCommonNotifCollection,
mNotifPipeline,
mSysUiState,
@@ -2204,6 +2213,33 @@ public class BubblesTest extends SysuiTestCase {
assertThat(mBubbleController.getLayerView().isExpanded()).isFalse();
}
+ @DisableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
+ @Test
+ public void doesNotRegisterSensitiveStateListener() {
+ verifyZeroInteractions(mSensitiveNotificationProtectionController);
+ }
+
+ @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
+ @Test
+ public void registerSensitiveStateListener() {
+ verify(mSensitiveNotificationProtectionController).registerSensitiveStateListener(any());
+ }
+
+ @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
+ @Test
+ public void onSensitiveNotificationProtectionStateChanged() {
+ verify(mSensitiveNotificationProtectionController, atLeastOnce())
+ .registerSensitiveStateListener(mSensitiveStateChangedListener.capture());
+
+ when(mSensitiveNotificationProtectionController.isSensitiveStateActive()).thenReturn(true);
+ mSensitiveStateChangedListener.getValue().run();
+ verify(mBubbleController).onSensitiveNotificationProtectionStateChanged(true);
+
+ when(mSensitiveNotificationProtectionController.isSensitiveStateActive()).thenReturn(false);
+ mSensitiveStateChangedListener.getValue().run();
+ verify(mBubbleController).onSensitiveNotificationProtectionStateChanged(false);
+ }
+
/** Creates a bubble using the userId and package. */
private Bubble createBubble(int userId, String pkg) {
final UserHandle userHandle = new UserHandle(userId);
diff --git a/packages/SystemUI/tests/utils/src/android/graphics/drawable/TestStubDrawable.kt b/packages/SystemUI/tests/utils/src/android/graphics/drawable/TestStubDrawable.kt
index b88f302cdfdd..1a9f4b40c179 100644
--- a/packages/SystemUI/tests/utils/src/android/graphics/drawable/TestStubDrawable.kt
+++ b/packages/SystemUI/tests/utils/src/android/graphics/drawable/TestStubDrawable.kt
@@ -24,12 +24,27 @@ import android.graphics.PixelFormat
* Stub drawable that does nothing. It's to be used in tests as a mock drawable and checked for the
* same instance
*/
-class TestStubDrawable : Drawable() {
+class TestStubDrawable(private val name: String? = null) : Drawable() {
override fun draw(canvas: Canvas) = Unit
override fun setAlpha(alpha: Int) = Unit
override fun setColorFilter(colorFilter: ColorFilter?) = Unit
override fun getOpacity(): Int = PixelFormat.UNKNOWN
- override fun equals(other: Any?): Boolean = this === other
+ override fun toString(): String {
+ return name ?: super.toString()
+ }
+
+ override fun getConstantState(): ConstantState =
+ TestStubConstantState(this, changingConfigurations)
+
+ private class TestStubConstantState(
+ private val drawable: Drawable,
+ private val changingConfigurations: Int,
+ ) : ConstantState() {
+
+ override fun newDrawable(): Drawable = drawable
+
+ override fun getChangingConfigurations(): Int = changingConfigurations
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
index b62b3a211e58..353d9709b056 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
@@ -46,6 +46,7 @@ import androidx.core.animation.AndroidXAnimatorIsolationRule;
import androidx.test.InstrumentationRegistry;
import androidx.test.uiautomator.UiDevice;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.systemui.broadcast.FakeBroadcastDispatcher;
import com.android.systemui.flags.SceneContainerRule;
@@ -170,6 +171,7 @@ public abstract class SysuiTestCase {
@Before
public void SysuiSetup() throws Exception {
+ ProtoLog.REQUIRE_PROTOLOGTOOL = false;
mSysuiDependency = new SysuiTestDependency(mContext, shouldFailOnLeakedReceiver());
mDependency = mSysuiDependency.install();
// TODO(b/292141694): build out Ravenwood support for Instrumentation
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
index b8c880b3892f..62a1aa93f35a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/TestMocksModule.kt
@@ -37,7 +37,7 @@ import com.android.systemui.log.LogBuffer
import com.android.systemui.log.dagger.BiometricLog
import com.android.systemui.log.dagger.BroadcastDispatcherLog
import com.android.systemui.log.dagger.SceneFrameworkLog
-import com.android.systemui.media.controls.ui.MediaHierarchyManager
+import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
import com.android.systemui.model.SysUiState
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.DarkIconDispatcher
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/FaceHelpMessageDeferralKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/FaceHelpMessageDeferralKosmos.kt
new file mode 100644
index 000000000000..681412117779
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/FaceHelpMessageDeferralKosmos.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 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.domain
+
+import com.android.systemui.biometrics.FaceHelpMessageDeferral
+import com.android.systemui.biometrics.FaceHelpMessageDeferralFactory
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+
+val Kosmos.faceHelpMessageDeferral by Kosmos.Fixture { mock<FaceHelpMessageDeferral>() }
+val Kosmos.faceHelpMessageDeferralFactory by
+ Kosmos.Fixture {
+ val mockFactory = mock<FaceHelpMessageDeferralFactory>()
+ whenever(mockFactory.create()).thenReturn(faceHelpMessageDeferral)
+ mockFactory
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/FakeCredentialInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/FakeCredentialInteractor.kt
index fbe291ebaf5d..a4c8a0b2c228 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/FakeCredentialInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/FakeCredentialInteractor.kt
@@ -14,10 +14,15 @@ class FakeCredentialInteractor : CredentialInteractor {
/** Sets return value for [getCredentialOwnerOrSelfId]. */
var credentialOwnerId: Int? = null
+ /** Sets return value for [getParentProfileIdOrSelfId]. */
+ var userIdForPasswordEntry: Int? = null
+
override fun isStealthModeActive(userId: Int): Boolean = stealthMode
override fun getCredentialOwnerOrSelfId(userId: Int): Int = credentialOwnerId ?: userId
+ override fun getParentProfileIdOrSelfId(userId: Int): Int = userIdForPasswordEntry ?: userId
+
override fun verifyCredential(
request: BiometricPromptRequest.Credential,
credential: LockscreenCredential,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/domain/interactor/FalsingInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/domain/interactor/FalsingInteractorKosmos.kt
index 8fee5b2b305c..43dc372f020f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/domain/interactor/FalsingInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/domain/interactor/FalsingInteractorKosmos.kt
@@ -17,11 +17,13 @@
package com.android.systemui.classifier.domain.interactor
import com.android.systemui.classifier.falsingCollector
+import com.android.systemui.classifier.falsingManager
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
val Kosmos.falsingInteractor by Fixture {
FalsingInteractor(
collector = falsingCollector,
+ manager = falsingManager,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
index ae7d87783b7c..9d508d23dca7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
@@ -38,11 +38,4 @@ class FakeCommunalRepository(
override fun setTransitionState(transitionState: Flow<ObservableCommunalTransitionState>?) {
_transitionState.value = transitionState
}
-
- private val _isCommunalHubShowing: MutableStateFlow<Boolean> = MutableStateFlow(false)
- override val isCommunalHubShowing: Flow<Boolean> = _isCommunalHubShowing
-
- fun setIsCommunalHubShowing(isCommunalHubShowing: Boolean) {
- _isCommunalHubShowing.value = isCommunalHubShowing
- }
}
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 fab64e38e1f8..4ed6fe27338a 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
@@ -2,6 +2,7 @@ package com.android.systemui.communal.data.repository
import android.appwidget.AppWidgetProviderInfo
import android.content.ComponentName
+import android.os.UserHandle
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.communal.widgets.WidgetConfigurator
import kotlinx.coroutines.CoroutineScope
@@ -23,6 +24,7 @@ class FakeCommunalWidgetRepository(private val coroutineScope: CoroutineScope) :
override fun addWidget(
provider: ComponentName,
+ user: UserHandle,
priority: Int,
configurator: WidgetConfigurator?
) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
index f7e9a117aa77..6ac702eb2446 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
@@ -21,12 +21,19 @@ import com.android.systemui.communal.data.repository.communalPrefsRepository
import com.android.systemui.communal.data.repository.communalRepository
import com.android.systemui.communal.data.repository.communalWidgetRepository
import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.log.logcatLogBuffer
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
+import com.android.systemui.settings.userTracker
import com.android.systemui.smartspace.data.repository.smartspaceRepository
+import com.android.systemui.user.data.repository.fakeUserRepository
import com.android.systemui.util.mockito.mock
val Kosmos.communalInteractor by Fixture {
@@ -40,10 +47,26 @@ val Kosmos.communalInteractor by Fixture {
appWidgetHost = mock(),
keyguardInteractor = keyguardInteractor,
editWidgetsActivityStarter = editWidgetsActivityStarter,
+ userTracker = userTracker,
logBuffer = logcatLogBuffer("CommunalInteractor"),
tableLogBuffer = mock(),
communalSettingsInteractor = communalSettingsInteractor,
+ sceneInteractor = sceneInteractor,
+ sceneContainerFlags = fakeSceneContainerFlags,
)
}
val Kosmos.editWidgetsActivityStarter by Fixture<EditWidgetsActivityStarter> { mock() }
+
+suspend fun Kosmos.setCommunalAvailable(available: Boolean) {
+ fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, available)
+ if (available) {
+ fakeUserRepository.asMainUser()
+ } else {
+ fakeUserRepository.asDefaultUser()
+ }
+ with(fakeKeyguardRepository) {
+ setIsEncryptedOrLockdown(!available)
+ setKeyguardShowing(available)
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorKosmos.kt
index 00fdceda01d1..23f63e6e20a7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorKosmos.kt
@@ -16,7 +16,6 @@
package com.android.systemui.communal.domain.interactor
-import com.android.systemui.communal.data.repository.communalRepository
import com.android.systemui.communal.data.repository.communalTutorialRepository
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.Kosmos
@@ -29,7 +28,6 @@ val Kosmos.communalTutorialInteractor by
scope = applicationCoroutineScope,
communalTutorialRepository = communalTutorialRepository,
keyguardInteractor = keyguardInteractor,
- communalRepository = communalRepository,
communalInteractor = communalInteractor,
communalSettingsInteractor = communalSettingsInteractor,
tableLogBuffer = mock(),
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractorKosmos.kt
index 77f48db60f41..3ea46872ebcf 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/BiometricMessageInteractorKosmos.kt
@@ -30,5 +30,6 @@ val Kosmos.biometricMessageInteractor by
fingerprintPropertyInteractor = fingerprintPropertyInteractor,
faceAuthInteractor = deviceEntryFaceAuthInteractor,
biometricSettingsInteractor = deviceEntryBiometricSettingsInteractor,
+ faceHelpMessageDeferralInteractor = faceHelpMessageDeferralInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractorKosmos.kt
new file mode 100644
index 000000000000..724e943c9f55
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractorKosmos.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 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.deviceentry.domain.interactor
+
+import com.android.systemui.biometrics.domain.faceHelpMessageDeferralFactory
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@ExperimentalCoroutinesApi
+val Kosmos.faceHelpMessageDeferralInteractor by
+ Kosmos.Fixture {
+ FaceHelpMessageDeferralInteractor(
+ scope = applicationCoroutineScope,
+ faceAuthInteractor = deviceEntryFaceAuthInteractor,
+ biometricSettingsInteractor = deviceEntryBiometricSettingsInteractor,
+ faceHelpMessageDeferralFactory = faceHelpMessageDeferralFactory,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
index 97f84c67f473..cceb3ffab282 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
@@ -17,9 +17,10 @@
package com.android.systemui.flags
import android.platform.test.annotations.EnableFlags
+import com.android.systemui.Flags.FLAG_COMPOSE_LOCKSCREEN
import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR
-import com.android.systemui.Flags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL
import com.android.systemui.Flags.FLAG_MEDIA_IN_SCENE_CONTAINER
+import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
/**
@@ -29,7 +30,8 @@ import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
@EnableFlags(
FLAG_SCENE_CONTAINER,
FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR,
- FLAG_KEYGUARD_SHADE_MIGRATION_NSSL,
+ FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT,
+ FLAG_COMPOSE_LOCKSCREEN,
FLAG_MEDIA_IN_SCENE_CONTAINER,
)
@Retention(AnnotationRetention.RUNTIME)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/globalactions/data/repository/GlobalActionsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/globalactions/data/repository/GlobalActionsRepositoryKosmos.kt
new file mode 100644
index 000000000000..0e439b04f484
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/globalactions/data/repository/GlobalActionsRepositoryKosmos.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2024 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.globalactions.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.globalActionsRepository by Kosmos.Fixture { GlobalActionsRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/globalactions/domain/interactor/GlobalActionsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/globalactions/domain/interactor/GlobalActionsInteractorKosmos.kt
new file mode 100644
index 000000000000..72143095a114
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/globalactions/domain/interactor/GlobalActionsInteractorKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 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.globalactions.domain.interactor
+
+import com.android.systemui.globalactions.data.repository.globalActionsRepository
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.globalActionsInteractor by
+ Kosmos.Fixture { GlobalActionsInteractor(globalActionsRepository) }
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 e20a0ab4190e..a9a2d91c0815 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
@@ -43,7 +43,7 @@ import kotlinx.coroutines.test.runCurrent
class FakeKeyguardTransitionRepository @Inject constructor() : KeyguardTransitionRepository {
private val _transitions =
- MutableSharedFlow<TransitionStep>(replay = 2, onBufferOverflow = BufferOverflow.DROP_OLDEST)
+ MutableSharedFlow<TransitionStep>(replay = 3, onBufferOverflow = BufferOverflow.DROP_OLDEST)
override val transitions: SharedFlow<TransitionStep> = _transitions
init {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitionsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitionsKosmos.kt
index 55885bf58acc..5dd50731c58a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitionsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitionsKosmos.kt
@@ -19,13 +19,11 @@ package com.android.systemui.keyguard.domain.interactor
import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
val Kosmos.glanceableHubTransitions by
Kosmos.Fixture {
GlanceableHubTransitions(
- scope = applicationCoroutineScope,
bgDispatcher = testDispatcher,
transitionRepository = keyguardTransitionRepository,
transitionInteractor = keyguardTransitionInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorKosmos.kt
index d9a3192ce821..8b0bba1320e4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorKosmos.kt
@@ -29,5 +29,6 @@ val Kosmos.keyguardBlueprintInteractor by
applicationScope = applicationCoroutineScope,
context = applicationContext,
splitShadeStateController = splitShadeStateController,
+ clockInteractor = keyguardClockInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelKosmos.kt
index 9fb32841d201..f1784a8bc9f2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelKosmos.kt
@@ -18,6 +18,7 @@
package com.android.systemui.keyguard.ui.viewmodel
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -28,5 +29,6 @@ val Kosmos.aodAlphaViewModel by Fixture {
keyguardTransitionInteractor = keyguardTransitionInteractor,
goneToAodTransitionViewModel = goneToAodTransitionViewModel,
goneToDozingTransitionViewModel = goneToDozingTransitionViewModel,
+ keyguardInteractor = keyguardInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt
index 733340c67e55..460913f75eb4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt
@@ -22,11 +22,13 @@ import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsIntera
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.shade.domain.interactor.shadeInteractor
import kotlinx.coroutines.ExperimentalCoroutinesApi
val Kosmos.aodToLockscreenTransitionViewModel by Fixture {
AodToLockscreenTransitionViewModel(
deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
+ shadeInteractor = shadeInteractor,
animationFlow = keyguardTransitionAnimationFlow,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModelKosmos.kt
new file mode 100644
index 000000000000..00741eb69c62
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModelKosmos.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 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.ui.viewmodel
+
+import com.android.systemui.common.ui.domain.interactor.configurationInteractor
+import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.dreamingToGlanceableHubTransitionViewModel by
+ Kosmos.Fixture {
+ DreamingToGlanceableHubTransitionViewModel(
+ configurationInteractor = configurationInteractor,
+ animationFlow = keyguardTransitionAnimationFlow,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModelKosmos.kt
new file mode 100644
index 000000000000..1302f155d93b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModelKosmos.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 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.ui.viewmodel
+
+import com.android.systemui.common.ui.domain.interactor.configurationInteractor
+import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.glanceableHubToDreamingTransitionViewModel by
+ Kosmos.Fixture {
+ GlanceableHubToDreamingTransitionViewModel(
+ configurationInteractor = configurationInteractor,
+ animationFlow = keyguardTransitionAnimationFlow,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelKosmos.kt
index 28fce77b75b7..b1c21b8fa6cf 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModelKosmos.kt
@@ -18,6 +18,7 @@
package com.android.systemui.keyguard.ui.viewmodel
+import com.android.systemui.common.ui.domain.interactor.configurationInteractor
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -26,6 +27,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
@OptIn(ExperimentalCoroutinesApi::class)
val Kosmos.glanceableHubToLockscreenTransitionViewModel by Fixture {
GlanceableHubToLockscreenTransitionViewModel(
+ configurationInteractor = configurationInteractor,
animationFlow = keyguardTransitionAnimationFlow,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
index 4939237bbafe..ecf66a297f0d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
@@ -24,6 +24,7 @@ import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor
import com.android.systemui.statusbar.phone.dozeParameters
import com.android.systemui.statusbar.phone.screenOffAnimationController
@@ -56,5 +57,6 @@ val Kosmos.keyguardRootViewModel by Fixture {
screenOffAnimationController = screenOffAnimationController,
aodBurnInViewModel = aodBurnInViewModel,
aodAlphaViewModel = aodAlphaViewModel,
+ shadeInteractor = shadeInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModelKosmos.kt
index 9fe4ea347f63..471381f7a13f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModelKosmos.kt
@@ -18,13 +18,16 @@
package com.android.systemui.keyguard.ui.viewmodel
+import com.android.systemui.common.ui.domain.interactor.configurationInteractor
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import kotlinx.coroutines.ExperimentalCoroutinesApi
+@ExperimentalCoroutinesApi
val Kosmos.lockscreenToGlanceableHubTransitionViewModel by Fixture {
LockscreenToGlanceableHubTransitionViewModel(
+ configurationInteractor = configurationInteractor,
animationFlow = keyguardTransitionAnimationFlow,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index f6b32800263a..3fc5af1a50ab 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -30,6 +30,7 @@ import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.globalactions.domain.interactor.globalActionsInteractor
import com.android.systemui.jank.interactionJankMonitor
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -91,6 +92,7 @@ class KosmosJavaAdapter(
val fromPrimaryBouncerTransitionInteractor by lazy {
kosmos.fromPrimaryBouncerTransitionInteractor
}
+ val globalActionsInteractor by lazy { kosmos.globalActionsInteractor }
val sceneDataSource by lazy { kosmos.sceneDataSource }
init {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/MediaKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/MediaKosmos.kt
new file mode 100644
index 000000000000..e1b1966aed6c
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/MediaKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 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.media
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.media.dialog.MediaOutputDialogFactory
+import com.android.systemui.util.mockito.mock
+
+var Kosmos.mediaOutputDialogFactory: MediaOutputDialogFactory by Kosmos.Fixture { mock {} }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/MediaHierarchyManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerKosmos.kt
index db2cdfa58f8d..7c24b4cc3d60 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/MediaHierarchyManagerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerKosmos.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.controls.ui
+package com.android.systemui.media.controls.ui.controller
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt
index be559efc7946..7264f7a2bc95 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt
@@ -17,8 +17,10 @@
package com.android.systemui.plugins.statusbar
import com.android.internal.logging.uiEventLogger
+import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.jank.interactionJankMonitor
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.StatusBarStateControllerImpl
import com.android.systemui.util.mockito.mock
@@ -29,7 +31,8 @@ var Kosmos.statusBarStateController by
uiEventLogger,
interactionJankMonitor,
mock(),
- ) {
- shadeInteractor
- }
+ { shadeInteractor },
+ { deviceUnlockedInteractor },
+ { sceneInteractor },
+ )
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/privacy/PrivacyDialogControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/privacy/PrivacyDialogControllerKosmos.kt
new file mode 100644
index 000000000000..960a06940a94
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/privacy/PrivacyDialogControllerKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 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.privacy
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+var Kosmos.privacyDialogController: PrivacyDialogController by
+ Kosmos.Fixture { mock<PrivacyDialogController>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/privacy/PrivacyDialogControllerV2Kosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/privacy/PrivacyDialogControllerV2Kosmos.kt
new file mode 100644
index 000000000000..7628c0e64e6a
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/privacy/PrivacyDialogControllerV2Kosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 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.privacy
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+var Kosmos.privacyDialogControllerV2: PrivacyDialogControllerV2 by
+ Kosmos.Fixture { mock<PrivacyDialogControllerV2>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/FakeIQSTileService.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/FakeIQSTileService.kt
new file mode 100644
index 000000000000..cff59807e00f
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/FakeIQSTileService.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.qs.external
+
+import android.os.Binder
+import android.os.IBinder
+import android.service.quicksettings.IQSTileService
+
+class FakeIQSTileService : IQSTileService {
+
+ var isTileAdded: Boolean = false
+ private set
+ var isTileListening: Boolean = false
+ private set
+ var isUnlockComplete: Boolean = false
+ val clicks: List<IBinder?>
+ get() = mutableClicks
+
+ private val mutableClicks: MutableList<IBinder?> = mutableListOf()
+ private val binder = Binder()
+
+ override fun asBinder(): IBinder = binder
+
+ override fun onTileAdded() {
+ isTileAdded = true
+ }
+
+ override fun onTileRemoved() {
+ isTileAdded = false
+ }
+
+ override fun onStartListening() {
+ isTileListening = true
+ }
+
+ override fun onStopListening() {
+ isTileListening = false
+ }
+
+ override fun onClick(wtoken: IBinder?) {
+ mutableClicks.add(wtoken)
+ }
+
+ override fun onUnlockComplete() {
+ isUnlockComplete = true
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/FakeTileServiceManagerFacade.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/FakeTileServiceManagerFacade.kt
new file mode 100644
index 000000000000..101335f38531
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/FakeTileServiceManagerFacade.kt
@@ -0,0 +1,59 @@
+/*
+ * 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.qs.external
+
+import android.service.quicksettings.IQSTileService
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+
+// TODO(b/299909989) Make a fake instead
+class FakeTileServiceManagerFacade(
+ private val iQSTileService: IQSTileService,
+ val tileServiceManager: TileServiceManager = mock {},
+) {
+
+ private var hasPendingBind: Boolean = false
+
+ var isBound: Boolean = false
+ private set
+
+ init {
+ with(tileServiceManager) {
+ whenever(tileService).thenReturn(iQSTileService)
+ whenever(setBindRequested(any())).then {
+ val isRequested: Boolean = it.getArgument(0)
+ hasPendingBind = isRequested
+ if (!isRequested) {
+ isBound = false
+ }
+ Unit
+ }
+ whenever(clearPendingBind()).then {
+ hasPendingBind = false
+ Unit
+ }
+ whenever(hasPendingBind()).then { hasPendingBind }
+ }
+ }
+
+ fun processPendingBind() {
+ if (hasPendingBind) {
+ isBound = true
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/FakeTileServicesFacade.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/FakeTileServicesFacade.kt
new file mode 100644
index 000000000000..0975e55295e4
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/FakeTileServicesFacade.kt
@@ -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 com.android.systemui.qs.external
+
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+
+class FakeTileServicesFacade(
+ private val TileServiceManager: TileServiceManager,
+ val tileServices: TileServices = mock {}
+) {
+
+ var customTileInterface: CustomTileInterface? = null
+ private set
+
+ init {
+ with(tileServices) {
+ whenever(getTileWrapper(any())).then {
+ customTileInterface = it.getArgument(0)
+ TileServiceManager
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TilesExternalKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TilesExternalKosmos.kt
new file mode 100644
index 000000000000..36c2c2b6eb23
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TilesExternalKosmos.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.qs.external
+
+import android.content.ComponentName
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+var Kosmos.componentName: ComponentName by Kosmos.Fixture()
+
+/** Returns mocks */
+var Kosmos.tileLifecycleManagerFactory: TileLifecycleManager.Factory by Kosmos.Fixture { mock {} }
+
+val Kosmos.iQSTileService: FakeIQSTileService by Kosmos.Fixture { FakeIQSTileService() }
+val Kosmos.tileServiceManagerFacade: FakeTileServiceManagerFacade by
+ Kosmos.Fixture { FakeTileServiceManagerFacade(iQSTileService) }
+
+val Kosmos.tileServiceManager: TileServiceManager by
+ Kosmos.Fixture { tileServiceManagerFacade.tileServiceManager }
+
+val Kosmos.tileServicesFacade: FakeTileServicesFacade by
+ Kosmos.Fixture { (FakeTileServicesFacade(tileServiceManager)) }
+val Kosmos.tileServices: TileServices by Kosmos.Fixture { tileServicesFacade.tileServices }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeDefaultTilesRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeDefaultTilesRepository.kt
new file mode 100644
index 000000000000..ced29cc9eb47
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeDefaultTilesRepository.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 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.qs.pipeline.data.repository
+
+import com.android.systemui.qs.pipeline.shared.TileSpec
+
+class FakeDefaultTilesRepository(override val defaultTiles: List<TileSpec> = emptyList()) :
+ DefaultTilesRepository
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt
index ae4cf3afe671..a9cce6912f15 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt
@@ -23,7 +23,9 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
-class FakeTileSpecRepository : TileSpecRepository {
+class FakeTileSpecRepository(
+ private val defaultTilesRepository: DefaultTilesRepository = FakeDefaultTilesRepository()
+) : TileSpecRepository {
private val tilesPerUser = mutableMapOf<Int, MutableStateFlow<List<TileSpec>>>()
@@ -67,4 +69,8 @@ class FakeTileSpecRepository : TileSpecRepository {
value = UserTileSpecRepository.reconcileTiles(value, currentAutoAdded, restoreData)
}
}
+
+ override suspend fun prependDefault(userId: Int) {
+ with(getFlow(userId)) { value = defaultTilesRepository.defaultTiles + value }
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/QSPipelineRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/QSPipelineRepositoryKosmos.kt
index 009148266143..604c16fd9e74 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/QSPipelineRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/QSPipelineRepositoryKosmos.kt
@@ -18,7 +18,17 @@ package com.android.systemui.qs.pipeline.data.repository
import com.android.systemui.kosmos.Kosmos
-val Kosmos.fakeTileSpecRepository by Kosmos.Fixture { FakeTileSpecRepository() }
+/** This fake uses 0 as the minimum number of tiles. That means that no tiles is a valid state. */
+var Kosmos.fakeMinimumTilesRepository by Kosmos.Fixture { MinimumTilesFixedRepository(0) }
+val Kosmos.minimumTilesRepository: MinimumTilesRepository by
+ Kosmos.Fixture { fakeMinimumTilesRepository }
+
+var Kosmos.fakeDefaultTilesRepository by Kosmos.Fixture { FakeDefaultTilesRepository() }
+val Kosmos.defaultTilesRepository: DefaultTilesRepository by
+ Kosmos.Fixture { fakeDefaultTilesRepository }
+
+val Kosmos.fakeTileSpecRepository by
+ Kosmos.Fixture { FakeTileSpecRepository(defaultTilesRepository) }
var Kosmos.tileSpecRepository: TileSpecRepository by Kosmos.Fixture { fakeTileSpecRepository }
val Kosmos.fakeAutoAddRepository by Kosmos.Fixture { FakeAutoAddRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt
index 67df563ec5b0..9ef44c4b9085 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt
@@ -24,6 +24,7 @@ import com.android.systemui.qs.external.tileLifecycleManagerFactory
import com.android.systemui.qs.newQSTileFactory
import com.android.systemui.qs.pipeline.data.repository.customTileAddedRepository
import com.android.systemui.qs.pipeline.data.repository.installedTilesRepository
+import com.android.systemui.qs.pipeline.data.repository.minimumTilesRepository
import com.android.systemui.qs.pipeline.data.repository.tileSpecRepository
import com.android.systemui.qs.pipeline.shared.logging.qsLogger
import com.android.systemui.qs.pipeline.shared.pipelineFlagsRepository
@@ -37,6 +38,7 @@ val Kosmos.currentTilesInteractor: CurrentTilesInteractor by
tileSpecRepository,
installedTilesRepository,
userRepository,
+ minimumTilesRepository,
customTileStatePersister,
{ newQSTileFactory },
qsTileFactory,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/CustomTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/CustomTileKosmos.kt
index 14f28fedc5e4..561e2540d465 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/CustomTileKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/CustomTileKosmos.kt
@@ -17,19 +17,47 @@
package com.android.systemui.qs.tiles.impl.custom
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
+import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.activityStarter
import com.android.systemui.qs.external.FakeCustomTileStatePersister
+import com.android.systemui.qs.external.tileServices
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.logging.QSTileLogger
import com.android.systemui.qs.tiles.impl.custom.data.repository.FakeCustomTileDefaultsRepository
import com.android.systemui.qs.tiles.impl.custom.data.repository.FakeCustomTilePackageUpdatesRepository
import com.android.systemui.qs.tiles.impl.custom.data.repository.FakeCustomTileRepository
import com.android.systemui.qs.tiles.impl.custom.data.repository.FakePackageManagerAdapterFacade
+import com.android.systemui.qs.tiles.impl.custom.domain.interactor.CustomTileInteractor
+import com.android.systemui.qs.tiles.impl.custom.domain.interactor.CustomTileServiceInteractor
+import com.android.systemui.qs.tiles.impl.custom.domain.interactor.CustomTileUserActionInteractor
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfigTestBuilder
+import com.android.systemui.user.data.repository.userRepository
+import com.android.systemui.util.mockito.mock
var Kosmos.tileSpec: TileSpec.CustomTileSpec by Kosmos.Fixture()
+var Kosmos.customTileQsTileConfig: QSTileConfig by
+ Kosmos.Fixture { QSTileConfigTestBuilder.build { tileSpec = this@Fixture.tileSpec } }
+val Kosmos.qsTileLogger: QSTileLogger by Kosmos.Fixture { mock {} }
+
val Kosmos.customTileStatePersister: FakeCustomTileStatePersister by
Kosmos.Fixture { FakeCustomTileStatePersister() }
+val Kosmos.customTileInteractor: CustomTileInteractor by
+ Kosmos.Fixture {
+ CustomTileInteractor(
+ tileSpec,
+ customTileDefaultsRepository,
+ customTileRepository,
+ testScope.backgroundScope,
+ testScope.testScheduler,
+ )
+ }
+
val Kosmos.customTileRepository: FakeCustomTileRepository by
Kosmos.Fixture {
FakeCustomTileRepository(
@@ -48,3 +76,31 @@ val Kosmos.customTilePackagesUpdatesRepository: FakeCustomTilePackageUpdatesRepo
val Kosmos.packageManagerAdapterFacade: FakePackageManagerAdapterFacade by
Kosmos.Fixture { FakePackageManagerAdapterFacade(tileSpec.componentName) }
+
+val Kosmos.customTileServiceInteractor: CustomTileServiceInteractor by
+ Kosmos.Fixture {
+ CustomTileServiceInteractor(
+ tileSpec,
+ activityStarter,
+ { customTileUserActionInteractor },
+ customTileInteractor,
+ userRepository,
+ qsTileLogger,
+ tileServices,
+ testScope.backgroundScope,
+ )
+ }
+
+val Kosmos.customTileUserActionInteractor: CustomTileUserActionInteractor by
+ Kosmos.Fixture {
+ CustomTileUserActionInteractor(
+ testCase.context,
+ tileSpec,
+ qsTileLogger,
+ mock {},
+ mock {},
+ FakeQSTileIntentUserInputHandler(),
+ testDispatcher,
+ customTileServiceInteractor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/QSTileStateSubject.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/QSTileStateSubject.kt
index 9d0faca94fb4..4f5c9b48690d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/QSTileStateSubject.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/QSTileStateSubject.kt
@@ -70,7 +70,7 @@ private constructor(failureMetadata: FailureMetadata, subject: QSTileState?) :
}
/** Shortcut for `Truth.assertAbout(states()).that(state)`. */
- fun assertThat(state: QSTileState?): QSTileStateSubject =
- Truth.assertAbout(states()).that(state)
+ fun assertThat(actual: QSTileState?): QSTileStateSubject =
+ Truth.assertAbout(states()).that(actual)
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt
index b1581d1771fd..4d902fa35204 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt
@@ -41,6 +41,8 @@ class FakeQSSceneAdapter(
private val _navBarPadding = MutableStateFlow<Int>(0)
val navBarPadding = _navBarPadding.asStateFlow()
+ override var isQsFullyCollapsed: Boolean = true
+
override suspend fun inflate(context: Context) {
_view.value = inflateDelegate(context)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterKosmos.kt
new file mode 100644
index 000000000000..00ab0b57fab7
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterKosmos.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 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.qs.ui.adapter
+
+import android.view.View
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+var Kosmos.fakeQSSceneAdapter by Kosmos.Fixture { FakeQSSceneAdapter({ mock<View>() }) }
+
+val Kosmos.qsSceneAdapter: QSSceneAdapter by Kosmos.Fixture { fakeQSSceneAdapter }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt
index fc023758fdf6..ef7aa6308491 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt
@@ -19,7 +19,6 @@ package com.android.systemui.scene.domain.interactor
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
-import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.scene.data.repository.sceneContainerRepository
import com.android.systemui.scene.shared.logger.sceneLogger
@@ -28,7 +27,6 @@ val Kosmos.sceneInteractor by
SceneInteractor(
applicationScope = applicationCoroutineScope,
repository = sceneContainerRepository,
- powerInteractor = powerInteractor,
logger = sceneLogger,
deviceUnlockedInteractor = deviceUnlockedInteractor,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt
index 82e0b8e83f24..f4acf4d8fb53 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt
@@ -73,7 +73,7 @@ val Kosmos.shadeControllerImpl by
deviceProvisionedController,
mock<NotificationShadeWindowController>(),
mock<WindowManager>(),
- { mock<ShadeViewController>() },
+ { mock<NotificationPanelViewController>() },
{ mock<AssistManager>() },
{ mock<NotificationGutsManager>() },
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakePrivacyChipRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakePrivacyChipRepository.kt
new file mode 100644
index 000000000000..5bc61e23cd6e
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakePrivacyChipRepository.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.shade.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.privacy.PrivacyItem
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+
+/** Fake implementation of [PrivacyChipRepository] */
+@SysUISingleton
+class FakePrivacyChipRepository @Inject constructor() : PrivacyChipRepository {
+ private val _isSafetyCenterEnabled = MutableStateFlow(false)
+ override val isSafetyCenterEnabled = _isSafetyCenterEnabled
+
+ private val _privacyItems: MutableStateFlow<List<PrivacyItem>> = MutableStateFlow(emptyList())
+ override val privacyItems = _privacyItems
+
+ private val _isMicCameraIndicationEnabled = MutableStateFlow(false)
+ override val isMicCameraIndicationEnabled = _isMicCameraIndicationEnabled
+
+ private val _isLocationIndicationEnabled = MutableStateFlow(false)
+ override val isLocationIndicationEnabled = _isLocationIndicationEnabled
+
+ fun setIsSafetyCenterEnabled(value: Boolean) {
+ _isSafetyCenterEnabled.value = value
+ }
+
+ fun setPrivacyItems(value: List<PrivacyItem>) {
+ _privacyItems.value = value
+ }
+
+ fun setIsMicCameraIndicationEnabled(value: Boolean) {
+ _isMicCameraIndicationEnabled.value = value
+ }
+
+ fun setIsLocationIndicationEnabled(value: Boolean) {
+ _isLocationIndicationEnabled.value = value
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/PrivacyChipRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/PrivacyChipRepositoryKosmos.kt
new file mode 100644
index 000000000000..2428c6156720
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/PrivacyChipRepositoryKosmos.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.shade.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+
+var Kosmos.privacyChipRepository: PrivacyChipRepository by
+ Kosmos.Fixture { fakePrivacyChipRepository }
+val Kosmos.fakePrivacyChipRepository: FakePrivacyChipRepository by
+ Kosmos.Fixture { FakePrivacyChipRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TileLifecycleManagerFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeHeaderClockRepositoryKosmos.kt
index f8ce707b0bb2..ecc3c9e27208 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TileLifecycleManagerFactoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeHeaderClockRepositoryKosmos.kt
@@ -14,11 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.qs.external
+package com.android.systemui.shade.data.repository
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.statusbar.policy.nextAlarmController
-/** Returns mocks */
-var Kosmos.tileLifecycleManagerFactory: TileLifecycleManager.Factory by
- Kosmos.Fixture { TileLifecycleManager.Factory { _, _ -> mock<TileLifecycleManager>() } }
+var Kosmos.shadeHeaderClockRepository: ShadeHeaderClockRepository by
+ Kosmos.Fixture { ShadeHeaderClockRepository(nextAlarmController) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/PrivacyChipInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/PrivacyChipInteractorKosmos.kt
new file mode 100644
index 000000000000..7334286f00c4
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/PrivacyChipInteractorKosmos.kt
@@ -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.shade.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.privacy.privacyDialogController
+import com.android.systemui.privacy.privacyDialogControllerV2
+import com.android.systemui.shade.data.repository.fakePrivacyChipRepository
+import com.android.systemui.statusbar.policy.deviceProvisionedController
+
+var Kosmos.privacyChipInteractor: PrivacyChipInteractor by
+ Kosmos.Fixture {
+ PrivacyChipInteractor(
+ applicationScope = applicationCoroutineScope,
+ repository = fakePrivacyChipRepository,
+ privacyDialogController = privacyDialogController,
+ privacyDialogControllerV2 = privacyDialogControllerV2,
+ deviceProvisionedController = deviceProvisionedController,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeHeaderClockInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeHeaderClockInteractorKosmos.kt
new file mode 100644
index 000000000000..6fd7cf6edbe4
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeHeaderClockInteractorKosmos.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.shade.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.plugins.activityStarter
+import com.android.systemui.shade.data.repository.shadeHeaderClockRepository
+
+var Kosmos.shadeHeaderClockInteractor: ShadeHeaderClockInteractor by
+ Kosmos.Fixture {
+ ShadeHeaderClockInteractor(
+ repository = shadeHeaderClockRepository,
+ activityStarter = activityStarter,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt
new file mode 100644
index 000000000000..4221d06e27ed
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 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.shade.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.shadeLockscreenInteractor by
+ Kosmos.Fixture {
+ ShadeLockscreenInteractorImpl(
+ scope = testScope,
+ shadeInteractor = shadeInteractorImpl,
+ sceneInteractor = sceneInteractor,
+ lockIconViewController = mock(),
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerKosmos.kt
index 1c6ce7987cd5..e4a3896378f6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerKosmos.kt
@@ -24,11 +24,12 @@ import com.android.systemui.keyguard.domain.interactor.naturalScrollingSettingOb
import com.android.systemui.keyguard.wakefulnessLifecycle
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.media.controls.ui.mediaHierarchyManager
+import com.android.systemui.media.controls.ui.controller.mediaHierarchyManager
import com.android.systemui.plugins.activityStarter
-import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.qs.ui.adapter.qsSceneAdapter
import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.shade.domain.interactor.shadeLockscreenInteractor
import com.android.systemui.statusbar.notification.stack.ambientState
import com.android.systemui.statusbar.phone.keyguardBypassController
import com.android.systemui.statusbar.phone.lsShadeTransitionLogger
@@ -58,8 +59,9 @@ val Kosmos.lockscreenShadeTransitionController by Fixture {
qsTransitionControllerFactory = lockscreenShadeQsTransitionControllerFactory,
shadeRepository = shadeRepository,
shadeInteractor = shadeInteractor,
- powerInteractor = powerInteractor,
splitShadeStateController = splitShadeStateController,
+ shadeLockscreenInteractorLazy = { shadeLockscreenInteractor },
naturalScrollingSettingObserver = naturalScrollingSettingObserver,
+ lazyQSSceneAdapter = { qsSceneAdapter }
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractorKosmos.kt
new file mode 100644
index 000000000000..0614309a3910
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsSoundPolicyInteractorKosmos.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.domain.interactor
+
+import com.android.settingslib.statusbar.notification.data.repository.FakeNotificationsSoundPolicyRepository
+import com.android.settingslib.statusbar.notification.domain.interactor.NotificationsSoundPolicyInteractor
+import com.android.systemui.kosmos.Kosmos
+
+var Kosmos.notificationsSoundPolicyRepository by
+ Kosmos.Fixture { FakeNotificationsSoundPolicyRepository() }
+
+val Kosmos.notificationsSoundPolicyInteractor: NotificationsSoundPolicyInteractor by
+ Kosmos.Fixture { NotificationsSoundPolicyInteractor(notificationsSoundPolicyRepository) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/data/repository/HeadsUpNotificationRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/data/repository/HeadsUpNotificationRepositoryKosmos.kt
new file mode 100644
index 000000000000..25864aee2136
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/data/repository/HeadsUpNotificationRepositoryKosmos.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.stack.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.statusbar.notification.data.repository.HeadsUpNotificationRepository
+import kotlinx.coroutines.flow.MutableStateFlow
+
+val Kosmos.headsUpNotificationRepository by Fixture { FakeHeadsUpNotificationRepository() }
+
+class FakeHeadsUpNotificationRepository : HeadsUpNotificationRepository {
+ override val hasPinnedHeadsUp = MutableStateFlow(false)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HeadsUpNotificationInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HeadsUpNotificationInteractorKosmos.kt
new file mode 100644
index 000000000000..d3451075c51b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HeadsUpNotificationInteractorKosmos.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.stack.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor
+import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository
+
+val Kosmos.headsUpNotificationInteractor by Fixture {
+ HeadsUpNotificationInteractor(headsUpNotificationRepository)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
index 8882de06637c..832344d7a822 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
@@ -25,6 +25,7 @@ import com.android.systemui.keyguard.ui.viewmodel.aodToLockscreenTransitionViewM
import com.android.systemui.keyguard.ui.viewmodel.dozingToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.dreamingToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.glanceableHubToLockscreenTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.goneToAodTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.goneToDozingTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.goneToDreamingTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.lockscreenToDreamingTransitionViewModel
@@ -41,7 +42,9 @@ import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+@OptIn(ExperimentalCoroutinesApi::class)
val Kosmos.sharedNotificationContainerViewModel by Fixture {
SharedNotificationContainerViewModel(
interactor = sharedNotificationContainerInteractor,
@@ -54,6 +57,7 @@ val Kosmos.sharedNotificationContainerViewModel by Fixture {
aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel,
dozingToLockscreenTransitionViewModel = dozingToLockscreenTransitionViewModel,
dreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel,
+ goneToAodTransitionViewModel = goneToAodTransitionViewModel,
goneToDozingTransitionViewModel = goneToDozingTransitionViewModel,
goneToDreamingTransitionViewModel = goneToDreamingTransitionViewModel,
glanceableHubToLockscreenTransitionViewModel = glanceableHubToLockscreenTransitionViewModel,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt
index c51de334c8ca..46a10532ea52 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt
@@ -43,6 +43,7 @@ class FakeConfigurationController @Inject constructor() : ConfigurationControlle
}
override fun isLayoutRtl(): Boolean = isRtl
+ override fun getNightModeName(): String = "undefined"
}
@Module
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeDeviceProvisionedController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeDeviceProvisionedController.kt
index 0c2b115a8af5..aa2c2a2e1ec3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeDeviceProvisionedController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeDeviceProvisionedController.kt
@@ -2,30 +2,45 @@ package com.android.systemui.statusbar.policy
class FakeDeviceProvisionedController : DeviceProvisionedController {
@JvmField var deviceProvisioned = true
+ @JvmField var currentUser = 0
+
+ private val callbacks = mutableSetOf<DeviceProvisionedController.DeviceProvisionedListener>()
+ private val usersSetup = mutableSetOf<Int>()
override fun addCallback(listener: DeviceProvisionedController.DeviceProvisionedListener) {
- TODO("Not yet implemented")
+ callbacks.add(listener)
}
override fun removeCallback(listener: DeviceProvisionedController.DeviceProvisionedListener) {
- TODO("Not yet implemented")
+ callbacks.remove(listener)
}
override fun isDeviceProvisioned() = deviceProvisioned
+ @Deprecated("Deprecated in Java")
override fun getCurrentUser(): Int {
- TODO("Not yet implemented")
+ return currentUser
}
override fun isUserSetup(user: Int): Boolean {
- TODO("Not yet implemented")
+ return user in usersSetup
}
override fun isCurrentUserSetup(): Boolean {
- TODO("Not yet implemented")
+ return currentUser in usersSetup
}
override fun isFrpActive(): Boolean {
TODO("Not yet implemented")
}
+
+ fun setCurrentUser(userId: Int) {
+ currentUser = userId
+ callbacks.toSet().forEach { it.onUserSwitched() }
+ }
+
+ fun setUserSetup(userId: Int) {
+ usersSetup.add(userId)
+ callbacks.toSet().forEach { it.onUserSetupChanged() }
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/NextAlarmControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/NextAlarmControllerKosmos.kt
new file mode 100644
index 000000000000..860c3fa016ae
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/NextAlarmControllerKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+var Kosmos.nextAlarmController: NextAlarmController by
+ Kosmos.Fixture { mock<NextAlarmController>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
index 1124425e5d49..3e9ae4d2e354 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
@@ -39,13 +39,19 @@ class FakeUserRepository @Inject constructor() : UserRepository {
// User id to represent a non system (human) user id. We presume this is the main user.
private const val MAIN_USER_ID = 10
- private val DEFAULT_SELECTED_USER = 0
+ private const val DEFAULT_SELECTED_USER = 0
private val DEFAULT_SELECTED_USER_INFO =
UserInfo(
/* id= */ DEFAULT_SELECTED_USER,
/* name= */ "default selected user",
/* flags= */ 0,
)
+ private val MAIN_USER =
+ UserInfo(
+ /* id= */ MAIN_USER_ID,
+ /* name= */ "main user",
+ /* flags= */ UserInfo.FLAG_MAIN,
+ )
}
private val _userSwitcherSettings = MutableStateFlow(UserSwitcherSettingsModel())
@@ -113,6 +119,20 @@ class FakeUserRepository @Inject constructor() : UserRepository {
yield()
}
+ /** Resets the current user to the default of [DEFAULT_SELECTED_USER_INFO]. */
+ suspend fun asDefaultUser(): UserInfo {
+ setUserInfos(listOf(DEFAULT_SELECTED_USER_INFO))
+ setSelectedUserInfo(DEFAULT_SELECTED_USER_INFO)
+ return DEFAULT_SELECTED_USER_INFO
+ }
+
+ /** Makes the current user [MAIN_USER]. */
+ suspend fun asMainUser(): UserInfo {
+ setUserInfos(listOf(MAIN_USER))
+ setSelectedUserInfo(MAIN_USER)
+ return MAIN_USER
+ }
+
suspend fun setSettings(settings: UserSwitcherSettingsModel) {
_userSwitcherSettings.value = settings
yield()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeConfigurationController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeConfigurationController.java
index 516eb6e6dffd..111c40d49efc 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeConfigurationController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeConfigurationController.java
@@ -38,4 +38,9 @@ public class FakeConfigurationController
public boolean isLayoutRtl() {
return false;
}
+
+ @Override
+ public String getNightModeName() {
+ return "undefined";
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt
new file mode 100644
index 000000000000..3f20df3376d9
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2024 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.volume
+
+import android.content.packageManager
+import android.content.pm.ApplicationInfo
+import android.media.session.MediaController
+import android.os.Handler
+import android.testing.TestableLooper
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.media.mediaOutputDialogFactory
+import com.android.systemui.plugins.activityStarter
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.volume.data.repository.FakeLocalMediaRepository
+import com.android.systemui.volume.data.repository.FakeMediaControllerRepository
+import com.android.systemui.volume.panel.component.mediaoutput.data.repository.FakeLocalMediaRepositoryFactory
+import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactory
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputActionsInteractor
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor
+
+var Kosmos.mediaController: MediaController by Kosmos.Fixture { mock {} }
+
+val Kosmos.localMediaRepository by Kosmos.Fixture { FakeLocalMediaRepository() }
+val Kosmos.localMediaRepositoryFactory: LocalMediaRepositoryFactory by
+ Kosmos.Fixture { FakeLocalMediaRepositoryFactory { localMediaRepository } }
+
+val Kosmos.mediaOutputActionsInteractor by
+ Kosmos.Fixture { MediaOutputActionsInteractor(mediaOutputDialogFactory, activityStarter) }
+val Kosmos.mediaControllerRepository by Kosmos.Fixture { FakeMediaControllerRepository() }
+val Kosmos.mediaOutputInteractor by
+ Kosmos.Fixture {
+ MediaOutputInteractor(
+ localMediaRepositoryFactory,
+ packageManager.apply {
+ val appInfo: ApplicationInfo = mock {
+ whenever(loadLabel(any())).thenReturn("test_label")
+ }
+ whenever(getApplicationInfo(any(), any<Int>())).thenReturn(appInfo)
+ },
+ testScope.backgroundScope,
+ testScope.testScheduler,
+ Handler(TestableLooper.get(testCase).looper),
+ mediaControllerRepository,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/VolumeKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/VolumeKosmos.kt
new file mode 100644
index 000000000000..5e1f85c70a1b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/VolumeKosmos.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 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.volume
+
+import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.volume.data.repository.FakeAudioRepository
+
+val Kosmos.audioRepository by Kosmos.Fixture { FakeAudioRepository() }
+val Kosmos.audioModeInteractor by Kosmos.Fixture { AudioModeInteractor(audioRepository) }
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeAudioRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt
index dddf8e82d5f7..a3ad2b87d5f5 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeAudioRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-package com.android.settingslib.volume.data.repository
+package com.android.systemui.volume.data.repository
import android.media.AudioDeviceInfo
+import com.android.settingslib.volume.data.repository.AudioRepository
import com.android.settingslib.volume.shared.model.AudioStream
import com.android.settingslib.volume.shared.model.AudioStreamModel
import com.android.settingslib.volume.shared.model.RingerMode
@@ -41,6 +42,7 @@ class FakeAudioRepository : AudioRepository {
get() = mutableCommunicationDevice.asStateFlow()
private val models: MutableMap<AudioStream, MutableStateFlow<AudioStreamModel>> = mutableMapOf()
+ private val lastAudibleVolumes: MutableMap<AudioStream, Int> = mutableMapOf()
private fun getAudioStreamModelState(
audioStream: AudioStream
@@ -58,12 +60,9 @@ class FakeAudioRepository : AudioRepository {
)
}
- override suspend fun getAudioStream(audioStream: AudioStream): Flow<AudioStreamModel> =
+ override fun getAudioStream(audioStream: AudioStream): Flow<AudioStreamModel> =
getAudioStreamModelState(audioStream).asStateFlow()
- override suspend fun getCurrentAudioStream(audioStream: AudioStream): AudioStreamModel =
- getAudioStreamModelState(audioStream).value
-
override suspend fun setVolume(audioStream: AudioStream, volume: Int) {
getAudioStreamModelState(audioStream).update { it.copy(volume = volume) }
}
@@ -72,6 +71,9 @@ class FakeAudioRepository : AudioRepository {
getAudioStreamModelState(audioStream).update { it.copy(isMuted = isMuted) }
}
+ override suspend fun getLastAudibleVolume(audioStream: AudioStream): Int =
+ lastAudibleVolumes.getOrDefault(audioStream, 0)
+
fun setMode(newMode: Int) {
mutableMode.value = newMode
}
@@ -87,4 +89,8 @@ class FakeAudioRepository : AudioRepository {
fun setAudioStreamModel(model: AudioStreamModel) {
getAudioStreamModelState(model.audioStream).update { model }
}
+
+ fun setLastAudibleVolume(audioStream: AudioStream, volume: Int) {
+ lastAudibleVolumes[audioStream] = volume
+ }
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeLocalMediaRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeLocalMediaRepository.kt
index 642b72c70e55..284bd55f15d7 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeLocalMediaRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeLocalMediaRepository.kt
@@ -1,23 +1,24 @@
/*
* Copyright (C) 2024 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
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * 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.
+ * 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.volume.data.repository
+package com.android.systemui.volume.data.repository
import com.android.settingslib.media.MediaDevice
import com.android.settingslib.volume.data.model.RoutingSession
+import com.android.settingslib.volume.data.repository.LocalMediaRepository
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -26,20 +27,19 @@ class FakeLocalMediaRepository : LocalMediaRepository {
private val volumeBySession: MutableMap<String?, Int> = mutableMapOf()
- private val mutableMediaDevices = MutableStateFlow<Collection<MediaDevice>>(emptyList())
- override val mediaDevices: StateFlow<Collection<MediaDevice>>
+ private val mutableMediaDevices = MutableStateFlow<List<MediaDevice>>(emptyList())
+ override val mediaDevices: StateFlow<List<MediaDevice>>
get() = mutableMediaDevices.asStateFlow()
private val mutableCurrentConnectedDevice = MutableStateFlow<MediaDevice?>(null)
override val currentConnectedDevice: StateFlow<MediaDevice?>
get() = mutableCurrentConnectedDevice.asStateFlow()
- private val mutableRemoteRoutingSessions =
- MutableStateFlow<Collection<RoutingSession>>(emptyList())
- override val remoteRoutingSessions: StateFlow<Collection<RoutingSession>>
+ private val mutableRemoteRoutingSessions = MutableStateFlow<List<RoutingSession>>(emptyList())
+ override val remoteRoutingSessions: StateFlow<List<RoutingSession>>
get() = mutableRemoteRoutingSessions.asStateFlow()
- fun updateMediaDevices(devices: Collection<MediaDevice>) {
+ fun updateMediaDevices(devices: List<MediaDevice>) {
mutableMediaDevices.value = devices
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeMediaControllerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeMediaControllerRepository.kt
new file mode 100644
index 000000000000..6d52e525d238
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeMediaControllerRepository.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 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.volume.data.repository
+
+import android.media.session.MediaController
+import com.android.settingslib.volume.data.repository.MediaControllerRepository
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+class FakeMediaControllerRepository : MediaControllerRepository {
+
+ private val mutableActiveLocalMediaController = MutableStateFlow<MediaController?>(null)
+ override val activeLocalMediaController: StateFlow<MediaController?> =
+ mutableActiveLocalMediaController.asStateFlow()
+
+ fun setActiveLocalMediaController(controller: MediaController?) {
+ mutableActiveLocalMediaController.value = controller
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/MediaOutputComponentKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/MediaOutputComponentKosmos.kt
new file mode 100644
index 000000000000..ad8ccb00f45f
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/MediaOutputComponentKosmos.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 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.volume.panel
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.media.mediaOutputDialogFactory
+import com.android.systemui.plugins.activityStarter
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputActionsInteractor
+
+val Kosmos.mediaOutputActionsInteractor by
+ Kosmos.Fixture { MediaOutputActionsInteractor(mediaOutputDialogFactory, activityStarter) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/FakeSliceFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/FakeSliceFactory.kt
new file mode 100644
index 000000000000..fc406eaae0f1
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/FakeSliceFactory.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.anc
+
+import androidx.slice.Slice
+import androidx.slice.SliceItem
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+
+object FakeSliceFactory {
+
+ fun createSlice(hasError: Boolean, hasSliceItem: Boolean): Slice {
+ return mock {
+ val sliceItem: SliceItem = mock {
+ whenever(format).thenReturn(android.app.slice.SliceItem.FORMAT_SLICE)
+ }
+
+ whenever(items)
+ .thenReturn(
+ buildList {
+ if (hasSliceItem) {
+ add(sliceItem)
+ }
+ }
+ )
+
+ whenever(hints)
+ .thenReturn(
+ buildList {
+ if (hasError) {
+ add(android.app.slice.Slice.HINT_ERROR)
+ }
+ }
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/VolumePanelAncKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/VolumePanelAncKosmos.kt
new file mode 100644
index 000000000000..f9b7e69eea7d
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/VolumePanelAncKosmos.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.anc
+
+import androidx.slice.SliceViewManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.volume.panel.component.anc.data.repository.FakeAncSliceRepository
+import com.android.systemui.volume.panel.component.anc.domain.interactor.AncSliceInteractor
+
+var Kosmos.sliceViewManager: SliceViewManager by Kosmos.Fixture { mock {} }
+val Kosmos.ancSliceRepository by Kosmos.Fixture { FakeAncSliceRepository() }
+val Kosmos.ancSliceInteractor by
+ Kosmos.Fixture { AncSliceInteractor(ancSliceRepository, testScope.backgroundScope) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/data/repository/FakeAncSliceRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/data/repository/FakeAncSliceRepository.kt
new file mode 100644
index 000000000000..b66d7f974eca
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/data/repository/FakeAncSliceRepository.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.anc.data.repository
+
+import androidx.slice.Slice
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class FakeAncSliceRepository : AncSliceRepository {
+
+ private val sliceByWidth = mutableMapOf<Int, MutableStateFlow<Slice?>>()
+
+ override fun ancSlice(width: Int): Flow<Slice?> =
+ sliceByWidth.getOrPut(width) { MutableStateFlow(null) }
+
+ fun putSlice(width: Int, slice: Slice?) {
+ sliceByWidth.getOrPut(width) { MutableStateFlow(null) }.value = slice
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/FakeLocalMediaRepositoryFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/FakeLocalMediaRepositoryFactory.kt
new file mode 100644
index 000000000000..1b3480c423e4
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/FakeLocalMediaRepositoryFactory.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 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.volume.panel.component.mediaoutput.data.repository
+
+import com.android.settingslib.volume.data.repository.LocalMediaRepository
+
+class FakeLocalMediaRepositoryFactory(
+ val provider: (packageName: String?) -> LocalMediaRepository
+) : LocalMediaRepositoryFactory {
+
+ override fun create(packageName: String?): LocalMediaRepository = provider(packageName)
+}
diff --git a/packages/VpnDialogs/res/values-mr/strings.xml b/packages/VpnDialogs/res/values-mr/strings.xml
index cccf3695a537..fc11ce08f8de 100644
--- a/packages/VpnDialogs/res/values-mr/strings.xml
+++ b/packages/VpnDialogs/res/values-mr/strings.xml
@@ -30,7 +30,7 @@
<string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"<xliff:g id="VPN_APP">%1$s</xliff:g> हे पूर्ण वेळ कनेक्ट राहण्यासाठी सेट अप केलेले आहे, पण हे आता कनेक्ट होऊ शकत नाही. VPN पुन्हा कनेक्ट होईपर्यंत तुमच्याकडे कनेक्शन नसेल."</string>
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
<string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN सेटिंग्ज बदला"</string>
- <string name="configure" msgid="4905518375574791375">"कॉंफिगर करा"</string>
+ <string name="configure" msgid="4905518375574791375">"कॉन्फिगर करा"</string>
<string name="disconnect" msgid="971412338304200056">"‍डिस्कनेक्ट करा"</string>
<string name="open_app" msgid="3717639178595958667">"अ‍ॅप उघडा"</string>
<string name="dismiss" msgid="6192859333764711227">"डिसमिस करा"</string>
diff --git a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
index 053ed779a27a..3ecdf3f101a5 100644
--- a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
+++ b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
@@ -36,9 +36,11 @@ import static com.android.window.flags.Flags.multiCrop;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -57,6 +59,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.graphics.Rect;
import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
import android.os.UserHandle;
@@ -78,6 +81,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -87,6 +91,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
@RunWith(AndroidJUnit4.class)
@@ -815,6 +820,48 @@ public class WallpaperBackupAgentTest {
WallpaperEventLogger.ERROR_LIVE_PACKAGE_NOT_INSTALLED);
}
+ @Test
+ public void testOnRestore_noCropHints() throws Exception {
+ testParseCropHints(Map.of());
+ }
+
+ @Test
+ public void testOnRestore_singleCropHint() throws Exception {
+ Map<Integer, Rect> testMap = Map.of(WallpaperManager.PORTRAIT, new Rect(1, 2, 3, 4));
+ testParseCropHints(testMap);
+ }
+
+ @Test
+ public void testOnRestore_multipleCropHints() throws Exception {
+ Map<Integer, Rect> testMap = Map.of(
+ WallpaperManager.PORTRAIT, new Rect(1, 2, 3, 4),
+ WallpaperManager.SQUARE_PORTRAIT, new Rect(5, 6, 7, 8),
+ WallpaperManager.SQUARE_LANDSCAPE, new Rect(9, 10, 11, 12));
+ testParseCropHints(testMap);
+ }
+
+ private void testParseCropHints(Map<Integer, Rect> testMap) throws Exception {
+ assumeTrue(multiCrop());
+ mockRestoredStaticWallpaperFile(testMap);
+ mockStagedWallpaperFile(SYSTEM_WALLPAPER_STAGE);
+ mWallpaperBackupAgent.onCreate(USER_HANDLE, BackupAnnotations.BackupDestination.CLOUD,
+ BackupAnnotations.OperationType.RESTORE);
+
+ mWallpaperBackupAgent.onRestoreFinished();
+
+ ArgumentMatcher<SparseArray<Rect>> matcher = array -> {
+ boolean result = testMap.entrySet().stream().allMatch(entry -> {
+ int key = entry.getKey();
+ return (array.contains(key) && array.get(key).equals(testMap.get(key)));
+ });
+ for (int i = 0; i < array.size(); i++) {
+ if (!testMap.containsKey(array.keyAt(i))) result = false;
+ }
+ return result;
+ };
+ verify(mWallpaperManager).setStreamWithCrops(any(), argThat(matcher), eq(true), anyInt());
+ }
+
private void mockCurrentWallpaperIds(int systemWallpaperId, int lockWallpaperId) {
when(mWallpaperManager.getWallpaperId(eq(FLAG_SYSTEM))).thenReturn(systemWallpaperId);
when(mWallpaperManager.getWallpaperId(eq(FLAG_LOCK))).thenReturn(lockWallpaperId);
@@ -880,6 +927,34 @@ public class WallpaperBackupAgentTest {
fstream.close();
}
+ private void mockRestoredStaticWallpaperFile(Map<Integer, Rect> crops) throws Exception {
+ File wallpaperFile = new File(mContext.getFilesDir(), WALLPAPER_INFO_STAGE);
+ wallpaperFile.createNewFile();
+ FileOutputStream fstream = new FileOutputStream(wallpaperFile, false);
+ TypedXmlSerializer out = Xml.resolveSerializer(fstream);
+ out.startDocument(null, true);
+ out.startTag(null, "wp");
+ for (Map.Entry<Integer, Rect> entry: crops.entrySet()) {
+ String orientation = switch (entry.getKey()) {
+ case WallpaperManager.PORTRAIT -> "Portrait";
+ case WallpaperManager.LANDSCAPE -> "Landscape";
+ case WallpaperManager.SQUARE_PORTRAIT -> "SquarePortrait";
+ case WallpaperManager.SQUARE_LANDSCAPE -> "SquareLandscape";
+ default -> throw new IllegalArgumentException("Invalid orientation");
+ };
+ Rect rect = entry.getValue();
+ out.attributeInt(null, "cropLeft" + orientation, rect.left);
+ out.attributeInt(null, "cropTop" + orientation, rect.top);
+ out.attributeInt(null, "cropRight" + orientation, rect.right);
+ out.attributeInt(null, "cropBottom" + orientation, rect.bottom);
+ }
+ out.endTag(null, "wp");
+ out.endDocument();
+ fstream.flush();
+ FileUtils.sync(fstream);
+ fstream.close();
+ }
+
private WallpaperInfo getFakeWallpaperInfo() throws Exception {
Context context = InstrumentationRegistry.getTargetContext();
Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
index c134a4c5b2ad..e0fe88a1a167 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
@@ -1204,23 +1204,26 @@ public class CameraExtensionsProxyService extends Service {
List<Pair<CameraCharacteristics.Key, Object>> entries =
mAdvancedExtender.getAvailableCharacteristicsKeyValues();
- if ((entries != null) && !entries.isEmpty()) {
- CameraMetadataNative ret = new CameraMetadataNative();
- long vendorId = mMetadataVendorIdMap.containsKey(cameraId)
- ? mMetadataVendorIdMap.get(cameraId) : Long.MAX_VALUE;
- ret.setVendorId(vendorId);
- int[] characteristicsKeyTags = new int[entries.size()];
- int i = 0;
- for (Pair<CameraCharacteristics.Key, Object> entry : entries) {
- int tag = CameraMetadataNative.getTag(entry.first.getName(), vendorId);
- characteristicsKeyTags[i++] = tag;
- ret.set(entry.first, entry.second);
- }
- ret.set(CameraCharacteristics.REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,
- characteristicsKeyTags);
+ if (entries == null || entries.isEmpty()) {
+ throw new RuntimeException("A valid set of key/value pairs are required that "
+ + "are supported by the extension.");
+ }
- return ret;
+ CameraMetadataNative ret = new CameraMetadataNative();
+ long vendorId = mMetadataVendorIdMap.containsKey(cameraId)
+ ? mMetadataVendorIdMap.get(cameraId) : Long.MAX_VALUE;
+ ret.setVendorId(vendorId);
+ int[] characteristicsKeyTags = new int[entries.size()];
+ int i = 0;
+ for (Pair<CameraCharacteristics.Key, Object> entry : entries) {
+ int tag = CameraMetadataNative.getTag(entry.first.getName(), vendorId);
+ characteristicsKeyTags[i++] = tag;
+ ret.set(entry.first, entry.second);
}
+ ret.set(CameraCharacteristics.REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,
+ characteristicsKeyTags);
+
+ return ret;
}
return null;
@@ -1274,6 +1277,20 @@ public class CameraExtensionsProxyService extends Service {
}
@Override
+ public void onCaptureFailed(int captureSequenceId, int reason) {
+ if (Flags.concertMode()) {
+ if (mCaptureCallback != null) {
+ try {
+ mCaptureCallback.onCaptureProcessFailed(captureSequenceId, reason);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to notify capture failure due to remote " +
+ "exception!");
+ }
+ }
+ }
+ }
+
+ @Override
public void onCaptureSequenceCompleted(int captureSequenceId) {
if (mCaptureCallback != null) {
try {
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index 35ce4814f97d..53897e14ecd6 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -24,6 +24,46 @@ java_library {
visibility: ["//visibility:public"],
}
+java_library_host {
+ name: "ravenwood-helper-libcore-runtime.host",
+ srcs: [
+ "runtime-helper-src/libcore-fake/**/*.java",
+ ],
+ visibility: ["//visibility:private"],
+}
+
+java_host_for_device {
+ name: "ravenwood-helper-libcore-runtime",
+ libs: [
+ "ravenwood-helper-libcore-runtime.host",
+ ],
+ visibility: ["//visibility:private"],
+}
+
+java_library {
+ name: "ravenwood-helper-framework-runtime",
+ srcs: [
+ "runtime-helper-src/framework/**/*.java",
+ ],
+ libs: [
+ "framework-minus-apex.ravenwood",
+ ],
+ visibility: ["//visibility:private"],
+}
+
+// Combine ravenwood-helper-*-runtime and create a single library, which we include
+// in the ravenwood runtime.
+// We do it this way rather than including the individual jars in the runtime, because
+// for some reason we couldn't include a java_host_for_device module in the ravenwood runtime.
+java_library {
+ name: "ravenwood-helper-runtime",
+ defaults: ["ravenwood-internal-only-visibility-java"],
+ static_libs: [
+ "ravenwood-helper-framework-runtime",
+ "ravenwood-helper-libcore-runtime",
+ ],
+}
+
java_library {
name: "ravenwood-junit-impl",
srcs: [
@@ -34,11 +74,14 @@ java_library {
"androidx.test.monitor-for-device",
],
libs: [
+ "android.test.mock",
"framework-minus-apex.ravenwood",
+ "services.core.ravenwood",
"junit",
],
sdk_version: "core_current",
visibility: ["//frameworks/base"],
+ jarjar_rules: ":ravenwood-services-jarjar-rules",
}
// Carefully compiles against only test_current to support tests that
@@ -58,16 +101,6 @@ java_library {
visibility: ["//visibility:public"],
}
-java_library {
- // Prefixed with "200" to ensure it's sorted early in Tradefed classpath
- // so that we provide a concrete implementation before Mainline stubs
- name: "200-kxml2-android",
- static_libs: [
- "kxml2-android",
- ],
- visibility: ["//frameworks/base"],
-}
-
java_host_for_device {
name: "androidx.test.monitor-for-device",
libs: [
@@ -81,3 +114,9 @@ java_device_for_host {
"androidx.test.monitor",
],
}
+
+filegroup {
+ name: "ravenwood-services-jarjar-rules",
+ srcs: ["ravenwood-services-jarjar-rules.txt"],
+ visibility: ["//frameworks/base"],
+}
diff --git a/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodReplace.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodReplace.java
index a920f63152fb..83a7b6e54389 100644
--- a/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodReplace.java
+++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodReplace.java
@@ -32,4 +32,14 @@ import java.lang.annotation.Target;
@Target({METHOD})
@Retention(RetentionPolicy.CLASS)
public @interface RavenwoodReplace {
+ /**
+ * One or more classes that aren't yet supported by Ravenwood, which is why this method is
+ * being replaced.
+ */
+ Class<?>[] blockedBy() default {};
+
+ /**
+ * General free-form description of why this method is being replaced.
+ */
+ String reason() default "";
}
diff --git a/ravenwood/api-maintainers.md b/ravenwood/api-maintainers.md
index d84cb6795fef..4b2f96804c97 100644
--- a/ravenwood/api-maintainers.md
+++ b/ravenwood/api-maintainers.md
@@ -82,7 +82,7 @@ When a pure-Java implementation grows too large or complex to host within the or
```
@RavenwoodKeepWholeClass
-@RavenwoodNativeSubstitutionClass("com.android.hoststubgen.nativesubstitution.MyComplexClass_host")
+@RavenwoodNativeSubstitutionClass("com.android.platform.test.ravenwood.nativesubstitution.MyComplexClass_host")
public class MyComplexClass {
private static native void nativeDoThing(long nativePtr);
...
diff --git a/ravenwood/framework-minus-apex-ravenwood-policies.txt b/ravenwood/framework-minus-apex-ravenwood-policies.txt
index 49cef07033c1..6b6736476210 100644
--- a/ravenwood/framework-minus-apex-ravenwood-policies.txt
+++ b/ravenwood/framework-minus-apex-ravenwood-policies.txt
@@ -52,5 +52,6 @@ class android.content.BroadcastReceiver stub
method <init> ()V stub
class android.content.Context stub
method <init> ()V stub
+ method getSystemService (Ljava/lang/Class;)Ljava/lang/Object; stub
class android.content.pm.PackageManager stub
method <init> ()V stub
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
new file mode 100644
index 000000000000..3668b03e58d3
--- /dev/null
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2024 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.platform.test.ravenwood;
+
+import android.content.Context;
+import android.hardware.ISerialManager;
+import android.hardware.SerialManager;
+import android.os.PermissionEnforcer;
+import android.os.ServiceManager;
+import android.test.mock.MockContext;
+import android.util.ArrayMap;
+import android.util.Singleton;
+
+import java.util.function.Supplier;
+
+public class RavenwoodContext extends MockContext {
+ private final RavenwoodPermissionEnforcer mEnforcer = new RavenwoodPermissionEnforcer();
+
+ private final ArrayMap<Class<?>, String> mClassToName = new ArrayMap<>();
+ private final ArrayMap<String, Supplier<?>> mNameToFactory = new ArrayMap<>();
+
+ private void registerService(Class<?> serviceClass, String serviceName,
+ Supplier<?> serviceSupplier) {
+ mClassToName.put(serviceClass, serviceName);
+ mNameToFactory.put(serviceName, serviceSupplier);
+ }
+
+ public RavenwoodContext() {
+ registerService(PermissionEnforcer.class,
+ Context.PERMISSION_ENFORCER_SERVICE, () -> mEnforcer);
+ registerService(SerialManager.class,
+ Context.SERIAL_SERVICE, asSingleton(() ->
+ new SerialManager(this, ISerialManager.Stub.asInterface(
+ ServiceManager.getService(Context.SERIAL_SERVICE)))
+ ));
+ }
+
+ @Override
+ public Object getSystemService(String serviceName) {
+ // TODO: pivot to using SystemServiceRegistry
+ final Supplier<?> serviceSupplier = mNameToFactory.get(serviceName);
+ if (serviceSupplier != null) {
+ return serviceSupplier.get();
+ } else {
+ throw new UnsupportedOperationException(
+ "Service " + serviceName + " not yet supported under Ravenwood");
+ }
+ }
+
+ @Override
+ public String getSystemServiceName(Class<?> serviceClass) {
+ // TODO: pivot to using SystemServiceRegistry
+ final String serviceName = mClassToName.get(serviceClass);
+ if (serviceName != null) {
+ return serviceName;
+ } else {
+ throw new UnsupportedOperationException(
+ "Service " + serviceClass + " not yet supported under Ravenwood");
+ }
+ }
+
+ /**
+ * Wrap the given {@link Supplier} to become a memoized singleton.
+ */
+ private static <T> Supplier<T> asSingleton(Supplier<T> supplier) {
+ final Singleton<T> singleton = new Singleton<>() {
+ @Override
+ protected T create() {
+ return supplier.get();
+ }
+ };
+ return () -> {
+ return singleton.get();
+ };
+ }
+}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodPermissionEnforcer.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodPermissionEnforcer.java
new file mode 100644
index 000000000000..42441352536e
--- /dev/null
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodPermissionEnforcer.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 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.platform.test.ravenwood;
+
+import static android.permission.PermissionManager.PERMISSION_GRANTED;
+
+import android.content.AttributionSource;
+import android.os.PermissionEnforcer;
+
+public class RavenwoodPermissionEnforcer extends PermissionEnforcer {
+ @Override
+ protected int checkPermission(String permission, AttributionSource source) {
+ // For the moment, since Ravenwood doesn't offer cross-process capabilities, assume all
+ // permissions are granted during tests
+ return PERMISSION_GRANTED;
+ }
+
+ @Override
+ protected int checkPermission(String permission, int pid, int uid) {
+ // For the moment, since Ravenwood doesn't offer cross-process capabilities, assume all
+ // permissions are granted during tests
+ return PERMISSION_GRANTED;
+ }
+}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
index 7b5932b42b84..231cce95f353 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
@@ -16,24 +16,35 @@
package android.platform.test.ravenwood;
+import static org.junit.Assert.assertFalse;
+
import android.app.ActivityManager;
import android.app.Instrumentation;
import android.os.Build;
import android.os.Bundle;
import android.os.HandlerThread;
import android.os.Looper;
+import android.os.ServiceManager;
import android.util.Log;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.internal.os.RuntimeInit;
+import com.android.server.LocalServices;
+import org.junit.After;
import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
import org.junit.runner.Description;
import org.junit.runner.RunWith;
import org.junit.runners.model.Statement;
import java.io.PrintStream;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Executors;
@@ -94,9 +105,10 @@ public class RavenwoodRuleImpl {
rule.mSystemProperties.getKeyReadablePredicate(),
rule.mSystemProperties.getKeyWritablePredicate());
- ActivityManager.init$ravenwood(rule.mCurrentUser);
+ ServiceManager.init$ravenwood();
+ LocalServices.removeAllServicesForTest();
- com.android.server.LocalServices.removeAllServicesForTest();
+ ActivityManager.init$ravenwood(rule.mCurrentUser);
if (rule.mProvideMainThread) {
final HandlerThread main = new HandlerThread(MAIN_THREAD_NAME);
@@ -104,7 +116,12 @@ public class RavenwoodRuleImpl {
Looper.setMainLooperForTest(main.getLooper());
}
- InstrumentationRegistry.registerInstance(new Instrumentation(), Bundle.EMPTY);
+ rule.mContext = new RavenwoodContext();
+ rule.mInstrumentation = new Instrumentation();
+ rule.mInstrumentation.basicInit(rule.mContext);
+ InstrumentationRegistry.registerInstance(rule.mInstrumentation, Bundle.EMPTY);
+
+ RavenwoodSystemServer.init(rule);
if (ENABLE_TIMEOUT_STACKS) {
sPendingTimeout = sTimeoutExecutor.schedule(RavenwoodRuleImpl::dumpStacks,
@@ -112,7 +129,7 @@ public class RavenwoodRuleImpl {
}
// Touch some references early to ensure they're <clinit>'ed
- Objects.requireNonNull(Build.IS_USERDEBUG);
+ Objects.requireNonNull(Build.TYPE);
Objects.requireNonNull(Build.VERSION.SDK);
}
@@ -121,17 +138,22 @@ public class RavenwoodRuleImpl {
sPendingTimeout.cancel(false);
}
+ RavenwoodSystemServer.reset(rule);
+
InstrumentationRegistry.registerInstance(null, Bundle.EMPTY);
+ rule.mInstrumentation = null;
+ rule.mContext = null;
if (rule.mProvideMainThread) {
Looper.getMainLooper().quit();
Looper.clearMainLooperForTest();
}
- com.android.server.LocalServices.removeAllServicesForTest();
-
ActivityManager.reset$ravenwood();
+ LocalServices.removeAllServicesForTest();
+ ServiceManager.reset$ravenwood();
+
android.os.SystemProperties.reset$ravenwood();
android.os.Binder.reset$ravenwood();
android.os.Process.reset$ravenwood();
@@ -183,6 +205,7 @@ public class RavenwoodRuleImpl {
public static void validate(Statement base, Description description,
boolean enableOptionalValidation) {
validateTestRunner(base, description, enableOptionalValidation);
+ validateTestAnnotations(base, description, enableOptionalValidation);
}
private static void validateTestRunner(Statement base, Description description,
@@ -206,4 +229,63 @@ public class RavenwoodRuleImpl {
}
}
}
+
+ private static void validateTestAnnotations(Statement base, Description description,
+ boolean enableOptionalValidation) {
+ final var testClass = description.getTestClass();
+
+ final var message = new StringBuilder();
+
+ boolean hasErrors = false;
+ for (Method m : collectMethods(testClass)) {
+ if (Modifier.isPublic(m.getModifiers()) && m.getName().startsWith("test")) {
+ if (m.getAnnotation(Test.class) == null) {
+ message.append("\nMethod " + m.getName() + "() doesn't have @Test");
+ hasErrors = true;
+ }
+ }
+ if ("setUp".equals(m.getName())) {
+ if (m.getAnnotation(Before.class) == null) {
+ message.append("\nMethod " + m.getName() + "() doesn't have @Before");
+ hasErrors = true;
+ }
+ if (!Modifier.isPublic(m.getModifiers())) {
+ message.append("\nMethod " + m.getName() + "() must be public");
+ hasErrors = true;
+ }
+ }
+ if ("tearDown".equals(m.getName())) {
+ if (m.getAnnotation(After.class) == null) {
+ message.append("\nMethod " + m.getName() + "() doesn't have @After");
+ hasErrors = true;
+ }
+ if (!Modifier.isPublic(m.getModifiers())) {
+ message.append("\nMethod " + m.getName() + "() must be public");
+ hasErrors = true;
+ }
+ }
+ }
+ assertFalse("Problem(s) detected in class " + testClass.getCanonicalName() + ":"
+ + message, hasErrors);
+ }
+
+ /**
+ * Collect all (public or private or any) methods in a class, including inherited methods.
+ */
+ private static List<Method> collectMethods(Class<?> clazz) {
+ var ret = new ArrayList<Method>();
+ collectMethods(clazz, ret);
+ return ret;
+ }
+
+ private static void collectMethods(Class<?> clazz, List<Method> result) {
+ // Class.getMethods() only return public methods, so we need to use getDeclaredMethods()
+ // instead, and recurse.
+ for (var m : clazz.getDeclaredMethods()) {
+ result.add(m);
+ }
+ if (clazz.getSuperclass() != null) {
+ collectMethods(clazz.getSuperclass(), result);
+ }
+ }
}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
new file mode 100644
index 000000000000..bb280f47ccd9
--- /dev/null
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2024 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.platform.test.ravenwood;
+
+import android.hardware.SerialManager;
+import android.os.SystemClock;
+import android.util.ArrayMap;
+
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.SystemServiceManager;
+import com.android.server.utils.TimingsTraceAndSlog;
+
+public class RavenwoodSystemServer {
+ /**
+ * Set of services that we know how to provide under Ravenwood. We keep this set distinct
+ * from {@code com.android.server.SystemServer} to give us the ability to choose either
+ * "real" or "fake" implementations based on the commitments of the service owner.
+ *
+ * Map from {@code FooManager.class} to the {@code com.android.server.SystemService}
+ * lifecycle class name used to instantiate and drive that service.
+ */
+ private static final ArrayMap<Class<?>, String> sKnownServices = new ArrayMap<>();
+
+ // TODO: expand SystemService API to support dependency expression, so we don't need test
+ // authors to exhaustively declare all transitive services
+
+ static {
+ sKnownServices.put(SerialManager.class, "com.android.server.SerialService$Lifecycle");
+ }
+
+ private static TimingsTraceAndSlog sTimings;
+ private static SystemServiceManager sServiceManager;
+
+ public static void init(RavenwoodRule rule) {
+ // Avoid overhead if no services required
+ if (rule.mServicesRequired.isEmpty()) return;
+
+ sTimings = new TimingsTraceAndSlog();
+ sServiceManager = new SystemServiceManager(rule.mContext);
+ sServiceManager.setStartInfo(false,
+ SystemClock.elapsedRealtime(),
+ SystemClock.uptimeMillis());
+ LocalServices.addService(SystemServiceManager.class, sServiceManager);
+
+ for (Class<?> service : rule.mServicesRequired) {
+ final String target = sKnownServices.get(service);
+ if (target == null) {
+ throw new RuntimeException("The requested service " + service
+ + " is not yet supported under the Ravenwood deviceless testing "
+ + "environment; consider requesting support from the API owner or "
+ + "consider using Mockito; more details at go/ravenwood-docs");
+ } else {
+ sServiceManager.startService(target);
+ }
+ }
+ sServiceManager.sealStartedServices();
+
+ // TODO: expand to include additional boot phases when relevant
+ sServiceManager.startBootPhase(sTimings, SystemService.PHASE_SYSTEM_SERVICES_READY);
+ sServiceManager.startBootPhase(sTimings, SystemService.PHASE_BOOT_COMPLETED);
+ }
+
+ public static void reset(RavenwoodRule rule) {
+ // TODO: consider introducing shutdown boot phases
+
+ LocalServices.removeServiceForTest(SystemServiceManager.class);
+ sServiceManager = null;
+ sTimings = null;
+ }
+}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index b90f112c1655..a8c24fcbd7e0 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -22,10 +22,13 @@ import static android.os.UserHandle.USER_SYSTEM;
import static org.junit.Assert.fail;
+import android.app.Instrumentation;
+import android.content.Context;
import android.platform.test.annotations.DisabledOnNonRavenwood;
import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.EnabledOnRavenwood;
import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.util.ArraySet;
import org.junit.Assume;
import org.junit.rules.TestRule;
@@ -122,6 +125,11 @@ public class RavenwoodRule implements TestRule {
final RavenwoodSystemProperties mSystemProperties = new RavenwoodSystemProperties();
+ final ArraySet<Class<?>> mServicesRequired = new ArraySet<>();
+
+ volatile Context mContext;
+ volatile Instrumentation mInstrumentation;
+
public RavenwoodRule() {
}
@@ -192,6 +200,23 @@ public class RavenwoodRule implements TestRule {
return this;
}
+ /**
+ * Configure the set of system services that are required for this test to operate.
+ *
+ * For example, passing {@code android.hardware.SerialManager.class} as an argument will
+ * ensure that the underlying service is created, initialized, and ready to use for the
+ * duration of the test. The {@code SerialManager} instance can be obtained via
+ * {@code RavenwoodRule.getContext()} and {@code Context.getSystemService()}, and
+ * {@code SerialManagerInternal} can be obtained via {@code LocalServices.getService()}.
+ */
+ public Builder setServicesRequired(Class<?>... services) {
+ mRule.mServicesRequired.clear();
+ for (Class<?> service : services) {
+ mRule.mServicesRequired.add(service);
+ }
+ return this;
+ }
+
public RavenwoodRule build() {
return mRule;
}
@@ -212,6 +237,28 @@ public class RavenwoodRule implements TestRule {
return IS_ON_RAVENWOOD;
}
+ /**
+ * Return a {@code Context} available for usage during the currently running test case.
+ *
+ * Each test should obtain needed information or references via this method;
+ * references must not be stored beyond the scope of a test case.
+ */
+ public Context getContext() {
+ return Objects.requireNonNull(mContext,
+ "Context is only available during @Test execution");
+ }
+
+ /**
+ * Return a {@code Instrumentation} available for usage during the currently running test case.
+ *
+ * Each test should obtain needed information or references via this method;
+ * references must not be stored beyond the scope of a test case.
+ */
+ public Instrumentation getInstrumentation() {
+ return Objects.requireNonNull(mInstrumentation,
+ "Instrumentation is only available during @Test execution");
+ }
+
static boolean shouldEnableOnDevice(Description description) {
if (description.isTest()) {
if (description.getAnnotation(DisabledOnNonRavenwood.class) != null) {
diff --git a/ravenwood/ravenwood-annotation-allowed-classes.txt b/ravenwood/ravenwood-annotation-allowed-classes.txt
index b5baef666b52..eb3c55cb4ff6 100644
--- a/ravenwood/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/ravenwood-annotation-allowed-classes.txt
@@ -99,6 +99,7 @@ android.util.SparseSetArray
android.util.StringBuilderPrinter
android.util.TeeWriter
android.util.TimeUtils
+android.util.TimingsTraceLog
android.util.UtilConfig
android.util.Xml
@@ -152,6 +153,7 @@ android.os.ParcelFormatException
android.os.ParcelUuid
android.os.Parcelable
android.os.PatternMatcher
+android.os.PermissionEnforcer
android.os.PersistableBundle
android.os.PowerComponents
android.os.Process
@@ -159,6 +161,8 @@ android.os.RemoteCallback
android.os.RemoteCallbackList
android.os.RemoteException
android.os.ResultReceiver
+android.os.ServiceManager
+android.os.ServiceManager$ServiceNotFoundException
android.os.ServiceSpecificException
android.os.StrictMode
android.os.SystemClock
@@ -251,6 +255,10 @@ android.view.Display
android.view.Display$HdrCapabilities
android.view.Display$Mode
android.view.DisplayInfo
+android.view.inputmethod.InputBinding
+
+android.hardware.SerialManager
+android.hardware.SerialManagerInternal
android.telephony.ActivityStatsTechSpecificInfo
android.telephony.CellSignalStrength
@@ -310,3 +318,9 @@ com.android.internal.os.StoragedUidIoStatsReader
com.google.android.collect.Lists
com.google.android.collect.Maps
com.google.android.collect.Sets
+
+com.android.server.SerialService
+com.android.server.SystemService
+com.android.server.SystemServiceManager
+
+com.android.server.utils.TimingsTraceAndSlog
diff --git a/ravenwood/ravenwood-services-jarjar-rules.txt b/ravenwood/ravenwood-services-jarjar-rules.txt
new file mode 100644
index 000000000000..8fdd3408f74d
--- /dev/null
+++ b/ravenwood/ravenwood-services-jarjar-rules.txt
@@ -0,0 +1,11 @@
+# Ignore one-off class defined out in core/java/
+rule com.android.server.LocalServices @0
+rule com.android.server.pm.pkg.AndroidPackage @0
+rule com.android.server.pm.pkg.AndroidPackageSplit @0
+
+# Rename all other service internals so that tests can continue to statically
+# link services code when owners aren't ready to support on Ravenwood
+rule com.android.server.** repackaged.@0
+
+# TODO: support AIDL generated Parcelables via hoststubgen
+rule android.hardware.power.stats.** repackaged.@0
diff --git a/ravenwood/run-ravenwood-tests.sh b/ravenwood/run-ravenwood-tests.sh
index 3f4b8a79e864..a303626bb445 100755
--- a/ravenwood/run-ravenwood-tests.sh
+++ b/ravenwood/run-ravenwood-tests.sh
@@ -13,10 +13,16 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# Run all the ravenwood tests.
+# Run all the ravenwood tests + hoststubgen unit tests.
+
+all_tests="hoststubgentest tiny-framework-dump-test hoststubgen-invoke-test"
# "echo" is to remove the newlines
-all_tests=$(echo $(${0%/*}/list-ravenwood-tests.sh) )
+all_tests="$all_tests $(echo $(${0%/*}/list-ravenwood-tests.sh) )"
+
+run() {
+ echo "Running: $*"
+ "${@}"
+}
-echo "Running tests: $all_tests"
-atest $all_tests
+run ${ATEST:-atest} $all_tests
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/CursorWindow_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/CursorWindow_host.java
index eba99107f126..f38d5653d3a9 100644
--- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/CursorWindow_host.java
+++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/CursorWindow_host.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.hoststubgen.nativesubstitution;
+package com.android.platform.test.ravenwood.nativesubstitution;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/EventLog_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/EventLog_host.java
index 6480cfc2b492..55d4ffb41e78 100644
--- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/EventLog_host.java
+++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/EventLog_host.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.hoststubgen.nativesubstitution;
+package com.android.platform.test.ravenwood.nativesubstitution;
import com.android.internal.os.RuntimeInit;
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Log_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/Log_host.java
index cdfa30276961..5930a14cdec8 100644
--- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Log_host.java
+++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/Log_host.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.hoststubgen.nativesubstitution;
+package com.android.platform.test.ravenwood.nativesubstitution;
import android.util.Log;
import android.util.Log.Level;
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/LongArrayMultiStateCounter_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/LongArrayMultiStateCounter_host.java
index 4d39d88d58c3..741411095f53 100644
--- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/LongArrayMultiStateCounter_host.java
+++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/LongArrayMultiStateCounter_host.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.hoststubgen.nativesubstitution;
+package com.android.platform.test.ravenwood.nativesubstitution;
import android.os.BadParcelableException;
import android.os.Parcel;
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/LongMultiStateCounter_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/LongMultiStateCounter_host.java
index a5d0fc6872de..9486651ce48d 100644
--- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/LongMultiStateCounter_host.java
+++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/LongMultiStateCounter_host.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.hoststubgen.nativesubstitution;
+package com.android.platform.test.ravenwood.nativesubstitution;
import android.os.BadParcelableException;
import android.os.Parcel;
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/MessageQueue_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/MessageQueue_host.java
index 65da4a144160..5e81124b6e70 100644
--- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/MessageQueue_host.java
+++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/MessageQueue_host.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.hoststubgen.nativesubstitution;
+package com.android.platform.test.ravenwood.nativesubstitution;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/ParcelFileDescriptor_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/ParcelFileDescriptor_host.java
index 0ebaac6016e2..2d799142df70 100644
--- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/ParcelFileDescriptor_host.java
+++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/ParcelFileDescriptor_host.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.hoststubgen.nativesubstitution;
+package com.android.platform.test.ravenwood.nativesubstitution;
import static android.os.ParcelFileDescriptor.MODE_APPEND;
import static android.os.ParcelFileDescriptor.MODE_CREATE;
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/Parcel_host.java
index d63bff6f4da3..81ad31e631fe 100644
--- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java
+++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/Parcel_host.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.hoststubgen.nativesubstitution;
+package com.android.platform.test.ravenwood.nativesubstitution;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/SystemProperties_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/SystemProperties_host.java
index 2f6a361e3609..eba6c8b2db64 100644
--- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/SystemProperties_host.java
+++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/SystemProperties_host.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.hoststubgen.nativesubstitution;
+package com.android.platform.test.ravenwood.nativesubstitution;
import android.util.SparseArray;
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/runtimehelper/ClassLoadHook.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java
index fbcc64892798..1e120305fa2d 100644
--- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/runtimehelper/ClassLoadHook.java
+++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java
@@ -13,9 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.hoststubgen.runtimehelper;
-
-import com.android.hoststubgen.hosthelper.HostTestException;
+package com.android.platform.test.ravenwood.runtimehelper;
import java.io.File;
import java.io.PrintStream;
@@ -79,7 +77,7 @@ public class ClassLoadHook {
private static void ensurePropertyNotSet(String key) {
if (System.getProperty(key) != null) {
- throw new HostTestException("System property \"" + key + "\" is set unexpectedly");
+ throw new RuntimeException("System property \"" + key + "\" is set unexpectedly");
}
}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/android/system/ErrnoException.java b/ravenwood/runtime-helper-src/libcore-fake/android/system/ErrnoException.java
index 388156aa3694..388156aa3694 100644
--- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/android/system/ErrnoException.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/android/system/ErrnoException.java
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/com/android/okhttp/internalandroidapi/Dns.java b/ravenwood/runtime-helper-src/libcore-fake/com/android/okhttp/internalandroidapi/Dns.java
index 379c4ae8a059..379c4ae8a059 100644
--- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/com/android/okhttp/internalandroidapi/Dns.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/com/android/okhttp/internalandroidapi/Dns.java
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/dalvik/system/VMRuntime.java b/ravenwood/runtime-helper-src/libcore-fake/dalvik/system/VMRuntime.java
index 7d2b00d9420d..7d2b00d9420d 100644
--- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/dalvik/system/VMRuntime.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/dalvik/system/VMRuntime.java
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/io/IoUtils.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/io/IoUtils.java
index 65c285e06bf8..65c285e06bf8 100644
--- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/io/IoUtils.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/io/IoUtils.java
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/EmptyArray.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/EmptyArray.java
index a1ae35a88656..a1ae35a88656 100644
--- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/EmptyArray.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/EmptyArray.java
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/HexEncoding.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/HexEncoding.java
index cc2fb7bbf236..cc2fb7bbf236 100644
--- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/HexEncoding.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/HexEncoding.java
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/SneakyThrow.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/SneakyThrow.java
index e142c46bc311..e142c46bc311 100644
--- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/libcore/util/SneakyThrow.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/SneakyThrow.java
diff --git a/ravenwood/services-test/Android.bp b/ravenwood/services-test/Android.bp
new file mode 100644
index 000000000000..39858f05e80d
--- /dev/null
+++ b/ravenwood/services-test/Android.bp
@@ -0,0 +1,21 @@
+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_ravenwood_test {
+ name: "RavenwoodServicesTest",
+ static_libs: [
+ "androidx.annotation_annotation",
+ "androidx.test.ext.junit",
+ "androidx.test.rules",
+ ],
+ srcs: [
+ "test/**/*.java",
+ ],
+ auto_gen_config: true,
+}
diff --git a/ravenwood/services-test/test/com/android/ravenwood/RavenwoodServicesTest.java b/ravenwood/services-test/test/com/android/ravenwood/RavenwoodServicesTest.java
new file mode 100644
index 000000000000..c1dee5d2f55b
--- /dev/null
+++ b/ravenwood/services-test/test/com/android/ravenwood/RavenwoodServicesTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2024 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.ravenwood;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.content.Context;
+import android.hardware.SerialManager;
+import android.hardware.SerialManagerInternal;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class RavenwoodServicesTest {
+ private static final String TEST_VIRTUAL_PORT = "virtual:example";
+
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+ .setProcessSystem()
+ .setServicesRequired(SerialManager.class)
+ .build();
+
+ @Test
+ public void testDefined() {
+ final SerialManager fromName = (SerialManager)
+ mRavenwood.getContext().getSystemService(Context.SERIAL_SERVICE);
+ final SerialManager fromClass =
+ mRavenwood.getContext().getSystemService(SerialManager.class);
+ assertNotNull(fromName);
+ assertNotNull(fromClass);
+ assertEquals(fromName, fromClass);
+
+ assertNotNull(LocalServices.getService(SerialManagerInternal.class));
+ }
+
+ @Test
+ public void testSimple() {
+ // Verify that we can obtain a manager, and talk to the backend service, and that no
+ // serial ports are configured by default
+ final SerialManager service = (SerialManager)
+ mRavenwood.getContext().getSystemService(Context.SERIAL_SERVICE);
+ final String[] ports = service.getSerialPorts();
+ assertEquals(0, ports.length);
+ }
+
+ @Test
+ public void testDriven() {
+ final SerialManager service = (SerialManager)
+ mRavenwood.getContext().getSystemService(Context.SERIAL_SERVICE);
+ final SerialManagerInternal internal = LocalServices.getService(
+ SerialManagerInternal.class);
+
+ internal.addVirtualSerialPortForTest(TEST_VIRTUAL_PORT, () -> {
+ throw new UnsupportedOperationException(
+ "Needs socketpair() to offer accurate emulation");
+ });
+ final String[] ports = service.getSerialPorts();
+ assertEquals(1, ports.length);
+ assertEquals(TEST_VIRTUAL_PORT, ports[0]);
+ }
+}
diff --git a/ravenwood/services.core-ravenwood-policies.txt b/ravenwood/services.core-ravenwood-policies.txt
new file mode 100644
index 000000000000..d8d563e05435
--- /dev/null
+++ b/ravenwood/services.core-ravenwood-policies.txt
@@ -0,0 +1,7 @@
+# Ravenwood "policy" file for services.core.
+
+# Keep all AIDL interfaces
+class :aidl stubclass
+
+# Keep all feature flag implementations
+class :feature_flags stubclass
diff --git a/rs/java/android/renderscript/ScriptC.java b/rs/java/android/renderscript/ScriptC.java
index 67c2caa338a6..4a2f3da0eb06 100644
--- a/rs/java/android/renderscript/ScriptC.java
+++ b/rs/java/android/renderscript/ScriptC.java
@@ -101,7 +101,19 @@ public class ScriptC extends Script {
setID(id);
}
- private static void throwExceptionIfSDKTooHigh() {
+ private static void throwExceptionIfScriptCUnsupported() {
+ // Checks that this device actually does have an ABI that supports ScriptC.
+ //
+ // For an explanation as to why `System.loadLibrary` is used, see discussion at
+ // https://android-review.googlesource.com/c/platform/frameworks/base/+/2957974/comment/2f908b80_a05292ee
+ try {
+ System.loadLibrary("RS");
+ } catch (UnsatisfiedLinkError e) {
+ String s = "This device does not have an ABI that supports ScriptC.";
+ throw new UnsupportedOperationException(s);
+ }
+
+ // Throw an exception if the target API is 35 or above
String message =
"ScriptC scripts are not supported when targeting an API Level >= 35. Please refer "
+ "to https://developer.android.com/guide/topics/renderscript/migration-guide "
@@ -113,7 +125,7 @@ public class ScriptC extends Script {
}
private static synchronized long internalCreate(RenderScript rs, Resources resources, int resourceID) {
- throwExceptionIfSDKTooHigh();
+ throwExceptionIfScriptCUnsupported();
byte[] pgm;
int pgmLength;
InputStream is = resources.openRawResource(resourceID);
@@ -150,7 +162,7 @@ public class ScriptC extends Script {
private static synchronized long internalStringCreate(RenderScript rs, String resName, byte[] bitcode) {
// Log.v(TAG, "Create script for resource = " + resName);
- throwExceptionIfSDKTooHigh();
+ throwExceptionIfScriptCUnsupported();
return rs.nScriptCCreate(resName, RenderScript.getCachePath(), bitcode, bitcode.length);
}
}
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index 015c35eed326..a754ba547767 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -24,6 +24,13 @@ flag {
}
flag {
+ name: "compute_window_changes_on_a11y"
+ namespace: "accessibility"
+ description: "Computes accessibility window changes in accessibility instead of wm package."
+ bug: "322444245"
+}
+
+flag {
name: "deprecate_package_list_observer"
namespace: "accessibility"
description: "Stops using the deprecated PackageListObserver."
@@ -66,6 +73,16 @@ flag {
}
flag {
+ name: "handle_multi_device_input"
+ namespace: "accessibility"
+ description: "Select a single active device when a multi-device stream is received by AccessibilityInputFilter"
+ bug: "310014874"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "pinch_zoom_zero_min_span"
namespace: "accessibility"
description: "Whether to set min span of ScaleGestureDetector to zero."
@@ -80,6 +97,16 @@ flag {
}
flag {
+ name: "reset_hover_event_timer_on_action_up"
+ namespace: "accessibility"
+ description: "Reset the timer for sending hover events on receiving ACTION_UP to guarantee the correct amount of time is available between taps."
+ bug: "326260351"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "scan_packages_without_lock"
namespace: "accessibility"
description: "Scans packages for accessibility service/activity info without holding the A11yMS lock"
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index abcd8e2a2d7e..16119d11ee1e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -25,6 +25,7 @@ import android.annotation.NonNull;
import android.content.Context;
import android.graphics.Region;
import android.os.PowerManager;
+import android.os.SystemClock;
import android.provider.Settings;
import android.util.Slog;
import android.util.SparseArray;
@@ -35,6 +36,8 @@ import android.view.InputEvent;
import android.view.InputFilter;
import android.view.KeyEvent;
import android.view.MotionEvent;
+import android.view.MotionEvent.PointerCoords;
+import android.view.MotionEvent.PointerProperties;
import android.view.accessibility.AccessibilityEvent;
import com.android.server.LocalServices;
@@ -203,6 +206,62 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
private EventStreamState mKeyboardStreamState;
+ /**
+ * The last MotionEvent emitted from the input device that's currently active. This is used to
+ * keep track of which input device is currently active, and also to generate the cancel event
+ * if a new device becomes active.
+ */
+ private MotionEvent mLastActiveDeviceMotionEvent = null;
+
+ private static MotionEvent cancelMotion(MotionEvent event) {
+ if (event.getActionMasked() == MotionEvent.ACTION_CANCEL
+ || event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT
+ || event.getActionMasked() == MotionEvent.ACTION_UP) {
+ throw new IllegalArgumentException("Can't cancel " + event);
+ }
+ final int action;
+ if (event.getActionMasked() == MotionEvent.ACTION_HOVER_ENTER
+ || event.getActionMasked() == MotionEvent.ACTION_HOVER_MOVE) {
+ action = MotionEvent.ACTION_HOVER_EXIT;
+ } else {
+ action = MotionEvent.ACTION_CANCEL;
+ }
+
+ final int pointerCount;
+ if (event.getActionMasked() == MotionEvent.ACTION_POINTER_UP) {
+ pointerCount = event.getPointerCount() - 1;
+ } else {
+ pointerCount = event.getPointerCount();
+ }
+ final PointerProperties[] properties = new PointerProperties[pointerCount];
+ final PointerCoords[] coords = new PointerCoords[pointerCount];
+ int newPointerIndex = 0;
+ for (int i = 0; i < event.getPointerCount(); i++) {
+ if (event.getActionMasked() == MotionEvent.ACTION_POINTER_UP) {
+ if (event.getActionIndex() == i) {
+ // Skip the pointer that's going away
+ continue;
+ }
+ }
+ final PointerCoords c = new PointerCoords();
+ c.x = event.getX(i);
+ c.y = event.getY(i);
+ coords[newPointerIndex] = c;
+ final PointerProperties p = new PointerProperties();
+ p.id = event.getPointerId(i);
+ p.toolType = event.getToolType(i);
+ properties[newPointerIndex] = p;
+ newPointerIndex++;
+ }
+
+ return MotionEvent.obtain(event.getDownTime(), SystemClock.uptimeMillis(), action,
+ pointerCount, properties, coords,
+ event.getMetaState(), event.getButtonState(),
+ event.getXPrecision(), event.getYPrecision(), event.getDeviceId(),
+ event.getEdgeFlags(), event.getSource(), event.getDisplayId(), event.getFlags(),
+ event.getClassification());
+ }
+
AccessibilityInputFilter(Context context, AccessibilityManagerService service) {
this(context, service, new SparseArray<>(0));
}
@@ -260,6 +319,17 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
AccessibilityTrace.FLAGS_INPUT_FILTER,
"event=" + event + ";policyFlags=" + policyFlags);
}
+ if (Flags.handleMultiDeviceInput()) {
+ if (!shouldProcessMultiDeviceEvent(event, policyFlags)) {
+ // We are only allowing a single device to be active at a time.
+ return;
+ }
+ }
+
+ onInputEventInternal(event, policyFlags);
+ }
+
+ private void onInputEventInternal(InputEvent event, int policyFlags) {
if (mEventHandler.size() == 0) {
if (DEBUG) Slog.d(TAG, "No mEventHandler for event " + event);
super.onInputEvent(event, policyFlags);
@@ -353,6 +423,63 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
}
}
+ boolean shouldProcessMultiDeviceEvent(InputEvent event, int policyFlags) {
+ if (event instanceof MotionEvent motion) {
+ // Only allow 1 device to be sending motion events at a time
+ // If the event is from an active device, let it through.
+ // If the event is not from an active device, only let it through if it starts a new
+ // gesture like ACTION_DOWN or ACTION_HOVER_ENTER
+ final boolean eventIsFromCurrentDevice = mLastActiveDeviceMotionEvent != null
+ && mLastActiveDeviceMotionEvent.getDeviceId() == motion.getDeviceId();
+ final int actionMasked = motion.getActionMasked();
+ switch (actionMasked) {
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_HOVER_ENTER:
+ case MotionEvent.ACTION_HOVER_MOVE: {
+ if (mLastActiveDeviceMotionEvent != null
+ && mLastActiveDeviceMotionEvent.getDeviceId() != motion.getDeviceId()) {
+ // This is a new gesture from a new device. Cancel the existing state
+ // and let this through
+ MotionEvent canceled = cancelMotion(mLastActiveDeviceMotionEvent);
+ onInputEventInternal(canceled, policyFlags);
+ }
+ mLastActiveDeviceMotionEvent = MotionEvent.obtain(motion);
+ return true;
+ }
+ case MotionEvent.ACTION_MOVE:
+ case MotionEvent.ACTION_POINTER_DOWN:
+ case MotionEvent.ACTION_POINTER_UP: {
+ if (eventIsFromCurrentDevice) {
+ mLastActiveDeviceMotionEvent = MotionEvent.obtain(motion);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_HOVER_EXIT: {
+ if (eventIsFromCurrentDevice) {
+ // This is the last event of the gesture from this device.
+ mLastActiveDeviceMotionEvent = null;
+ return true;
+ } else {
+ // Event is from another device
+ return false;
+ }
+ }
+ default: {
+ if (mLastActiveDeviceMotionEvent != null
+ && event.getDeviceId() != mLastActiveDeviceMotionEvent.getDeviceId()) {
+ // This is an event from another device, ignore it.
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
private void processMotionEvent(EventStreamState state, MotionEvent event, int policyFlags) {
if (!state.shouldProcessScroll() && event.getActionMasked() == MotionEvent.ACTION_SCROLL) {
super.onInputEvent(event, policyFlags);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 3442a6ac56b3..46db624cf3c1 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -64,6 +64,7 @@ import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.app.RemoteAction;
import android.app.admin.DevicePolicyManager;
+import android.app.ecm.EnhancedConfirmationManager;
import android.appwidget.AppWidgetManagerInternal;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
@@ -112,6 +113,7 @@ import android.text.TextUtils;
import android.text.TextUtils.SimpleStringSplitter;
import android.util.ArraySet;
import android.util.IntArray;
+import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -4394,13 +4396,29 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
// permittedServices null means all accessibility services are allowed.
boolean allowed = permittedServices == null || permittedServices.contains(packageName);
if (allowed) {
- final AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
- final int mode = appOps.noteOpNoThrow(
- AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
- uid, packageName, /* attributionTag= */ null, /* message= */ null);
- final boolean ecmEnabled = mContext.getResources().getBoolean(
- R.bool.config_enhancedConfirmationModeEnabled);
- return !ecmEnabled || mode == AppOpsManager.MODE_ALLOWED;
+ if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
+ && android.security.Flags.extendEcmToAllSettings()) {
+ try {
+ return !mContext.getSystemService(EnhancedConfirmationManager.class)
+ .isRestricted(packageName,
+ AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(LOG_TAG, "Exception when retrieving package:" + packageName, e);
+ return false;
+ }
+ } else {
+ try {
+ final int mode = mContext.getSystemService(AppOpsManager.class)
+ .noteOpNoThrow(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
+ uid, packageName);
+ final boolean ecmEnabled = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_enhancedConfirmationModeEnabled);
+ return !ecmEnabled || mode == AppOpsManager.MODE_ALLOWED;
+ } catch (Exception e) {
+ // Fallback in case if app ops is not available in testing.
+ return false;
+ }
+ }
}
return false;
} finally {
@@ -4423,8 +4441,21 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return true;
}
- RestrictedLockUtils.sendShowRestrictedSettingDialogIntent(mContext,
- packageName, uid);
+ if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()
+ && android.security.Flags.extendEcmToAllSettings()) {
+ try {
+ Intent settingDialogIntent = mContext
+ .getSystemService(EnhancedConfirmationManager.class)
+ .createRestrictedSettingDialogIntent(packageName,
+ AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE);
+ mContext.startActivity(settingDialogIntent);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(LOG_TAG, "Exception when retrieving package:" + packageName, e);
+ }
+ } else {
+ RestrictedLockUtils.sendShowRestrictedSettingDialogIntent(mContext,
+ packageName, uid);
+ }
return true;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index 26c1bc904a1a..b8181505b9c4 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -19,6 +19,8 @@ package com.android.server.accessibility;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
@@ -27,6 +29,7 @@ import static com.android.server.accessibility.AbstractAccessibilityServiceConne
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.graphics.Point;
import android.graphics.Region;
import android.os.Binder;
import android.os.Handler;
@@ -37,6 +40,7 @@ import android.os.SystemClock;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
@@ -52,6 +56,7 @@ import android.view.accessibility.IAccessibilityInteractionConnection;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.accessibility.AccessibilitySecurityPolicy.AccessibilityUserManager;
import com.android.server.utils.Slogf;
+import com.android.server.wm.AccessibilityWindowsPopulator.AccessibilityWindow;
import com.android.server.wm.WindowManagerInternal;
import java.io.FileDescriptor;
@@ -60,6 +65,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
import java.util.stream.Collectors;
/**
@@ -440,7 +446,7 @@ public class AccessibilityWindowManager {
updateWindowsByWindowAttributesLocked(windows);
if (DEBUG) {
Slogf.i(LOG_TAG, "mDisplayId=%d, topFocusedDisplayId=%d, currentUserId=%d, "
- + "visibleBgUsers=%s", mDisplayId, topFocusedDisplayId,
+ + "visibleBgUsers=%s", mDisplayId, topFocusedDisplayId,
mAccessibilityUserManager.getCurrentUserIdLocked(),
mAccessibilityUserManager.getVisibleUserIdsLocked());
if (VERBOSE) {
@@ -460,7 +466,7 @@ public class AccessibilityWindowManager {
mTopFocusedWindowToken = topFocusedWindowToken;
if (DEBUG) {
Slogf.d(LOG_TAG, "onWindowsForAccessibilityChanged(): updating windows for "
- + "display %d and token %s",
+ + "display %d and token %s",
topFocusedDisplayId, topFocusedWindowToken);
}
cacheWindows(windows);
@@ -472,12 +478,168 @@ public class AccessibilityWindowManager {
}
else if (DEBUG) {
Slogf.d(LOG_TAG, "onWindowsForAccessibilityChanged(): NOT updating windows for "
- + "display %d and token %s",
+ + "display %d and token %s",
topFocusedDisplayId, topFocusedWindowToken);
}
}
}
+ /**
+ * Called when the windows for accessibility changed. This is called if
+ * {@link com.android.server.accessibility.Flags.FLAG_COMPUTE_WINDOW_CHANGES_ON_A11Y} is
+ * true.
+ *
+ * @param forceSend Send the windows for accessibility even if they haven't
+ * changed.
+ * @param topFocusedDisplayId The display Id which has the top focused window.
+ * @param topFocusedWindowToken The window token of top focused window.
+ * @param screenSize The size of the display that the change happened.
+ * @param windows The windows for accessibility.
+ */
+ @Override
+ public void onAccessibilityWindowsChanged(boolean forceSend, int topFocusedDisplayId,
+ @NonNull IBinder topFocusedWindowToken, @NonNull Point screenSize,
+ @NonNull List<AccessibilityWindow> windows) {
+ // TODO(b/322444245): Get a screenSize from DisplayManager#getDisplay(int)
+ // .getRealSize().
+ final List<WindowInfo> windowInfoList = createWindowInfoList(screenSize, windows);
+ onWindowsForAccessibilityChanged(forceSend, topFocusedDisplayId,
+ topFocusedWindowToken, windowInfoList);
+ }
+
+ private static List<WindowInfo> createWindowInfoList(@NonNull Point screenSize,
+ @NonNull List<AccessibilityWindow> visibleWindows) {
+ final Set<IBinder> addedWindows = new ArraySet<>();
+ final List<WindowInfo> windows = new ArrayList<>();
+
+ // Avoid allocating Region for each window.
+ final Region regionInWindow = new Region();
+ final Region touchableRegionInScreen = new Region();
+
+ // Iterate until we figure out what is touchable for the entire screen.
+ boolean focusedWindowAdded = false;
+ final Region unaccountedSpace = new Region(0, 0, screenSize.x, screenSize.y);
+ for (final AccessibilityWindow a11yWindow : visibleWindows) {
+ a11yWindow.getTouchableRegionInWindow(regionInWindow);
+ if (windowMattersToAccessibility(a11yWindow, regionInWindow, unaccountedSpace)) {
+ final WindowInfo window = a11yWindow.getWindowInfo();
+ if (window.token != null) {
+ // Even if token is null, the window will be used in calculating visible
+ // windows, but is excluded from the accessibility window list.
+ // TODO(b/322444245): We can call #updateWindowWithWindowAttributes() here.
+ window.regionInScreen.set(regionInWindow);
+ window.layer = addedWindows.size();
+ windows.add(window);
+ addedWindows.add(window.token);
+ }
+
+ if (windowMattersToUnaccountedSpaceComputation(a11yWindow)) {
+ // Account for the space this window takes.
+ a11yWindow.getTouchableRegionInScreen(touchableRegionInScreen);
+ unaccountedSpace.op(touchableRegionInScreen, unaccountedSpace,
+ Region.Op.REVERSE_DIFFERENCE);
+ }
+
+ focusedWindowAdded |= a11yWindow.isFocused();
+ } else if (a11yWindow.isUntouchableNavigationBar()
+ && a11yWindow.getSystemBarInsetsFrame() != null) {
+ // If this widow is navigation bar without touchable region, accounting the
+ // region of navigation bar inset because all touch events from this region
+ // would be received by launcher, i.e. this region is a un-touchable one
+ // for the application.
+ unaccountedSpace.op(
+ a11yWindow.getSystemBarInsetsFrame(),
+ unaccountedSpace,
+ Region.Op.REVERSE_DIFFERENCE);
+ }
+
+ if (unaccountedSpace.isEmpty() && focusedWindowAdded) {
+ break;
+ }
+ }
+
+ // Remove child/parent references to windows that were not added.
+ for (final WindowInfo window : windows) {
+ if (!addedWindows.contains(window.parentToken)) {
+ window.parentToken = null;
+ }
+ if (window.childTokens != null) {
+ final int childTokenCount = window.childTokens.size();
+ for (int j = childTokenCount - 1; j >= 0; j--) {
+ if (!addedWindows.contains(window.childTokens.get(j))) {
+ window.childTokens.remove(j);
+ }
+ }
+ // Leave the child token list if empty.
+ }
+ }
+
+ return windows;
+ }
+
+ private static boolean windowMattersToAccessibility(AccessibilityWindow a11yWindow,
+ Region regionInScreen, Region unaccountedSpace) {
+ if (a11yWindow.ignoreRecentsAnimationForAccessibility()) {
+ return false;
+ }
+
+ if (a11yWindow.isFocused()) {
+ return true;
+ }
+
+ // Ignore non-touchable windows, except the split-screen divider, which is
+ // occasionally non-touchable but still useful for identifying split-screen
+ // mode and the PIP menu.
+ if (!a11yWindow.isTouchable()
+ && a11yWindow.getType() != TYPE_DOCK_DIVIDER && !a11yWindow.isPIPMenu()) {
+ return false;
+ }
+
+ // If the window is completely covered by other windows - ignore.
+ if (unaccountedSpace.quickReject(regionInScreen)) {
+ return false;
+ }
+
+ // Add windows of certain types not covered by modal windows.
+ if (isReportedWindowType(a11yWindow.getType())) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private static boolean isReportedWindowType(int windowType) {
+ return (windowType != WindowManager.LayoutParams.TYPE_WALLPAPER
+ && windowType != WindowManager.LayoutParams.TYPE_BOOT_PROGRESS
+ && windowType != WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY
+ && windowType != WindowManager.LayoutParams.TYPE_DRAG
+ && windowType != WindowManager.LayoutParams.TYPE_INPUT_CONSUMER
+ && windowType != WindowManager.LayoutParams.TYPE_POINTER
+ && windowType != TYPE_MAGNIFICATION_OVERLAY
+ && windowType != WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY
+ && windowType != WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY
+ && windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION);
+ }
+
+ // Some windows should be excluded from unaccounted space computation, though they still
+ // should be reported
+ private static boolean windowMattersToUnaccountedSpaceComputation(
+ AccessibilityWindow a11yWindow) {
+ // Do not account space of trusted non-touchable windows, except the split-screen
+ // divider.
+ // If it's not trusted, touch events are not sent to the windows behind it.
+ if (!a11yWindow.isTouchable()
+ && (a11yWindow.getType() != TYPE_DOCK_DIVIDER)
+ && a11yWindow.isTrustedOverlay()) {
+ return false;
+ }
+
+ if (a11yWindow.getType() == WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
+ return false;
+ }
+ return true;
+ }
+
private void updateWindowsByWindowAttributesLocked(List<WindowInfo> windows) {
for (int i = windows.size() - 1; i >= 0; i--) {
final WindowInfo windowInfo = windows.get(i);
diff --git a/services/accessibility/java/com/android/server/accessibility/BrailleDisplayConnection.java b/services/accessibility/java/com/android/server/accessibility/BrailleDisplayConnection.java
index 1f18e15bb646..40b6ff01965e 100644
--- a/services/accessibility/java/com/android/server/accessibility/BrailleDisplayConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/BrailleDisplayConnection.java
@@ -42,7 +42,6 @@ import com.android.internal.annotations.VisibleForTesting;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -112,7 +111,7 @@ class BrailleDisplayConnection extends IBrailleDisplayConnection.Stub {
BrailleDisplayConnection(@NonNull Object lock,
@NonNull AccessibilityServiceConnection serviceConnection) {
this.mLock = Objects.requireNonNull(lock);
- this.mScanner = getDefaultNativeScanner(getDefaultNativeInterface());
+ this.mScanner = getDefaultNativeScanner(new DefaultNativeInterface());
this.mServiceConnection = Objects.requireNonNull(serviceConnection);
}
@@ -128,7 +127,7 @@ class BrailleDisplayConnection extends IBrailleDisplayConnection.Stub {
*/
@VisibleForTesting
interface BrailleDisplayScanner {
- Collection<Path> getHidrawNodePaths();
+ Collection<Path> getHidrawNodePaths(@NonNull Path directory);
byte[] getDeviceReportDescriptor(@NonNull Path path);
@@ -158,8 +157,9 @@ class BrailleDisplayConnection extends IBrailleDisplayConnection.Stub {
Objects.requireNonNull(expectedUniqueId);
this.mController = Objects.requireNonNull(controller);
+ final Path devicePath = Path.of("/dev");
final List<Pair<File, byte[]>> result = new ArrayList<>();
- final Collection<Path> hidrawNodePaths = mScanner.getHidrawNodePaths();
+ final Collection<Path> hidrawNodePaths = mScanner.getHidrawNodePaths(devicePath);
if (hidrawNodePaths == null) {
Slog.w(LOG_TAG, "Unable to access the HIDRAW node directory");
sendConnectionErrorLocked(FLAG_ERROR_CANNOT_ACCESS);
@@ -232,9 +232,63 @@ class BrailleDisplayConnection extends IBrailleDisplayConnection.Stub {
}
/** Returns true if this descriptor includes usages for the Braille display usage page 0x41. */
- private static boolean isBrailleDisplay(byte[] descriptor) {
- // TODO: b/316036493 - Check that descriptor includes 0x41 reports.
- return true;
+ @VisibleForTesting
+ static boolean isBrailleDisplay(byte[] descriptor) {
+ boolean foundMatch = false;
+ for (int i = 0; i < descriptor.length; i++) {
+ // HID Spec "6.2.2.2 Short Items" defines that the report descriptor is a collection of
+ // items: each item is a collection of bytes where the first byte defines info about
+ // the type of item and the following 0, 1, 2, or 4 bytes are data bytes for that item.
+ // All items in the HID descriptor are expected to be Short Items.
+ final byte itemInfo = descriptor[i];
+ if (!isHidItemShort(itemInfo)) {
+ Slog.w(LOG_TAG, "Item " + itemInfo + " declares unsupported long type");
+ return false;
+ }
+ final int dataSize = getHidItemDataSize(itemInfo);
+ if (i + dataSize >= descriptor.length) {
+ Slog.w(LOG_TAG, "Item " + itemInfo + " specifies size past the remaining bytes");
+ return false;
+ }
+ // The item we're looking for (usage page declaration) should have size 1.
+ if (dataSize == 1) {
+ final byte itemData = descriptor[i + 1];
+ if (isHidItemBrailleDisplayUsagePage(itemInfo, itemData)) {
+ foundMatch = true;
+ }
+ }
+ // Move to the next item by skipping past all data bytes in this item.
+ i += dataSize;
+ }
+ return foundMatch;
+ }
+
+ private static boolean isHidItemShort(byte itemInfo) {
+ // Info bits 7-4 describe the item type, and HID Spec "6.2.2.3 Long Items" says that long
+ // items always have type bits 1111. Otherwise, the item is a short item.
+ return (itemInfo & 0b1111_0000) != 0b1111_0000;
+ }
+
+ private static int getHidItemDataSize(byte itemInfo) {
+ // HID Spec "6.2.2.2 Short Items" says that info bits 0-1 specify the optional data size:
+ // 0, 1, 2, or 4 bytes.
+ return switch (itemInfo & 0b0000_0011) {
+ case 0b00 -> 0;
+ case 0b01 -> 1;
+ case 0b10 -> 2;
+ default -> 4;
+ };
+ }
+
+ private static boolean isHidItemBrailleDisplayUsagePage(byte itemInfo, byte itemData) {
+ // From HID Spec "6.2.2.7 Global Items"
+ final byte usagePageType = 0b0000_0100;
+ // From HID Usage Tables version 1.2.
+ final byte brailleDisplayUsagePage = 0x41;
+ // HID Spec "6.2.2.2 Short Items" says item info bits 2-7 describe the type and
+ // function of the item.
+ final byte itemType = (byte) (itemInfo & 0b1111_1100);
+ return itemType == usagePageType && itemData == brailleDisplayUsagePage;
}
/**
@@ -285,6 +339,9 @@ class BrailleDisplayConnection extends IBrailleDisplayConnection.Stub {
if (buffer.length > IBinder.getSuggestedMaxIpcSizeBytes()) {
Slog.e(LOG_TAG, "Requested write of size " + buffer.length
+ " which is larger than maximum " + IBinder.getSuggestedMaxIpcSizeBytes());
+ // The caller only got here by bypassing the AccessibilityService-side check with
+ // reflection, so disconnect this connection to prevent further attempts.
+ disconnect();
return;
}
synchronized (mLock) {
@@ -292,7 +349,7 @@ class BrailleDisplayConnection extends IBrailleDisplayConnection.Stub {
if (mOutputThread == null) {
try {
mOutputStream = new FileOutputStream(mHidrawNode);
- } catch (FileNotFoundException e) {
+ } catch (Exception e) {
Slog.e(LOG_TAG, "Unable to create write stream", e);
disconnect();
return;
@@ -387,14 +444,13 @@ class BrailleDisplayConnection extends IBrailleDisplayConnection.Stub {
BrailleDisplayScanner getDefaultNativeScanner(@NonNull NativeInterface nativeInterface) {
Objects.requireNonNull(nativeInterface);
return new BrailleDisplayScanner() {
- private static final Path DEVICE_DIR = Path.of("/dev");
private static final String HIDRAW_DEVICE_GLOB = "hidraw*";
@Override
- public Collection<Path> getHidrawNodePaths() {
+ public Collection<Path> getHidrawNodePaths(@NonNull Path directory) {
final List<Path> result = new ArrayList<>();
try (DirectoryStream<Path> hidrawNodePaths = Files.newDirectoryStream(
- DEVICE_DIR, HIDRAW_DEVICE_GLOB)) {
+ directory, HIDRAW_DEVICE_GLOB)) {
for (Path path : hidrawNodePaths) {
result.add(path);
}
@@ -458,8 +514,8 @@ class BrailleDisplayConnection extends IBrailleDisplayConnection.Stub {
synchronized (mLock) {
mScanner = new BrailleDisplayScanner() {
@Override
- public Collection<Path> getHidrawNodePaths() {
- return brailleDisplayMap.keySet();
+ public Collection<Path> getHidrawNodePaths(@NonNull Path directory) {
+ return brailleDisplayMap.isEmpty() ? null : brailleDisplayMap.keySet();
}
@Override
@@ -490,38 +546,56 @@ class BrailleDisplayConnection extends IBrailleDisplayConnection.Stub {
*/
@VisibleForTesting
interface NativeInterface {
+ /**
+ * Returns the HIDRAW descriptor size for the file descriptor.
+ *
+ * @return the result of ioctl(HIDIOCGRDESCSIZE), or -1 if the ioctl fails.
+ */
int getHidrawDescSize(int fd);
+ /**
+ * Returns the HIDRAW descriptor for the file descriptor.
+ *
+ * @return the result of ioctl(HIDIOCGRDESC), or null if the ioctl fails.
+ */
byte[] getHidrawDesc(int fd, int descSize);
+ /**
+ * Returns the HIDRAW unique identifier for the file descriptor.
+ *
+ * @return the result of ioctl(HIDIOCGRAWUNIQ), or null if the ioctl fails.
+ */
String getHidrawUniq(int fd);
+ /**
+ * Returns the HIDRAW bus type for the file descriptor.
+ *
+ * @return the result of ioctl(HIDIOCGRAWINFO).bustype, or -1 if the ioctl fails.
+ */
int getHidrawBusType(int fd);
}
/** Native interface that actually calls native HIDRAW ioctls. */
- private NativeInterface getDefaultNativeInterface() {
- return new NativeInterface() {
- @Override
- public int getHidrawDescSize(int fd) {
- return nativeGetHidrawDescSize(fd);
- }
+ private class DefaultNativeInterface implements NativeInterface {
+ @Override
+ public int getHidrawDescSize(int fd) {
+ return nativeGetHidrawDescSize(fd);
+ }
- @Override
- public byte[] getHidrawDesc(int fd, int descSize) {
- return nativeGetHidrawDesc(fd, descSize);
- }
+ @Override
+ public byte[] getHidrawDesc(int fd, int descSize) {
+ return nativeGetHidrawDesc(fd, descSize);
+ }
- @Override
- public String getHidrawUniq(int fd) {
- return nativeGetHidrawUniq(fd);
- }
+ @Override
+ public String getHidrawUniq(int fd) {
+ return nativeGetHidrawUniq(fd);
+ }
- @Override
- public int getHidrawBusType(int fd) {
- return nativeGetHidrawBusType(fd);
- }
- };
+ @Override
+ public int getHidrawBusType(int fd) {
+ return nativeGetHidrawBusType(fd);
+ }
}
private native int nativeGetHidrawDescSize(int fd);
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
index b6223c7b4b35..bf9202f1b266 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
@@ -106,11 +106,30 @@ class EventDispatcher {
return;
}
}
+ final long downTime;
if (action == MotionEvent.ACTION_DOWN) {
- event.setDownTime(event.getEventTime());
+ downTime = event.getEventTime();
} else {
- event.setDownTime(mState.getLastInjectedDownEventTime());
+ downTime = mState.getLastInjectedDownEventTime();
}
+
+ // The only way to change device id of the motion event is by re-creating the whole thing
+ final PointerProperties[] properties = new PointerProperties[event.getPointerCount()];
+ final PointerCoords[] coords = new PointerCoords[event.getPointerCount()];
+ for (int i = 0; i < event.getPointerCount(); i++) {
+ final PointerCoords c = new PointerCoords();
+ event.getPointerCoords(i, c);
+ coords[i] = c;
+ final PointerProperties p = new PointerProperties();
+ event.getPointerProperties(i, p);
+ properties[i] = p;
+ }
+ event = MotionEvent.obtain(downTime, event.getEventTime(), event.getAction(),
+ event.getPointerCount(), properties, coords,
+ event.getMetaState(), event.getButtonState(),
+ event.getXPrecision(), event.getYPrecision(), rawEvent.getDeviceId(),
+ event.getEdgeFlags(), rawEvent.getSource(), event.getDisplayId(), event.getFlags(),
+ event.getClassification());
// If the user is long pressing but the long pressing pointer
// was not exactly over the accessibility focused item we need
// to remap the location of that pointer so the user does not
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index 3086ce1ceb40..4fc65bfeca58 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -852,6 +852,11 @@ public class TouchExplorer extends BaseEventStreamTransformation
final int pointerIdBits = (1 << pointerId);
if (mSendHoverEnterAndMoveDelayed.isPending()) {
// If we have not delivered the enter schedule an exit.
+ if (Flags.resetHoverEventTimerOnActionUp()) {
+ // We cancel first to reset the time window so that the user has the full amount of
+ // time to do a multi tap.
+ mSendHoverEnterAndMoveDelayed.repost();
+ }
mSendHoverExitDelayed.post(event, rawEvent, pointerIdBits, policyFlags);
} else {
// The user is touch exploring so we send events for end.
@@ -1554,6 +1559,14 @@ public class TouchExplorer extends BaseEventStreamTransformation
}
}
+ public void repost() {
+ // cancel without clearing
+ if (isPending()) {
+ mHandler.removeCallbacks(this);
+ mHandler.postDelayed(this, mDetermineUserIntentTimeout);
+ }
+ }
+
private boolean isPending() {
return mHandler.hasCallbacks(this);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index 351760bd8865..a5bbc7efcb23 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -599,7 +599,7 @@ public class FullScreenMagnificationController implements
callback.onFullScreenMagnificationActivationState(
mDisplayId, mMagnificationActivated);
});
- mControllerCtx.getWindowManager().setForceShowMagnifiableBounds(
+ mControllerCtx.getWindowManager().setFullscreenMagnificationActivated(
mDisplayId, activated);
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 5407af7bda44..6f45f60f6dfa 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -327,10 +327,13 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
synchronized (mLock) {
// No need to enforce unlocked state when there is no caller. User can be in the
// stopping state or removed by the time the message is processed
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "convert_state_to_bytes");
ensureGroupStateLoadedLocked(userId, false /* enforceUserUnlockingOrUnlocked */);
userIdToBytesMapping = saveStateToByteArrayLocked(userId);
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "byte_to_disk_io");
for (int i = 0; i < userIdToBytesMapping.size(); i++) {
int currentProfileId = userIdToBytesMapping.keyAt(i);
byte[] currentStateByteArray = userIdToBytesMapping.valueAt(i);
@@ -351,6 +354,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
currentFile.failWrite(fileStream);
}
}
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
return true;
}
@@ -2409,6 +2413,16 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
}
AppWidgetProviderInfo info = createPartialProviderInfo(providerId, ri, existing);
+
+ if (android.os.Flags.allowPrivateProfile()
+ && android.multiuser.Flags.disablePrivateSpaceItemsOnHome()) {
+ // Do not add widget providers for profiles with items restricted on home screen.
+ if (info != null && mUserManager
+ .getUserProperties(info.getProfile()).areItemsRestrictedOnHomeScreen()) {
+ return false;
+ }
+ }
+
if (info != null) {
if (existing != null) {
if (existing.zombie && !mSafeMode) {
@@ -4787,8 +4801,10 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
synchronized (mLock) {
// No need to enforce unlocked state when there is no caller. User can be in the
// stopping state or removed by the time the message is processed
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "convert_state_and_io");
ensureGroupStateLoadedLocked(mUserId, false /* enforceUserUnlockingOrUnlocked */ );
saveStateLocked(mUserId);
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
}
diff --git a/services/autofill/Android.bp b/services/autofill/Android.bp
index eb23f2f9b435..3965869ad5f5 100644
--- a/services/autofill/Android.bp
+++ b/services/autofill/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_autofill",
// 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"
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 96c65565c96b..c4341b9367ec 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -127,7 +127,6 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
-import android.os.OutcomeReceiver;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Process;
@@ -2770,6 +2769,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
+ id + " destroyed");
return;
}
+ if (sDebug) {
+ Slog.d(TAG, "setAuthenticationResultLocked(): id= " + authenticationId
+ + ", data=" + data);
+ }
final int requestId = AutofillManager.getRequestIdFromAuthenticationId(authenticationId);
if (requestId == AUGMENTED_AUTOFILL_REQUEST_ID) {
setAuthenticationResultForAugmentedAutofillLocked(data, authenticationId);
@@ -2823,26 +2826,34 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
+ ", clientState=" + newClientState + ", authenticationId=" + authenticationId);
}
if (result instanceof FillResponse) {
+ if (sDebug) {
+ Slog.d(TAG, "setAuthenticationResultLocked(): received FillResponse from"
+ + " authentication flow");
+ }
logAuthenticationStatusLocked(requestId, MetricsEvent.AUTOFILL_AUTHENTICATED);
mPresentationStatsEventLogger.maybeSetAuthenticationResult(
AUTHENTICATION_RESULT_SUCCESS);
replaceResponseLocked(authenticatedResponse, (FillResponse) result, newClientState);
} else if (result instanceof GetCredentialResponse) {
- Slog.d(TAG, "Received GetCredentialResponse from authentication flow");
- boolean isCredmanCallbackInvoked = false;
- if (Flags.autofillCredmanIntegration()) {
- GetCredentialResponse response = (GetCredentialResponse) result;
- isCredmanCallbackInvoked = invokeCredentialManagerCallback(response);
+ if (sDebug) {
+ Slog.d(TAG, "Received GetCredentialResponse from authentication flow");
}
-
- if (!isCredmanCallbackInvoked) {
+ if (Flags.autofillCredmanDevIntegration()) {
+ GetCredentialResponse response = (GetCredentialResponse) result;
+ sendCredentialManagerResponseToApp(response,
+ /*exception=*/ null, response.getAutofillId());
+ } else if (Flags.autofillCredmanIntegration()) {
Dataset dataset = getDatasetFromCredentialResponse(
- (GetCredentialResponse) result);
+ (GetCredentialResponse) result);
if (dataset != null) {
autoFill(requestId, datasetIdx, dataset, false, UI_TYPE_UNKNOWN);
}
}
} else if (result instanceof Dataset) {
+ if (sDebug) {
+ Slog.d(TAG, "setAuthenticationResultLocked(): received Dataset from"
+ + " authentication flow");
+ }
if (datasetIdx != AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED) {
logAuthenticationStatusLocked(requestId,
MetricsEvent.AUTOFILL_DATASET_AUTHENTICATED);
@@ -2878,49 +2889,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
- private boolean invokeCredentialManagerCallback(GetCredentialResponse response) {
- synchronized (mLock) {
- return invokeCredentialManagerCallbackLocked(response);
- }
- }
-
- @GuardedBy("mLock")
- private boolean invokeCredentialManagerCallbackLocked(GetCredentialResponse response) {
- AutofillId autofillId = response.getAutofillId();
- if (autofillId != null) {
- OutcomeReceiver<GetCredentialResponse,
- GetCredentialException> callback =
- getCredmanCallbackFromContextsLocked(autofillId);
- if (callback != null) {
- Slog.w(TAG, "Propagating response to Credential Manager callback");
- callback.onResult(response);
- return true;
- } else {
- Slog.w(TAG, "Received Credential Manager response but no callback found");
- }
- } else {
- Slog.w(TAG, "Received Credential Manager response but no autofillId found");
- }
- return false;
- }
-
- @GuardedBy("mLock")
- @Nullable
- private OutcomeReceiver<GetCredentialResponse,
- GetCredentialException> getCredmanCallbackFromContextsLocked(
- @NonNull AutofillId autofillId) {
- final int numContexts = mContexts.size();
- for (int i = numContexts - 1; i >= 0; i--) {
- final FillContext context = mContexts.get(i);
- final ViewNode node = Helper.findViewNodeByAutofillId(context.getStructure(),
- autofillId);
- if (node != null) {
- return node.getCredentialManagerCallback();
- }
- }
- return null;
- }
-
private Dataset getDatasetFromCredentialResponse(GetCredentialResponse result) {
if (result == null) {
return null;
@@ -5095,10 +5063,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE,
GetCredentialResponse.class);
- isCredmanCallbackInvoked =
- invokeCredentialManagerCallback(getCredentialResponse);
-
- if (!isCredmanCallbackInvoked) {
+ if (Flags.autofillCredmanDevIntegration()) {
+ sendCredentialManagerResponseToApp(getCredentialResponse,
+ /*exception=*/ null, getCredentialResponse.getAutofillId());
+ } else {
Dataset datasetFromCredential = getDatasetFromCredentialResponse(
getCredentialResponse);
if (datasetFromCredential != null) {
@@ -5108,12 +5076,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
} else if (resultCode == FAILURE_CREDMAN_SELECTOR) {
- GetCredentialException exception = resultData.getParcelable(
- CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION,
- GetCredentialException.class);
- Slog.d(TAG, "Credman bottom sheet from pinned "
- + "entry failed with: + " + exception.getType() + " , "
- + exception.getMessage());
+ String[] exception = resultData.getStringArray(
+ CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION);
+ if (exception != null && exception.length >= 2) {
+ Slog.w(TAG, "Credman bottom sheet from pinned "
+ + "entry failed with: + " + exception[0] + " , "
+ + exception[1]);
+ // TODO(b/326313420): Propagate exception
+ }
} else {
Slog.d(TAG, "Unknown resultCode from credential "
+ "manager bottom sheet: " + resultCode);
@@ -6392,6 +6362,37 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
+ void sendCredentialManagerResponseToApp(@Nullable GetCredentialResponse response,
+ @Nullable GetCredentialException exception, @NonNull AutofillId viewId) {
+ synchronized (mLock) {
+ if (mDestroyed) {
+ Slog.w(TAG, "Call to Session#sendCredentialManagerResponseToApp() rejected "
+ + "- session: " + id + " destroyed");
+ return;
+ }
+ try {
+ final ViewState viewState = mViewStates.get(viewId);
+ if (mService.getMaster().getIsFillFieldsFromCurrentSessionOnly()
+ && viewState != null && viewState.id.getSessionId() != id) {
+ if (sVerbose) {
+ Slog.v(TAG, "Skipping sending credential response to view: "
+ + viewId + " as it isn't part of the current session: " + id);
+ }
+ }
+ if (exception != null) {
+ // TODO(b/326313420): Add Exception support
+ } else if (response != null) {
+ mClient.onGetCredentialResponse(id, viewId, response);
+ } else {
+ Slog.w(TAG, "sendCredentialManagerResponseToApp called with null response"
+ + "and exception");
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error sending credential response to activity: " + e);
+ }
+ }
+ }
+
void autoFillApp(Dataset dataset) {
synchronized (mLock) {
if (mDestroyed) {
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index d580f3a7d7d8..78edb8ead7a0 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -15,7 +15,7 @@
*/
package com.android.server.autofill.ui;
-import static android.service.autofill.FillRequest.FLAG_VIEW_REQUESTS_CREDMAN_SERVICE;
+import static android.service.autofill.FillResponse.FLAG_CREDENTIAL_MANAGER_RESPONSE;
import static com.android.server.autofill.Helper.paramsToString;
import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sFullScreenMode;
@@ -215,7 +215,7 @@ final class FillUi {
Slog.v(TAG, "overriding maximum visible datasets to " + mVisibleDatasetsMaxCount);
}
} else if (Flags.autofillCredmanIntegration() && (
- (response.getFlags() & FLAG_VIEW_REQUESTS_CREDMAN_SERVICE) != 0)) {
+ (response.getFlags() & FLAG_CREDENTIAL_MANAGER_RESPONSE) != 0)) {
mVisibleDatasetsMaxCount = AUTOFILL_CREDMAN_MAX_VISIBLE_DATASETS;
}
else {
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java b/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java
index c734680009c6..ffc80ee7d710 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java
@@ -16,6 +16,8 @@
package com.android.server.autofill.ui;
+import static android.service.autofill.FillResponse.FLAG_CREDENTIAL_MANAGER_RESPONSE;
+
import static com.android.server.autofill.Helper.sVerbose;
import android.annotation.NonNull;
@@ -24,6 +26,7 @@ import android.annotation.UserIdInt;
import android.content.IntentSender;
import android.service.autofill.Dataset;
import android.service.autofill.FillResponse;
+import android.service.autofill.Flags;
import android.service.autofill.InlinePresentation;
import android.text.TextUtils;
import android.util.Pair;
@@ -141,10 +144,12 @@ public final class InlineFillUi {
return new InlineFillUi(inlineFillUiInfo, inlineAuthentication,
maxInputLengthForAutofill);
} else if (response.getDatasets() != null) {
+ boolean ignoreHostSpec = Flags.autofillCredmanIntegration() && (
+ (response.getFlags() & FLAG_CREDENTIAL_MANAGER_RESPONSE) != 0);
SparseArray<Pair<Dataset, InlineSuggestion>> inlineSuggestions =
InlineSuggestionFactory.createInlineSuggestions(inlineFillUiInfo,
InlineSuggestionInfo.SOURCE_AUTOFILL, response.getDatasets(),
- uiCallback);
+ uiCallback, ignoreHostSpec);
return new InlineFillUi(inlineFillUiInfo, inlineSuggestions,
maxInputLengthForAutofill);
}
@@ -160,7 +165,8 @@ public final class InlineFillUi {
@NonNull InlineSuggestionUiCallback uiCallback) {
SparseArray<Pair<Dataset, InlineSuggestion>> inlineSuggestions =
InlineSuggestionFactory.createInlineSuggestions(inlineFillUiInfo,
- InlineSuggestionInfo.SOURCE_PLATFORM, datasets, uiCallback);
+ InlineSuggestionInfo.SOURCE_PLATFORM, datasets,
+ uiCallback, /* ignoreHostSpec= */ false);
return new InlineFillUi(inlineFillUiInfo, inlineSuggestions);
}
@@ -254,7 +260,7 @@ public final class InlineFillUi {
if (!TextUtils.isEmpty(mFilterText) && mFilterText.length() > mMaxInputLengthForAutofill) {
if (sVerbose) {
Slog.v(TAG, "Not showing inline suggestion when user entered more than "
- + mMaxInputLengthForAutofill + " characters");
+ + mMaxInputLengthForAutofill + " characters");
}
return new InlineSuggestionsResponse(inlineSuggestions);
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
index 52109ba44917..d3b950112f91 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
@@ -16,6 +16,7 @@
package com.android.server.autofill.ui;
+import static android.service.autofill.FillResponse.FLAG_CREDENTIAL_MANAGER_RESPONSE;
import static android.view.inputmethod.InlineSuggestionInfo.TYPE_SUGGESTION;
import static com.android.server.autofill.Helper.sDebug;
@@ -25,6 +26,7 @@ import android.annotation.Nullable;
import android.content.IntentSender;
import android.service.autofill.Dataset;
import android.service.autofill.FillResponse;
+import android.service.autofill.Flags;
import android.service.autofill.InlinePresentation;
import android.util.Pair;
import android.util.Slog;
@@ -47,11 +49,14 @@ final class InlineSuggestionFactory {
@NonNull InlineFillUi.InlineSuggestionUiCallback uiCallback) {
InlinePresentation inlineAuthentication = response.getInlinePresentation();
final int requestId = response.getRequestId();
+ boolean ignoreHostSpec = Flags.autofillCredmanIntegration() && (
+ (response.getFlags() & FLAG_CREDENTIAL_MANAGER_RESPONSE) != 0);
return createInlineSuggestion(inlineFillUiInfo, InlineSuggestionInfo.SOURCE_AUTOFILL,
InlineSuggestionInfo.TYPE_ACTION, () -> uiCallback.authenticate(requestId,
AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED),
- mergedInlinePresentation(inlineFillUiInfo.mInlineRequest, 0, inlineAuthentication),
+ mergedInlinePresentation(inlineFillUiInfo.mInlineRequest, 0, inlineAuthentication,
+ ignoreHostSpec),
createInlineSuggestionTooltip(inlineFillUiInfo.mInlineRequest,
inlineFillUiInfo, InlineSuggestionInfo.SOURCE_AUTOFILL,
response.getInlineTooltipPresentation()),
@@ -67,7 +72,7 @@ final class InlineSuggestionFactory {
@NonNull InlineFillUi.InlineFillUiInfo inlineFillUiInfo,
@NonNull @InlineSuggestionInfo.Source String suggestionSource,
@NonNull List<Dataset> datasets,
- @NonNull InlineFillUi.InlineSuggestionUiCallback uiCallback) {
+ @NonNull InlineFillUi.InlineSuggestionUiCallback uiCallback, boolean ignoreHostSpec) {
if (sDebug) Slog.d(TAG, "createInlineSuggestions(source=" + suggestionSource + ") called");
final InlineSuggestionsRequest request = inlineFillUiInfo.mInlineRequest;
@@ -107,7 +112,8 @@ final class InlineSuggestionFactory {
InlineSuggestion inlineSuggestion = createInlineSuggestion(
inlineFillUiInfo, suggestionSource, suggestionType,
() -> uiCallback.autofill(dataset, index),
- mergedInlinePresentation(request, datasetIndex, inlinePresentation),
+ mergedInlinePresentation(request, datasetIndex, inlinePresentation,
+ ignoreHostSpec),
inlineSuggestionTooltip,
uiCallback);
response.append(datasetIndex, Pair.create(dataset, inlineSuggestion));
@@ -141,16 +147,18 @@ final class InlineSuggestionFactory {
*/
private static InlinePresentation mergedInlinePresentation(
@NonNull InlineSuggestionsRequest request,
- int index, @NonNull InlinePresentation inlinePresentation) {
+ int index, @NonNull InlinePresentation inlinePresentation, boolean ignoreHostSpec) {
final List<InlinePresentationSpec> specs = request.getInlinePresentationSpecs();
if (specs.isEmpty()) {
return inlinePresentation;
}
InlinePresentationSpec specFromHost = specs.get(Math.min(specs.size() - 1, index));
+ InlinePresentationSpec specToUse =
+ ignoreHostSpec ? inlinePresentation.getInlinePresentationSpec() : specFromHost;
InlinePresentationSpec mergedInlinePresentation = new InlinePresentationSpec.Builder(
inlinePresentation.getInlinePresentationSpec().getMinSize(),
inlinePresentation.getInlinePresentationSpec().getMaxSize()).setStyle(
- specFromHost.getStyle()).build();
+ specToUse.getStyle()).build();
return new InlinePresentation(inlinePresentation.getSlice(), mergedInlinePresentation,
inlinePresentation.isPinned());
@@ -211,7 +219,7 @@ final class InlineSuggestionFactory {
inlineFillUiInfo, tooltipInline, () -> { /* no operation */ }, uiCallback);
final InlineSuggestionInfo tooltipInlineSuggestionInfo = new InlineSuggestionInfo(
mergedSpec, suggestionSource, /* autofillHints */ null, TYPE_SUGGESTION,
- /* pinned */ false, /* tooltip */ null);
+ /* pinned */ false, /* tooltip */ null);
return new InlineSuggestion(tooltipInlineSuggestionInfo, tooltipContentProvider);
}
diff --git a/services/backup/flags.aconfig b/services/backup/flags.aconfig
index 71f2b9e8e10b..e9f959f4b9eb 100644
--- a/services/backup/flags.aconfig
+++ b/services/backup/flags.aconfig
@@ -35,6 +35,15 @@ flag {
}
flag {
+ name: "enable_v_to_u_restore_for_system_components_in_allowlist"
+ namespace: "onboarding"
+ description: "Enables system components to opt in to support restore in V to U downgrade "
+ "scenario without opting in for restoreAnyVersion."
+ bug: "324233962"
+ is_fixed_read_only: true
+}
+
+flag {
name: "enable_increase_datatypes_for_agent_logging"
namespace: "onboarding"
description: "Increase the number of a supported datatypes that an agent can define for its "
diff --git a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
index 9f0deea503cf..6e98e68601f6 100644
--- a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
+++ b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
@@ -177,6 +177,10 @@ public class PackageManagerBackupAgent extends BackupAgent {
return mHasMetadata;
}
+ public int getSourceSdk() {
+ return mStoredSdkVersion;
+ }
+
public Metadata getRestoredMetadata(String packageName) {
if (mRestoredSignatures == null) {
Slog.w(TAG, "getRestoredMetadata() before metadata read!");
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index d85dd879e21d..e666442af9c9 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -44,6 +44,7 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
+import android.os.Build;
import android.os.Bundle;
import android.os.Message;
import android.os.ParcelFileDescriptor;
@@ -51,6 +52,7 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.provider.Settings;
import android.util.EventLog;
import android.util.Slog;
@@ -82,6 +84,7 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -158,6 +161,12 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
// When finished call listener
private final OnTaskFinishedListener mListener;
+ // List of packages that support V-> U downgrade but do not have RestoreAnyVersion set to true.
+ private List<String> mVToUAllowlist;
+
+ // List of packages that have RestoreAnyVersion set to true but do not support V-> U downgrade.
+ private List<String> mVToUDenylist;
+
// Key/value: bookkeeping about staged data and files for agent access
private File mBackupDataName;
private File mStageName;
@@ -172,7 +181,8 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
@VisibleForTesting
PerformUnifiedRestoreTask(
UserBackupManagerService backupManagerService,
- TransportConnection transportConnection) {
+ TransportConnection transportConnection,
+ String vToUAllowlist, String vToUDenyList) {
mListener = null;
mAgentTimeoutParameters = null;
mOperationStorage = null;
@@ -183,6 +193,8 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
mBackupEligibilityRules = null;
this.backupManagerService = backupManagerService;
mBackupManagerMonitorEventSender = new BackupManagerMonitorEventSender(/* monitor= */ null);
+ mVToUAllowlist = createVToUList(vToUAllowlist);
+ mVToUDenylist = createVToUList(vToUDenyList);
}
// This task can assume that the wakelock is properly held for it and doesn't have to worry
@@ -223,6 +235,18 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
backupManagerService.getAgentTimeoutParameters(),
"Timeout parameters cannot be null");
mBackupEligibilityRules = backupEligibilityRules;
+ mVToUAllowlist =
+ createVToUList(
+ Settings.Secure.getStringForUser(
+ backupManagerService.getContext().getContentResolver(),
+ Settings.Secure.V_TO_U_RESTORE_ALLOWLIST,
+ mUserId));
+ mVToUDenylist =
+ createVToUList(
+ Settings.Secure.getStringForUser(
+ backupManagerService.getContext().getContentResolver(),
+ Settings.Secure.V_TO_U_RESTORE_DENYLIST,
+ mUserId));
if (targetPackage != null) {
// Single package restore
@@ -636,60 +660,29 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
// Data is from a "newer" version of the app than we have currently
// installed. If the app has not declared that it is prepared to
// handle this case, we do not attempt the restore.
- if ((mCurrentPackage.applicationInfo.flags
- & ApplicationInfo.FLAG_RESTORE_ANY_VERSION)
- == 0) {
- String message =
- "Source version "
- + metaInfo.versionCode
- + " > installed version "
- + mCurrentPackage.getLongVersionCode();
- Slog.w(TAG, "Package " + pkgName + ": " + message);
- Bundle monitoringExtras =
- mBackupManagerMonitorEventSender.putMonitoringExtra(
- null,
- BackupManagerMonitor.EXTRA_LOG_RESTORE_VERSION,
- metaInfo.versionCode);
- monitoringExtras =
- mBackupManagerMonitorEventSender.putMonitoringExtra(
- monitoringExtras,
- BackupManagerMonitor.EXTRA_LOG_RESTORE_ANYWAY,
- false);
- monitoringExtras = addRestoreOperationTypeToEvent(monitoringExtras);
- mBackupManagerMonitorEventSender.monitorEvent(
- BackupManagerMonitor.LOG_EVENT_ID_RESTORE_VERSION_HIGHER,
- mCurrentPackage,
- BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
- monitoringExtras);
- EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName, message);
- nextState = UnifiedRestoreState.RUNNING_QUEUE;
- return;
+ if (mIsSystemRestore
+ && isVToUDowngrade(mPmAgent.getSourceSdk(), android.os.Build.VERSION.SDK_INT)) {
+ if (isPackageEligibleForVToURestore(mCurrentPackage)) {
+ Slog.i(TAG, "Package " + pkgName
+ + " is eligible for V to U downgrade scenario");
+ } else {
+ String message = "Package not eligible for V to U downgrade scenario";
+ Slog.i(TAG, pkgName + " : " + message);
+ EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, pkgName, message);
+ nextState = UnifiedRestoreState.RUNNING_QUEUE;
+ return;
+ }
} else {
- if (DEBUG) {
- Slog.v(
- TAG,
- "Source version "
- + metaInfo.versionCode
- + " > installed version "
- + mCurrentPackage.getLongVersionCode()
- + " but restoreAnyVersion");
+ if ((mCurrentPackage.applicationInfo.flags
+ & ApplicationInfo.FLAG_RESTORE_ANY_VERSION)
+ == 0) {
+ // Downgrade scenario with RestoreAnyVersion flag off
+ logDowngradeScenario(/* isRestoreAnyVersion */ false, metaInfo);
+ nextState = UnifiedRestoreState.RUNNING_QUEUE;
+ return;
+ } else {
+ logDowngradeScenario(/* isRestoreAnyVersion */ true, metaInfo);
}
- Bundle monitoringExtras =
- mBackupManagerMonitorEventSender.putMonitoringExtra(
- null,
- BackupManagerMonitor.EXTRA_LOG_RESTORE_VERSION,
- metaInfo.versionCode);
- monitoringExtras =
- mBackupManagerMonitorEventSender.putMonitoringExtra(
- monitoringExtras,
- BackupManagerMonitor.EXTRA_LOG_RESTORE_ANYWAY,
- true);
- monitoringExtras = addRestoreOperationTypeToEvent(monitoringExtras);
- mBackupManagerMonitorEventSender.monitorEvent(
- BackupManagerMonitor.LOG_EVENT_ID_RESTORE_VERSION_HIGHER,
- mCurrentPackage,
- BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
- monitoringExtras);
}
}
@@ -1673,4 +1666,86 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
return mBackupManagerMonitorEventSender.putMonitoringExtra(
extras, BackupManagerMonitor.EXTRA_LOG_OPERATION_TYPE, RESTORE);
}
+
+ // checks the sdk of the target/source device for a B&R operation.
+ // system components can opt in/out of V->U restore via allowlists. All other apps are
+ // not impacted
+ @SuppressWarnings("AndroidFrameworkCompatChange")
+ @VisibleForTesting
+ protected boolean isVToUDowngrade(int sourceSdk, int targetSdk) {
+ // We assume that if the source sdk is greater than U then the source is V.
+ return Flags.enableVToURestoreForSystemComponentsInAllowlist()
+ && (sourceSdk > Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ && (targetSdk == Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
+ }
+
+ @VisibleForTesting
+ protected List<String> createVToUList(@Nullable String listString) {
+ // The allowlist/denylist is stored as a comma-separated list of package names
+ List<String> list = new ArrayList<>();
+ if (listString != null) {
+ list = Arrays.asList(listString.split(","));
+ }
+ return list;
+ }
+
+ @VisibleForTesting
+ protected boolean isPackageEligibleForVToURestore(PackageInfo mCurrentPackage) {
+ // A package is eligible for V to U downgrade restore if either:
+ // - The package has restoreAnyVersion set to false and is part of the V to U allowlist
+ // (and not in the denylist)
+ // - The package has restoreAnyVersion set to true and is not part of the denylist
+ if (mVToUDenylist.contains(mCurrentPackage.packageName)){
+ return false;
+ } else if ((mCurrentPackage.applicationInfo.flags
+ & ApplicationInfo.FLAG_RESTORE_ANY_VERSION)
+ == 0) {
+ // package has restoreAnyVersion set to false
+ return mVToUAllowlist.contains(mCurrentPackage.packageName);
+ } else {
+ // package has restoreAnyVersion set to true and is nor in denylist
+ return true;
+ }
+ }
+
+ private void logDowngradeScenario(boolean isRestoreAnyVersion, Metadata metaInfo) {
+ Bundle monitoringExtras =
+ mBackupManagerMonitorEventSender.putMonitoringExtra(
+ null,
+ BackupManagerMonitor.EXTRA_LOG_RESTORE_VERSION,
+ metaInfo.versionCode);
+ String message;
+ if (isRestoreAnyVersion) {
+ monitoringExtras =
+ mBackupManagerMonitorEventSender.putMonitoringExtra(
+ monitoringExtras,
+ BackupManagerMonitor.EXTRA_LOG_RESTORE_ANYWAY,
+ true);
+ message = "Source version "
+ + metaInfo.versionCode
+ + " > installed version "
+ + mCurrentPackage.getLongVersionCode()
+ + " but restoreAnyVersion";
+ } else {
+ monitoringExtras =
+ mBackupManagerMonitorEventSender.putMonitoringExtra(
+ monitoringExtras,
+ BackupManagerMonitor.EXTRA_LOG_RESTORE_ANYWAY,
+ false);
+ message = "Source version "
+ + metaInfo.versionCode
+ + " > installed version "
+ + mCurrentPackage.getLongVersionCode();
+ EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, mCurrentPackage.packageName,
+ message);
+ }
+ Slog.i(TAG, "Package " + mCurrentPackage.packageName + ": " + message);
+ monitoringExtras = addRestoreOperationTypeToEvent(monitoringExtras);
+ mBackupManagerMonitorEventSender.monitorEvent(
+ BackupManagerMonitor.LOG_EVENT_ID_RESTORE_VERSION_HIGHER,
+ mCurrentPackage,
+ BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
+ monitoringExtras);
+ }
+
}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index b43f1a93f183..ba1f51baa624 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -40,6 +40,7 @@ import static com.android.internal.util.Preconditions.checkState;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static com.android.server.companion.AssociationStore.CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED;
import static com.android.server.companion.MetricUtils.logRemoveAssociation;
+import static com.android.server.companion.PackageUtils.isRestrictedSettingsAllowed;
import static com.android.server.companion.PackageUtils.enforceUsesCompanionDeviceFeature;
import static com.android.server.companion.PackageUtils.getPackageInfo;
import static com.android.server.companion.PermissionsUtils.checkCallerCanManageCompanionDevice;
@@ -871,13 +872,22 @@ public class CompanionDeviceManagerService extends SystemService {
@Override
public PendingIntent requestNotificationAccess(ComponentName component, int userId)
throws RemoteException {
- String callingPackage = component.getPackageName();
+ int callingUid = getCallingUid();
+ final String callingPackage = component.getPackageName();
+
checkCanCallNotificationApi(callingPackage, userId);
+
if (component.flattenToString().length() > MAX_CN_LENGTH) {
throw new IllegalArgumentException("Component name is too long.");
}
+
final long identity = Binder.clearCallingIdentity();
try {
+ if (!isRestrictedSettingsAllowed(getContext(), callingPackage, callingUid)) {
+ Slog.e(TAG, "Side loaded app must enable restricted "
+ + "setting before request the notification access");
+ return null;
+ }
return PendingIntent.getActivityAsUser(getContext(),
0 /* request code */,
NotificationAccessConfirmationActivityContract.launcherIntent(
diff --git a/services/companion/java/com/android/server/companion/PackageUtils.java b/services/companion/java/com/android/server/companion/PackageUtils.java
index 6c77018de27b..3aae1ec99f55 100644
--- a/services/companion/java/com/android/server/companion/PackageUtils.java
+++ b/services/companion/java/com/android/server/companion/PackageUtils.java
@@ -28,6 +28,7 @@ import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.AppOpsManager;
import android.companion.CompanionDeviceService;
import android.content.ComponentName;
import android.content.Context;
@@ -222,4 +223,15 @@ public final class PackageUtils {
return requestingPackageSignatureAllowlisted;
}
+
+ /**
+ * Check if restricted settings is enabled for a side-loaded app.
+ */
+ public static boolean isRestrictedSettingsAllowed(
+ Context context, String packageName, int uid) {
+ final int mode = context.getSystemService(AppOpsManager.class).noteOpNoThrow(
+ AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, uid,
+ packageName, /* attributionTag= */ null, /* message= */ null);
+ return mode == AppOpsManager.MODE_ALLOWED;
+ }
}
diff --git a/services/companion/java/com/android/server/companion/virtual/Android.bp b/services/companion/java/com/android/server/companion/virtual/Android.bp
index 6526c78d5124..4a2030f9caff 100644
--- a/services/companion/java/com/android/server/companion/virtual/Android.bp
+++ b/services/companion/java/com/android/server/companion/virtual/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_team: "trendy_team_xr_framework",
+}
+
java_aconfig_library {
name: "virtualdevice_flags_lib",
aconfig_declarations: "virtualdevice_flags",
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index 31766f2ec4eb..ff076192b6ad 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -29,6 +29,7 @@ import android.app.compat.CompatChanges;
import android.companion.virtual.VirtualDeviceManager.ActivityListener;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
+import android.content.AttributionSource;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -44,6 +45,7 @@ import android.window.DisplayWindowPolicyController;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.BlockedAppStreamingActivity;
+import com.android.modules.expresslog.Counter;
import java.util.Set;
@@ -104,6 +106,8 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
public static final long ALLOW_SECURE_ACTIVITY_DISPLAY_ON_REMOTE_DEVICE = 201712607L;
@NonNull
+ private final AttributionSource mAttributionSource;
+ @NonNull
private final ArraySet<UserHandle> mAllowedUsers;
@GuardedBy("mGenericWindowPolicyControllerLock")
private boolean mActivityLaunchAllowedByDefault;
@@ -144,6 +148,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
*
* @param windowFlags The window flags that this controller is interested in.
* @param systemWindowFlags The system window flags that this controller is interested in.
+ * @param attributionSource The AttributionSource of the VirtualDevice owner application.
* @param allowedUsers The set of users that are allowed to stream in this display.
* @param activityLaunchAllowedByDefault Whether activities are default allowed to be launched
* or blocked.
@@ -169,6 +174,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
public GenericWindowPolicyController(
int windowFlags,
int systemWindowFlags,
+ AttributionSource attributionSource,
@NonNull ArraySet<UserHandle> allowedUsers,
boolean activityLaunchAllowedByDefault,
@NonNull Set<ComponentName> activityPolicyExemptions,
@@ -184,6 +190,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
boolean showTasksInHostDeviceRecents,
@Nullable ComponentName customHomeComponent) {
super();
+ mAttributionSource = attributionSource;
mAllowedUsers = allowedUsers;
mActivityLaunchAllowedByDefault = activityLaunchAllowedByDefault;
mActivityPolicyExemptions = activityPolicyExemptions;
@@ -436,6 +443,12 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
if (!mIsMirrorDisplay && mActivityBlockedCallback != null) {
mActivityBlockedCallback.onActivityBlocked(mDisplayId, activityInfo);
}
+ if (android.companion.virtualdevice.flags.Flags.metricsCollection()) {
+ Counter.logIncrementWithUid(
+ "virtual_devices.value_activity_blocked_count",
+ mAttributionSource.getUid());
+ }
+
}
private static boolean isAllowedByPolicy(boolean allowedByDefault,
diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java
index 1b49f18e60cf..9b72288955a7 100644
--- a/services/companion/java/com/android/server/companion/virtual/InputController.java
+++ b/services/companion/java/com/android/server/companion/virtual/InputController.java
@@ -21,6 +21,7 @@ import static android.text.TextUtils.formatSimple;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.StringDef;
+import android.content.AttributionSource;
import android.graphics.PointF;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.input.InputDeviceIdentifier;
@@ -38,6 +39,7 @@ import android.os.IBinder;
import android.os.IInputConstants;
import android.os.RemoteException;
import android.util.ArrayMap;
+import android.util.Log;
import android.util.Slog;
import android.view.Display;
import android.view.InputDevice;
@@ -45,6 +47,7 @@ import android.view.WindowManager;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.expresslog.Counter;
import com.android.server.LocalServices;
import com.android.server.input.InputManagerInternal;
@@ -98,11 +101,12 @@ class InputController {
private final DisplayManagerInternal mDisplayManagerInternal;
private final InputManagerInternal mInputManagerInternal;
private final WindowManager mWindowManager;
+ private final AttributionSource mAttributionSource;
private final DeviceCreationThreadVerifier mThreadVerifier;
InputController(@NonNull Handler handler,
- @NonNull WindowManager windowManager) {
- this(new NativeWrapper(), handler, windowManager,
+ @NonNull WindowManager windowManager, AttributionSource attributionSource) {
+ this(new NativeWrapper(), handler, windowManager, attributionSource,
// Verify that virtual devices are not created on the handler thread.
() -> !handler.getLooper().isCurrentThread());
}
@@ -110,12 +114,14 @@ class InputController {
@VisibleForTesting
InputController(@NonNull NativeWrapper nativeWrapper,
@NonNull Handler handler, @NonNull WindowManager windowManager,
+ AttributionSource attributionSource,
@NonNull DeviceCreationThreadVerifier threadVerifier) {
mHandler = handler;
mNativeWrapper = nativeWrapper;
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
mWindowManager = windowManager;
+ mAttributionSource = attributionSource;
mThreadVerifier = threadVerifier;
}
@@ -838,6 +844,33 @@ class InputController {
new InputDeviceDescriptor(ptr, binderDeathRecipient, type, displayId, phys,
deviceName, inputDeviceId));
}
+
+ if (android.companion.virtualdevice.flags.Flags.metricsCollection()) {
+ String metricId = getMetricIdForInputType(type);
+ if (metricId != null) {
+ Counter.logIncrementWithUid(metricId, mAttributionSource.getUid());
+ }
+ }
+ }
+
+ private static String getMetricIdForInputType(@InputDeviceDescriptor.Type int type) {
+ switch (type) {
+ case InputDeviceDescriptor.TYPE_KEYBOARD:
+ return "virtual_devices.value_virtual_keyboard_created_count";
+ case InputDeviceDescriptor.TYPE_MOUSE:
+ return "virtual_devices.value_virtual_mouse_created_count";
+ case InputDeviceDescriptor.TYPE_TOUCHSCREEN:
+ return "virtual_devices.value_virtual_touchscreen_created_count";
+ case InputDeviceDescriptor.TYPE_DPAD:
+ return "virtual_devices.value_virtual_dpad_created_count";
+ case InputDeviceDescriptor.TYPE_NAVIGATION_TOUCHPAD:
+ return "virtual_devices.value_virtual_navigationtouchpad_created_count";
+ case InputDeviceDescriptor.TYPE_STYLUS:
+ return "virtual_devices.value_virtual_stylus_created_count";
+ default:
+ Log.e(TAG, "No metric known for input type: " + type);
+ return null;
+ }
}
@VisibleForTesting
diff --git a/services/companion/java/com/android/server/companion/virtual/SensorController.java b/services/companion/java/com/android/server/companion/virtual/SensorController.java
index e9241dddcde9..cf4818060aa8 100644
--- a/services/companion/java/com/android/server/companion/virtual/SensorController.java
+++ b/services/companion/java/com/android/server/companion/virtual/SensorController.java
@@ -23,6 +23,7 @@ import android.companion.virtual.sensor.IVirtualSensorCallback;
import android.companion.virtual.sensor.VirtualSensor;
import android.companion.virtual.sensor.VirtualSensorConfig;
import android.companion.virtual.sensor.VirtualSensorEvent;
+import android.content.AttributionSource;
import android.hardware.SensorDirectChannel;
import android.os.Binder;
import android.os.IBinder;
@@ -35,6 +36,7 @@ import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.expresslog.Counter;
import com.android.server.LocalServices;
import com.android.server.sensors.SensorManagerInternal;
@@ -71,14 +73,18 @@ public class SensorController {
private List<VirtualSensor> mVirtualSensorList = null;
@NonNull
+ private final AttributionSource mAttributionSource;
+ @NonNull
private final SensorManagerInternal.RuntimeSensorCallback mRuntimeSensorCallback;
private final SensorManagerInternal mSensorManagerInternal;
private final VirtualDeviceManagerInternal mVdmInternal;
public SensorController(@NonNull IVirtualDevice virtualDevice, int virtualDeviceId,
+ @NonNull AttributionSource attributionSource,
@Nullable IVirtualSensorCallback virtualSensorCallback,
@NonNull List<VirtualSensorConfig> sensors) {
mVirtualDeviceId = virtualDeviceId;
+ mAttributionSource = attributionSource;
mRuntimeSensorCallback = new RuntimeSensorCallbackWrapper(virtualSensorCallback);
mSensorManagerInternal = LocalServices.getService(SensorManagerInternal.class);
mVdmInternal = LocalServices.getService(VirtualDeviceManagerInternal.class);
@@ -139,6 +145,11 @@ public class SensorController {
mSensorDescriptors.put(sensorToken, sensorDescriptor);
mVirtualSensors.put(handle, sensor);
}
+ if (android.companion.virtualdevice.flags.Flags.metricsCollection()) {
+ Counter.logIncrementWithUid(
+ "virtual_devices.value_virtual_sensors_created_count",
+ mAttributionSource.getUid());
+ }
}
boolean sendSensorEvent(@NonNull IBinder token, @NonNull VirtualSensorEvent event) {
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index f13f49a5d378..6d731b21ac8a 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -104,6 +104,7 @@ import android.widget.Toast;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.BlockedAppStreamingActivity;
+import com.android.modules.expresslog.Counter;
import com.android.server.LocalServices;
import com.android.server.companion.virtual.GenericWindowPolicyController.RunningAppsChangedListener;
import com.android.server.companion.virtual.audio.VirtualAudioController;
@@ -152,6 +153,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
private final int mOwnerUid;
private final VirtualDeviceLog mVirtualDeviceLog;
private final String mOwnerPackageName;
+ @NonNull
+ private final AttributionSource mAttributionSource;
private final int mDeviceId;
@Nullable
private final String mPersistentDeviceId;
@@ -288,6 +291,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
super(PermissionEnforcer.fromContext(context));
mVirtualDeviceLog = virtualDeviceLog;
mOwnerPackageName = attributionSource.getPackageName();
+ mAttributionSource = attributionSource;
UserHandle ownerUserHandle = UserHandle.getUserHandleForUid(attributionSource.getUid());
mContext = context.createContextAsUser(ownerUserHandle, 0);
mAssociationInfo = associationInfo;
@@ -307,11 +311,11 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
if (inputController == null) {
mInputController = new InputController(
context.getMainThreadHandler(),
- context.getSystemService(WindowManager.class));
+ context.getSystemService(WindowManager.class), mAttributionSource);
} else {
mInputController = inputController;
}
- mSensorController = new SensorController(this, mDeviceId,
+ mSensorController = new SensorController(this, mDeviceId, mAttributionSource,
mParams.getVirtualSensorCallback(), mParams.getVirtualSensorConfigs());
mCameraAccessController = cameraAccessController;
if (mCameraAccessController != null) {
@@ -620,7 +624,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
}
if (mVirtualAudioController == null) {
- mVirtualAudioController = new VirtualAudioController(mContext);
+ mVirtualAudioController = new VirtualAudioController(mContext, mAttributionSource);
GenericWindowPolicyController gwpc = mVirtualDisplays.get(
displayId).getWindowPolicyController();
mVirtualAudioController.startListening(gwpc, routingCallback,
@@ -1028,7 +1032,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
if (mVirtualCameraController == null) {
throw new UnsupportedOperationException("Virtual camera controller is not available");
}
- mVirtualCameraController.registerCamera(cameraConfig);
+ mVirtualCameraController.registerCamera(cameraConfig, mAttributionSource);
}
@Override // Binder call
@@ -1110,6 +1114,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
final GenericWindowPolicyController gwpc = new GenericWindowPolicyController(
FLAG_SECURE,
SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
+ mAttributionSource,
getAllowedUserHandles(),
activityLaunchAllowedByDefault,
mActivityPolicyExemptions,
@@ -1179,6 +1184,11 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
Binder.restoreCallingIdentity(token);
}
+ if (android.companion.virtualdevice.flags.Flags.metricsCollection()) {
+ Counter.logIncrementWithUid(
+ "virtual_devices.value_virtual_display_created_count",
+ mAttributionSource.getUid());
+ }
return displayId;
}
@@ -1220,6 +1230,12 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
if ((display.getFlags() & FLAG_SECURE) == 0) {
showToastWhereUidIsRunning(uid, com.android.internal.R.string.vdm_secure_window,
Toast.LENGTH_LONG, mContext.getMainLooper());
+
+ if (android.companion.virtualdevice.flags.Flags.metricsCollection()) {
+ Counter.logIncrementWithUid(
+ "virtual_devices.value_secure_window_blocked_count",
+ mAttributionSource.getUid());
+ }
}
}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 2168cb2043ed..9ad73cae08cd 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -482,6 +482,11 @@ public class VirtualDeviceManagerService extends SystemService {
}
});
}
+ if (android.companion.virtualdevice.flags.Flags.metricsCollection()) {
+ Counter.logIncrementWithUid(
+ "virtual_devices.value_virtual_devices_created_with_uid_count",
+ attributionSource.getUid());
+ }
return virtualDevice;
}
diff --git a/services/companion/java/com/android/server/companion/virtual/audio/VirtualAudioController.java b/services/companion/java/com/android/server/companion/virtual/audio/VirtualAudioController.java
index c91877aad47e..4bffb76e1701 100644
--- a/services/companion/java/com/android/server/companion/virtual/audio/VirtualAudioController.java
+++ b/services/companion/java/com/android/server/companion/virtual/audio/VirtualAudioController.java
@@ -23,6 +23,7 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.companion.virtual.audio.IAudioConfigChangedCallback;
import android.companion.virtual.audio.IAudioRoutingCallback;
+import android.content.AttributionSource;
import android.content.Context;
import android.media.AudioManager;
import android.media.AudioPlaybackConfiguration;
@@ -35,6 +36,7 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.expresslog.Counter;
import com.android.server.companion.virtual.GenericWindowPolicyController;
import com.android.server.companion.virtual.GenericWindowPolicyController.RunningAppsChangedListener;
import com.android.server.companion.virtual.audio.AudioPlaybackDetector.AudioPlaybackCallback;
@@ -70,10 +72,16 @@ public final class VirtualAudioController implements AudioPlaybackCallback,
@GuardedBy("mCallbackLock")
private IAudioConfigChangedCallback mConfigChangedCallback;
- public VirtualAudioController(Context context) {
+ public VirtualAudioController(Context context, AttributionSource attributionSource) {
mContext = context;
mAudioPlaybackDetector = new AudioPlaybackDetector(context);
mAudioRecordingDetector = new AudioRecordingDetector(context);
+
+ if (android.companion.virtualdevice.flags.Flags.metricsCollection()) {
+ Counter.logIncrementWithUid(
+ "virtual_devices.value_virtual_audio_created_count",
+ attributionSource.getUid());
+ }
}
/**
diff --git a/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java
index 2d82b5e7dd66..3bb1e33f83bb 100644
--- a/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java
+++ b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java
@@ -26,6 +26,7 @@ import android.companion.virtual.VirtualDeviceParams.DevicePolicy;
import android.companion.virtual.camera.VirtualCameraConfig;
import android.companion.virtualcamera.IVirtualCameraService;
import android.companion.virtualcamera.VirtualCameraConfiguration;
+import android.content.AttributionSource;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -35,6 +36,7 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.expresslog.Counter;
import java.io.PrintWriter;
import java.util.Map;
@@ -76,7 +78,8 @@ public final class VirtualCameraController implements IBinder.DeathRecipient {
*
* @param cameraConfig The {@link VirtualCameraConfig} sent by the client.
*/
- public void registerCamera(@NonNull VirtualCameraConfig cameraConfig) {
+ public void registerCamera(@NonNull VirtualCameraConfig cameraConfig,
+ AttributionSource attributionSource) {
checkConfigByPolicy(cameraConfig);
connectVirtualCameraServiceIfNeeded();
@@ -97,6 +100,11 @@ public final class VirtualCameraController implements IBinder.DeathRecipient {
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
+ if (android.companion.virtualdevice.flags.Flags.metricsCollection()) {
+ Counter.logIncrementWithUid(
+ "virtual_devices.value_virtual_camera_created_count",
+ attributionSource.getUid());
+ }
}
/**
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 469209976f5f..94aab7599bb0 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -41,16 +41,18 @@ java_library_static {
genrule {
name: "services.core.protologsrc",
srcs: [
+ ":protolog-impl",
":protolog-groups",
":services.core-sources-am-wm",
],
tools: ["protologtool"],
cmd: "$(location protologtool) transform-protolog-calls " +
"--protolog-class com.android.internal.protolog.common.ProtoLog " +
- "--protolog-impl-class com.android.internal.protolog.ProtoLogImpl " +
- "--protolog-cache-class 'com.android.server.wm.ProtoLogCache' " +
"--loggroups-class com.android.internal.protolog.ProtoLogGroup " +
"--loggroups-jar $(location :protolog-groups) " +
+ "--viewer-config-file-path /etc/core.protolog.pb " +
+ "--legacy-viewer-config-file-path /system/etc/protolog.conf.json.gz " +
+ "--legacy-output-file-path /data/misc/wmtrace/wm_log.winscope " +
"--output-srcjar $(out) " +
"$(locations :services.core-sources-am-wm)",
out: ["services.core.protolog.srcjar"],
@@ -67,25 +69,43 @@ genrule {
"--protolog-class com.android.internal.protolog.common.ProtoLog " +
"--loggroups-class com.android.internal.protolog.ProtoLogGroup " +
"--loggroups-jar $(location :protolog-groups) " +
- "--viewer-conf $(out) " +
+ "--viewer-config-type json " +
+ "--viewer-config $(out) " +
"$(locations :services.core-sources-am-wm)",
out: ["services.core.protolog.json"],
}
genrule {
- name: "checked-protolog.json",
+ name: "gen-core.protolog.pb",
srcs: [
- ":generate-protolog.json",
- ":services.core.protolog.json",
+ ":protolog-groups",
+ ":services.core-sources-am-wm",
],
- cmd: "cp $(location :generate-protolog.json) $(out) && " +
- "{ ! (diff $(out) $(location :services.core.protolog.json) | grep -q '^<') || " +
+ tools: ["protologtool"],
+ cmd: "$(location protologtool) generate-viewer-config " +
+ "--protolog-class com.android.internal.protolog.common.ProtoLog " +
+ "--loggroups-class com.android.internal.protolog.ProtoLogGroup " +
+ "--loggroups-jar $(location :protolog-groups) " +
+ "--viewer-config-type proto " +
+ "--viewer-config $(out) " +
+ "$(locations :services.core-sources-am-wm)",
+ out: ["core.protolog.pb"],
+}
+
+genrule {
+ name: "checked-core.protolog.pb",
+ srcs: [
+ ":gen-core.protolog.pb",
+ ":file-core.protolog.pb",
+ ],
+ cmd: "cp $(location :gen-core.protolog.pb) $(out) && " +
+ "{ ! (diff $(out) $(location :file-core.protolog.pb) | grep -q '^<') || " +
"{ echo -e '\\n\\n################################################################\\n#\\n" +
"# ERROR: ProtoLog viewer config is stale. To update it, run:\\n#\\n" +
- "# cp $${ANDROID_BUILD_TOP}/$(location :generate-protolog.json) " +
- "$${ANDROID_BUILD_TOP}/$(location :services.core.protolog.json)\\n#\\n" +
+ "# cp $${ANDROID_BUILD_TOP}/$(location :gen-core.protolog.pb) " +
+ "$${ANDROID_BUILD_TOP}/$(location :file-core.protolog.pb)\\n#\\n" +
"################################################################\\n\\n' >&2 && false; } }",
- out: ["services.core.protolog.json"],
+ out: ["core.protolog.pb"],
}
genrule {
@@ -157,7 +177,7 @@ java_library_static {
required: [
"default_television.xml",
"gps_debug.conf",
- "protolog.conf.json.gz",
+ "core.protolog.pb",
],
static_libs: [
@@ -207,6 +227,7 @@ java_library_static {
"notification_flags_lib",
"biometrics_flags_lib",
"am_flags_lib",
+ "com_android_server_accessibility_flags_lib",
"com_android_systemui_shared_flags_lib",
"com_android_wm_shell_flags_lib",
"com.android.server.utils_aconfig-java",
@@ -257,14 +278,7 @@ prebuilt_etc {
src: "java/com/android/server/location/gnss/gps_debug.conf",
}
-genrule {
- name: "services.core.json.gz",
- srcs: [":checked-protolog.json"],
- out: ["services.core.protolog.json.gz"],
- cmd: "gzip -c < $(in) > $(out)",
-}
-
prebuilt_etc {
- name: "protolog.conf.json.gz",
- src: ":services.core.json.gz",
+ name: "core.protolog.pb",
+ src: ":checked-core.protolog.pb",
}
diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java
index 81c9ee790d72..4aa5ce19b9b0 100644
--- a/services/core/java/com/android/server/IntentResolver.java
+++ b/services/core/java/com/android/server/IntentResolver.java
@@ -16,6 +16,8 @@
package com.android.server;
+import static android.permission.flags.Flags.ignoreProcessText;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -353,6 +355,13 @@ public abstract class IntentResolver<F, R extends Object> {
public List<R> queryIntentFromList(@NonNull Computer computer, Intent intent,
String resolvedType, boolean defaultOnly, ArrayList<F[]> listCut, int userId,
long customFlags) {
+ if (Intent.ACTION_PROCESS_TEXT.equals(intent.getAction()) && ignoreProcessText()) {
+ // This is for an experiment about deprecating PROCESS_TEXT
+ // Note: SettingsProvider isn't ready early in boot and ACTION_PROCESS_TEXT isn't
+ // queried during boot so we are checking the action before the flag.
+ return Collections.emptyList();
+ }
+
ArrayList<R> resultList = new ArrayList<R>();
final boolean debug = localLOGV ||
@@ -377,6 +386,13 @@ public abstract class IntentResolver<F, R extends Object> {
protected final List<R> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent,
String resolvedType, boolean defaultOnly, @UserIdInt int userId, long customFlags) {
+ if (Intent.ACTION_PROCESS_TEXT.equals(intent.getAction()) && ignoreProcessText()) {
+ // This is for an experiment about deprecating PROCESS_TEXT
+ // Note: SettingsProvider isn't ready early in boot and ACTION_PROCESS_TEXT isn't
+ // queried during boot so we are checking the action before the flag.
+ return Collections.emptyList();
+ }
+
String scheme = intent.getScheme();
ArrayList<R> finalList = new ArrayList<R>();
diff --git a/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java b/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java
index 6ce471e635cd..0904c47b61f2 100644
--- a/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java
+++ b/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java
@@ -16,17 +16,19 @@
package com.android.server;
+import static android.permission.flags.Flags.sensitiveNotificationAppProtection;
import static android.provider.Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS;
-
-import static com.android.internal.util.Preconditions.checkNotNull;
-import static com.android.server.notification.Flags.sensitiveNotificationAppProtection;
+import static android.view.flags.Flags.sensitiveContentAppProtection;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManagerInternal;
import android.media.projection.MediaProjectionInfo;
import android.media.projection.MediaProjectionManager;
+import android.os.Binder;
+import android.os.IBinder;
import android.os.RemoteException;
import android.os.Trace;
import android.os.UserHandle;
@@ -36,29 +38,30 @@ import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
import android.util.Log;
+import android.view.ISensitiveContentProtectionManager;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wm.SensitiveContentPackages.PackageInfo;
import com.android.server.wm.WindowManagerInternal;
+import java.util.Objects;
import java.util.Set;
/**
- * Service that monitors for notifications with sensitive content and protects content from screen
- * sharing
+ * This service protects sensitive content from screen sharing. The service monitors notifications
+ * for sensitive content and protects from screen share. The service also protects sensitive
+ * content rendered on screen during screen share.
*/
public final class SensitiveContentProtectionManagerService extends SystemService {
private static final String TAG = "SensitiveContentProtect";
private static final boolean DEBUG = false;
- private static final boolean sNotificationProtectionEnabled =
- sensitiveNotificationAppProtection();
@VisibleForTesting
@Nullable
NotificationListener mNotificationListener;
- private @Nullable MediaProjectionManager mProjectionManager;
- private @Nullable WindowManagerInternal mWindowManager;
+ @Nullable private MediaProjectionManager mProjectionManager;
+ @Nullable private WindowManagerInternal mWindowManager;
final Object mSensitiveContentProtectionLock = new Object();
@@ -93,7 +96,7 @@ public final class SensitiveContentProtectionManagerService extends SystemServic
public SensitiveContentProtectionManagerService(@NonNull Context context) {
super(context);
- if (sNotificationProtectionEnabled) {
+ if (sensitiveNotificationAppProtection()) {
mNotificationListener = new NotificationListener();
}
}
@@ -111,14 +114,18 @@ public final class SensitiveContentProtectionManagerService extends SystemServic
init(getContext().getSystemService(MediaProjectionManager.class),
LocalServices.getService(WindowManagerInternal.class));
+ if (sensitiveContentAppProtection()) {
+ publishBinderService(Context.SENSITIVE_CONTENT_PROTECTION_SERVICE,
+ new SensitiveContentProtectionManagerServiceBinder());
+ }
}
@VisibleForTesting
void init(MediaProjectionManager projectionManager, WindowManagerInternal windowManager) {
if (DEBUG) Log.d(TAG, "init");
- checkNotNull(projectionManager, "Failed to get valid MediaProjectionManager");
- checkNotNull(windowManager, "Failed to get valid WindowManagerInternal");
+ Objects.requireNonNull(projectionManager);
+ Objects.requireNonNull(windowManager);
mProjectionManager = projectionManager;
mWindowManager = windowManager;
@@ -127,7 +134,7 @@ public final class SensitiveContentProtectionManagerService extends SystemServic
// handler, delegate, and binder death recipient
mProjectionManager.addCallback(mProjectionCallback, getContext().getMainThreadHandler());
- if (sNotificationProtectionEnabled) {
+ if (sensitiveNotificationAppProtection()) {
try {
mNotificationListener.registerAsSystemService(
getContext(),
@@ -145,7 +152,7 @@ public final class SensitiveContentProtectionManagerService extends SystemServic
if (mProjectionManager != null) {
mProjectionManager.removeCallback(mProjectionCallback);
}
- if (sNotificationProtectionEnabled) {
+ if (sensitiveNotificationAppProtection()) {
try {
mNotificationListener.unregisterAsSystemService();
} catch (RemoteException e) {
@@ -170,7 +177,7 @@ public final class SensitiveContentProtectionManagerService extends SystemServic
synchronized (mSensitiveContentProtectionLock) {
mProjectionActive = true;
- if (sNotificationProtectionEnabled) {
+ if (sensitiveNotificationAppProtection()) {
updateAppsThatShouldBlockScreenCapture();
}
}
@@ -306,4 +313,74 @@ public final class SensitiveContentProtectionManagerService extends SystemServic
}
}
}
+
+ /**
+ * Block projection for a package window when the window is showing sensitive content on
+ * the screen, the projection is unblocked when window no more shows sensitive content.
+ *
+ * @param windowToken window where the content is shown.
+ * @param packageName package name.
+ * @param uid uid of the package.
+ * @param isShowingSensitiveContent whether the window is showing sensitive content.
+ */
+ @VisibleForTesting
+ void setSensitiveContentProtection(IBinder windowToken, String packageName, int uid,
+ boolean isShowingSensitiveContent) {
+ synchronized (mSensitiveContentProtectionLock) {
+ if (!mProjectionActive) {
+ return;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "setSensitiveContentProtection - windowToken=" + windowToken
+ + ", package=" + packageName + ", uid=" + uid
+ + ", isShowingSensitiveContent=" + isShowingSensitiveContent);
+ }
+
+ // The window token distinguish this package from packages added for notifications.
+ PackageInfo packageInfo = new PackageInfo(packageName, uid, windowToken);
+ ArraySet<PackageInfo> packageInfos = new ArraySet<>();
+ packageInfos.add(packageInfo);
+ if (isShowingSensitiveContent) {
+ mWindowManager.addBlockScreenCaptureForApps(packageInfos);
+ } else {
+ mWindowManager.removeBlockScreenCaptureForApps(packageInfos);
+ }
+ }
+ }
+
+ private final class SensitiveContentProtectionManagerServiceBinder
+ extends ISensitiveContentProtectionManager.Stub {
+ private final PackageManagerInternal mPackageManagerInternal;
+
+ SensitiveContentProtectionManagerServiceBinder() {
+ mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+ }
+
+ public void setSensitiveContentProtection(IBinder windowToken, String packageName,
+ boolean isShowingSensitiveContent) {
+ Trace.beginSection(
+ "SensitiveContentProtectionManagerService.setSensitiveContentProtection");
+ try {
+ int callingUid = Binder.getCallingUid();
+ verifyCallingPackage(callingUid, packageName);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ SensitiveContentProtectionManagerService.this.setSensitiveContentProtection(
+ windowToken, packageName, callingUid, isShowingSensitiveContent);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ } finally {
+ Trace.endSection();
+ }
+ }
+
+ private void verifyCallingPackage(int callingUid, String callingPackage) {
+ if (mPackageManagerInternal.getPackageUid(
+ callingPackage, 0, UserHandle.getUserId(callingUid)) != callingUid) {
+ throw new SecurityException("Specified calling package [" + callingPackage
+ + "] does not match the calling uid " + callingUid);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/SerialService.java b/services/core/java/com/android/server/SerialService.java
index ff903a0b4c24..82c2038d8011 100644
--- a/services/core/java/com/android/server/SerialService.java
+++ b/services/core/java/com/android/server/SerialService.java
@@ -17,51 +17,123 @@
package com.android.server;
import android.annotation.EnforcePermission;
+import android.annotation.NonNull;
import android.content.Context;
import android.hardware.ISerialManager;
+import android.hardware.SerialManagerInternal;
import android.os.ParcelFileDescriptor;
+import android.os.PermissionEnforcer;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
import java.io.File;
import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.function.Supplier;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class SerialService extends ISerialManager.Stub {
-
private final Context mContext;
- private final String[] mSerialPorts;
+
+ @GuardedBy("mSerialPorts")
+ private final LinkedHashMap<String, Supplier<ParcelFileDescriptor>> mSerialPorts =
+ new LinkedHashMap<>();
+
+ private static final String PREFIX_VIRTUAL = "virtual:";
public SerialService(Context context) {
+ super(PermissionEnforcer.fromContext(context));
mContext = context;
- mSerialPorts = context.getResources().getStringArray(
+
+ synchronized (mSerialPorts) {
+ final String[] serialPorts = getSerialPorts(context);
+ for (String serialPort : serialPorts) {
+ mSerialPorts.put(serialPort, () -> {
+ return native_open(serialPort);
+ });
+ }
+ }
+ }
+
+ @android.ravenwood.annotation.RavenwoodReplace
+ private static String[] getSerialPorts(Context context) {
+ return context.getResources().getStringArray(
com.android.internal.R.array.config_serialPorts);
}
+ private static String[] getSerialPorts$ravenwood(Context context) {
+ return new String[0];
+ }
+
+ public static class Lifecycle extends SystemService {
+ private SerialService mService;
+
+ public Lifecycle(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ mService = new SerialService(getContext());
+ publishBinderService(Context.SERIAL_SERVICE, mService);
+ publishLocalService(SerialManagerInternal.class, mService.mInternal);
+ }
+ }
+
@EnforcePermission(android.Manifest.permission.SERIAL_PORT)
public String[] getSerialPorts() {
super.getSerialPorts_enforcePermission();
- ArrayList<String> ports = new ArrayList<String>();
- for (int i = 0; i < mSerialPorts.length; i++) {
- String path = mSerialPorts[i];
- if (new File(path).exists()) {
- ports.add(path);
+ synchronized (mSerialPorts) {
+ final ArrayList<String> ports = new ArrayList<>();
+ for (String path : mSerialPorts.keySet()) {
+ if (path.startsWith(PREFIX_VIRTUAL) || new File(path).exists()) {
+ ports.add(path);
+ }
}
+ return ports.toArray(new String[ports.size()]);
}
- String[] result = new String[ports.size()];
- ports.toArray(result);
- return result;
}
@EnforcePermission(android.Manifest.permission.SERIAL_PORT)
public ParcelFileDescriptor openSerialPort(String path) {
super.openSerialPort_enforcePermission();
- for (int i = 0; i < mSerialPorts.length; i++) {
- if (mSerialPorts[i].equals(path)) {
- return native_open(path);
+ synchronized (mSerialPorts) {
+ final Supplier<ParcelFileDescriptor> supplier = mSerialPorts.get(path);
+ if (supplier != null) {
+ return supplier.get();
+ } else {
+ throw new IllegalArgumentException("Invalid serial port " + path);
}
}
- throw new IllegalArgumentException("Invalid serial port " + path);
}
+ private final SerialManagerInternal mInternal = new SerialManagerInternal() {
+ @Override
+ public void addVirtualSerialPortForTest(@NonNull String name,
+ @NonNull Supplier<ParcelFileDescriptor> supplier) {
+ synchronized (mSerialPorts) {
+ Preconditions.checkState(!mSerialPorts.containsKey(name),
+ "Port " + name + " already defined");
+ Preconditions.checkArgument(name.startsWith(PREFIX_VIRTUAL),
+ "Port " + name + " must be under " + PREFIX_VIRTUAL);
+ mSerialPorts.put(name, supplier);
+ }
+ }
+
+ @Override
+ public void removeVirtualSerialPortForTest(@NonNull String name) {
+ synchronized (mSerialPorts) {
+ Preconditions.checkState(mSerialPorts.containsKey(name),
+ "Port " + name + " not yet defined");
+ Preconditions.checkArgument(name.startsWith(PREFIX_VIRTUAL),
+ "Port " + name + " must be under " + PREFIX_VIRTUAL);
+ mSerialPorts.remove(name);
+ }
+ }
+ };
+
private native ParcelFileDescriptor native_open(String path);
}
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index a341b4acaca1..9189ea763577 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -263,6 +263,10 @@ public class SystemConfig {
// location settings are off, for emergency purposes, as read from the configuration files.
final ArrayMap<String, ArraySet<String>> mAllowIgnoreLocationSettings = new ArrayMap<>();
+ // These are the packages that are allow-listed to be able to access camera when
+ // the camera privacy state is for driver assistance apps only.
+ final ArrayMap<String, Boolean> mAllowlistCameraPrivacy = new ArrayMap<>();
+
// These are the action strings of broadcasts which are whitelisted to
// be delivered anonymously even to apps which target O+.
final ArraySet<String> mAllowImplicitBroadcasts = new ArraySet<>();
@@ -328,7 +332,6 @@ public class SystemConfig {
private ArrayMap<String, Set<String>> mPackageToUserTypeBlacklist = new ArrayMap<>();
private final ArraySet<String> mRollbackWhitelistedPackages = new ArraySet<>();
- private final ArraySet<String> mAutomaticRollbackDenylistedPackages = new ArraySet<>();
private final ArraySet<String> mWhitelistedStagedInstallers = new ArraySet<>();
// A map from package name of vendor APEXes that can be updated to an installer package name
// allowed to install updates for it.
@@ -483,6 +486,10 @@ public class SystemConfig {
return mAllowedAssociations;
}
+ public ArrayMap<String, Boolean> getCameraPrivacyAllowlist() {
+ return mAllowlistCameraPrivacy;
+ }
+
public ArraySet<String> getBugreportWhitelistedPackages() {
return mBugreportWhitelistedPackages;
}
@@ -491,10 +498,6 @@ public class SystemConfig {
return mRollbackWhitelistedPackages;
}
- public Set<String> getAutomaticRollbackDenylistedPackages() {
- return mAutomaticRollbackDenylistedPackages;
- }
-
public Set<String> getWhitelistedStagedInstallers() {
return mWhitelistedStagedInstallers;
}
@@ -1062,6 +1065,22 @@ public class SystemConfig {
}
XmlUtils.skipCurrentTag(parser);
} break;
+ case "camera-privacy-allowlisted-app" : {
+ if (allowOverrideAppRestrictions) {
+ String pkgname = parser.getAttributeValue(null, "package");
+ boolean isMandatory = XmlUtils.readBooleanAttribute(
+ parser, "mandatory", false);
+ if (pkgname == null) {
+ Slog.w(TAG, "<" + name + "> without package in "
+ + permFile + " at " + parser.getPositionDescription());
+ } else {
+ mAllowlistCameraPrivacy.put(pkgname, isMandatory);
+ }
+ } else {
+ logNotAllowedInPartition(name, permFile, parser);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ } break;
case "allow-ignore-location-settings": {
if (allowOverrideAppRestrictions) {
String pkgname = parser.getAttributeValue(null, "package");
@@ -1457,16 +1476,6 @@ public class SystemConfig {
}
XmlUtils.skipCurrentTag(parser);
} break;
- case "automatic-rollback-denylisted-app": {
- String pkgname = parser.getAttributeValue(null, "package");
- if (pkgname == null) {
- Slog.w(TAG, "<" + name + "> without package in " + permFile
- + " at " + parser.getPositionDescription());
- } else {
- mAutomaticRollbackDenylistedPackages.add(pkgname);
- }
- XmlUtils.skipCurrentTag(parser);
- } break;
case "whitelisted-staged-installer": {
if (allowAppConfigs) {
String pkgname = parser.getAttributeValue(null, "package");
diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java
index 933d2596aed8..7dc9f10e646c 100644
--- a/services/core/java/com/android/server/SystemService.java
+++ b/services/core/java/com/android/server/SystemService.java
@@ -66,6 +66,7 @@ import java.util.List;
* {@hide}
*/
@SystemApi(client = Client.SYSTEM_SERVER)
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class SystemService {
/** @hide */
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index a05b84baf667..20816a1b22c8 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -67,6 +67,8 @@ import java.util.concurrent.TimeUnit;
*
* {@hide}
*/
+@android.ravenwood.annotation.RavenwoodKeepPartialClass
+@android.ravenwood.annotation.RavenwoodKeepStaticInitializer
public final class SystemServiceManager implements Dumpable {
private static final String TAG = SystemServiceManager.class.getSimpleName();
private static final boolean DEBUG = false;
@@ -123,7 +125,8 @@ public final class SystemServiceManager implements Dumpable {
@GuardedBy("mTargetUsers")
@Nullable private TargetUser mCurrentUser;
- SystemServiceManager(Context context) {
+ @android.ravenwood.annotation.RavenwoodKeep
+ public SystemServiceManager(Context context) {
mContext = context;
mServices = new ArrayList<>();
mServiceClassnames = new ArraySet<>();
@@ -136,6 +139,7 @@ public final class SystemServiceManager implements Dumpable {
*
* @return The service instance.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public SystemService startService(String className) {
final Class<SystemService> serviceClass = loadClassFromLoader(className,
this.getClass().getClassLoader());
@@ -178,6 +182,7 @@ public final class SystemServiceManager implements Dumpable {
* Loads and initializes a class from the given classLoader. Returns the class.
*/
@SuppressWarnings("unchecked")
+ @android.ravenwood.annotation.RavenwoodKeep
private static Class<SystemService> loadClassFromLoader(String className,
ClassLoader classLoader) {
try {
@@ -201,6 +206,7 @@ public final class SystemServiceManager implements Dumpable {
* @return The service instance, never null.
* @throws RuntimeException if the service fails to start.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public <T extends SystemService> T startService(Class<T> serviceClass) {
try {
final String name = serviceClass.getName();
@@ -237,6 +243,7 @@ public final class SystemServiceManager implements Dumpable {
}
}
+ @android.ravenwood.annotation.RavenwoodKeep
public void startService(@NonNull final SystemService service) {
// Check if already started
String className = service.getClass().getName();
@@ -261,7 +268,8 @@ public final class SystemServiceManager implements Dumpable {
}
/** Disallow starting new services after this call. */
- void sealStartedServices() {
+ @android.ravenwood.annotation.RavenwoodKeep
+ public void sealStartedServices() {
mServiceClassnames = Collections.emptySet();
mServices = Collections.unmodifiableList(mServices);
}
@@ -273,6 +281,7 @@ public final class SystemServiceManager implements Dumpable {
* @param t trace logger
* @param phase The boot phase to start.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public void startBootPhase(@NonNull TimingsTraceAndSlog t, int phase) {
if (phase <= mCurrentPhase) {
throw new IllegalArgumentException("Next phase must be larger than previous");
@@ -305,13 +314,23 @@ public final class SystemServiceManager implements Dumpable {
if (phase == SystemService.PHASE_BOOT_COMPLETED) {
final long totalBootTime = SystemClock.uptimeMillis() - mRuntimeStartUptime;
t.logDuration("TotalBootTime", totalBootTime);
- SystemServerInitThreadPool.shutdown();
+ shutdownInitThreadPool();
}
}
+ @android.ravenwood.annotation.RavenwoodReplace
+ private void shutdownInitThreadPool() {
+ SystemServerInitThreadPool.shutdown();
+ }
+
+ private void shutdownInitThreadPool$ravenwood() {
+ // Thread pool not configured yet on Ravenwood; ignored
+ }
+
/**
* @return true if system has completed the boot; false otherwise.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public boolean isBootCompleted() {
return mCurrentPhase >= SystemService.PHASE_BOOT_COMPLETED;
}
@@ -646,12 +665,14 @@ public final class SystemServiceManager implements Dumpable {
}
/** Logs the failure. That's all. Tests may rely on parsing it, so only modify carefully. */
+ @android.ravenwood.annotation.RavenwoodKeep
private void logFailure(String onWhat, TargetUser curUser, String serviceName, Exception ex) {
Slog.wtf(TAG, "SystemService failure: Failure reporting " + onWhat + " of user "
+ curUser + " to service " + serviceName, ex);
}
/** Sets the safe mode flag for services to query. */
+ @android.ravenwood.annotation.RavenwoodKeep
void setSafeMode(boolean safeMode) {
mSafeMode = safeMode;
}
@@ -661,6 +682,7 @@ public final class SystemServiceManager implements Dumpable {
*
* @return safe mode flag
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public boolean isSafeMode() {
return mSafeMode;
}
@@ -668,6 +690,7 @@ public final class SystemServiceManager implements Dumpable {
/**
* @return true if runtime was restarted, false if it's normal boot
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public boolean isRuntimeRestarted() {
return mRuntimeRestarted;
}
@@ -675,6 +698,7 @@ public final class SystemServiceManager implements Dumpable {
/**
* @return Time when SystemServer was started, in elapsed realtime.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public long getRuntimeStartElapsedTime() {
return mRuntimeStartElapsedTime;
}
@@ -682,17 +706,20 @@ public final class SystemServiceManager implements Dumpable {
/**
* @return Time when SystemServer was started, in uptime.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public long getRuntimeStartUptime() {
return mRuntimeStartUptime;
}
- void setStartInfo(boolean runtimeRestarted,
+ @android.ravenwood.annotation.RavenwoodKeep
+ public void setStartInfo(boolean runtimeRestarted,
long runtimeStartElapsedTime, long runtimeStartUptime) {
mRuntimeRestarted = runtimeRestarted;
mRuntimeStartElapsedTime = runtimeStartElapsedTime;
mRuntimeStartUptime = runtimeStartUptime;
}
+ @android.ravenwood.annotation.RavenwoodKeep
private void warnIfTooLong(long duration, SystemService service, String operation) {
if (duration > SERVICE_CALL_WARN_TIME_MS) {
Slog.w(TAG, "Service " + service.getClass().getName() + " took " + duration + " ms in "
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 05010f88efaf..f1776f4f9148 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -17,7 +17,7 @@
package com.android.server;
import static android.app.Flags.modesApi;
-import static android.app.Flags.enableNightModeCache;
+import static android.app.Flags.enableNightModeBinderCache;
import static android.app.UiModeManager.ContrastUtils.CONTRAST_DEFAULT_VALUE;
import static android.app.UiModeManager.DEFAULT_PRIORITY;
import static android.app.UiModeManager.MODE_ATTENTION_THEME_OVERLAY_OFF;
@@ -148,7 +148,7 @@ final class UiModeManagerService extends SystemService {
@Override
public void set(int mode) {
mNightModeValue = mode;
- if (enableNightModeCache()) {
+ if (enableNightModeBinderCache()) {
UiModeManager.invalidateNightModeCache();
}
}
diff --git a/services/core/java/com/android/server/adaptiveauth/AdaptiveAuthService.java b/services/core/java/com/android/server/adaptiveauth/AdaptiveAuthService.java
new file mode 100644
index 000000000000..3312be231516
--- /dev/null
+++ b/services/core/java/com/android/server/adaptiveauth/AdaptiveAuthService.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2024 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.adaptiveauth;
+
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST;
+
+import android.app.KeyguardManager;
+import android.content.Context;
+import android.hardware.biometrics.AuthenticationStateListener;
+import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.BiometricSourceType;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseIntArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockSettingsInternal;
+import com.android.internal.widget.LockSettingsStateListener;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.pm.UserManagerInternal;
+import com.android.server.wm.WindowManagerInternal;
+
+import java.util.Objects;
+
+/**
+ * @hide
+ */
+public class AdaptiveAuthService extends SystemService {
+ private static final String TAG = "AdaptiveAuthService";
+ private static final boolean DEBUG = Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.DEBUG);
+
+ @VisibleForTesting
+ static final int MAX_ALLOWED_FAILED_AUTH_ATTEMPTS = 5;
+ private static final int MSG_REPORT_PRIMARY_AUTH_ATTEMPT = 1;
+ private static final int MSG_REPORT_BIOMETRIC_AUTH_ATTEMPT = 2;
+ private static final int AUTH_SUCCESS = 1;
+ private static final int AUTH_FAILURE = 0;
+
+ private final LockPatternUtils mLockPatternUtils;
+ private final LockSettingsInternal mLockSettings;
+ private final BiometricManager mBiometricManager;
+ private final KeyguardManager mKeyguardManager;
+ private final PowerManager mPowerManager;
+ private final WindowManagerInternal mWindowManager;
+ private final UserManagerInternal mUserManager;
+ @VisibleForTesting
+ final SparseIntArray mFailedAttemptsForUser = new SparseIntArray();
+
+ public AdaptiveAuthService(Context context) {
+ this(context, new LockPatternUtils(context));
+ }
+
+ @VisibleForTesting
+ public AdaptiveAuthService(Context context, LockPatternUtils lockPatternUtils) {
+ super(context);
+ mLockPatternUtils = lockPatternUtils;
+ mLockSettings = Objects.requireNonNull(
+ LocalServices.getService(LockSettingsInternal.class));
+ mBiometricManager = Objects.requireNonNull(
+ context.getSystemService(BiometricManager.class));
+ mKeyguardManager = Objects.requireNonNull(context.getSystemService(KeyguardManager.class));
+ mPowerManager = Objects.requireNonNull(context.getSystemService(PowerManager.class));
+ mWindowManager = Objects.requireNonNull(
+ LocalServices.getService(WindowManagerInternal.class));
+ mUserManager = Objects.requireNonNull(LocalServices.getService(UserManagerInternal.class));
+ }
+
+ @Override
+ public void onStart() {}
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+ init();
+ }
+ }
+
+ @VisibleForTesting
+ void init() {
+ mLockSettings.registerLockSettingsStateListener(mLockSettingsStateListener);
+ mBiometricManager.registerAuthenticationStateListener(mAuthenticationStateListener);
+ }
+
+ private final LockSettingsStateListener mLockSettingsStateListener =
+ new LockSettingsStateListener() {
+ @Override
+ public void onAuthenticationSucceeded(int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "LockSettingsStateListener#onAuthenticationSucceeded");
+ }
+ mHandler.obtainMessage(MSG_REPORT_PRIMARY_AUTH_ATTEMPT, AUTH_SUCCESS, userId)
+ .sendToTarget();
+ }
+
+ @Override
+ public void onAuthenticationFailed(int userId) {
+ Slog.i(TAG, "LockSettingsStateListener#onAuthenticationFailed");
+ mHandler.obtainMessage(MSG_REPORT_PRIMARY_AUTH_ATTEMPT, AUTH_FAILURE, userId)
+ .sendToTarget();
+ }
+ };
+
+ private final AuthenticationStateListener mAuthenticationStateListener =
+ new AuthenticationStateListener.Stub() {
+ @Override
+ public void onAuthenticationStarted(int requestReason) {}
+
+ @Override
+ public void onAuthenticationStopped() {}
+
+ @Override
+ public void onAuthenticationSucceeded(int requestReason, int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "AuthenticationStateListener#onAuthenticationSucceeded");
+ }
+ mHandler.obtainMessage(MSG_REPORT_BIOMETRIC_AUTH_ATTEMPT, AUTH_SUCCESS, userId)
+ .sendToTarget();
+ }
+
+ @Override
+ public void onAuthenticationFailed(int requestReason, int userId) {
+ Slog.i(TAG, "AuthenticationStateListener#onAuthenticationFailed");
+ mHandler.obtainMessage(MSG_REPORT_BIOMETRIC_AUTH_ATTEMPT, AUTH_FAILURE, userId)
+ .sendToTarget();
+ }
+
+ @Override
+ public void onAuthenticationAcquired(BiometricSourceType biometricSourceType,
+ int requestReason, int acquiredInfo) {}
+ };
+
+ private final Handler mHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_REPORT_PRIMARY_AUTH_ATTEMPT:
+ handleReportPrimaryAuthAttempt(msg.arg1 != AUTH_FAILURE, msg.arg2);
+ break;
+ case MSG_REPORT_BIOMETRIC_AUTH_ATTEMPT:
+ handleReportBiometricAuthAttempt(msg.arg1 != AUTH_FAILURE, msg.arg2);
+ break;
+ }
+ }
+ };
+
+ private void handleReportPrimaryAuthAttempt(boolean success, int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "handleReportPrimaryAuthAttempt: success=" + success
+ + ", userId=" + userId);
+ }
+ reportAuthAttempt(success, userId);
+ }
+
+ private void handleReportBiometricAuthAttempt(boolean success, int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "handleReportBiometricAuthAttempt: success=" + success
+ + ", userId=" + userId);
+ }
+ reportAuthAttempt(success, userId);
+ }
+
+ private void reportAuthAttempt(boolean success, int userId) {
+ if (success) {
+ // Deleting the entry effectively resets the counter of failed attempts for the user
+ mFailedAttemptsForUser.delete(userId);
+ return;
+ }
+
+ final int numFailedAttempts = mFailedAttemptsForUser.get(userId, 0) + 1;
+ Slog.i(TAG, "reportAuthAttempt: numFailedAttempts=" + numFailedAttempts
+ + ", userId=" + userId);
+ mFailedAttemptsForUser.put(userId, numFailedAttempts);
+
+ // Don't lock again if the device is already locked and if Keyguard is already showing and
+ // isn't trivially dismissible
+ if (mKeyguardManager.isDeviceLocked(userId) && mKeyguardManager.isKeyguardLocked()) {
+ Slog.d(TAG, "Not locking the device because the device is already locked.");
+ return;
+ }
+
+ if (numFailedAttempts < MAX_ALLOWED_FAILED_AUTH_ATTEMPTS) {
+ Slog.d(TAG, "Not locking the device because the number of failed attempts is below"
+ + " the threshold.");
+ return;
+ }
+
+ //TODO: additionally consider the trust signal before locking device
+ lockDevice(userId);
+ }
+
+ /**
+ * Locks the device and requires primary auth or biometric auth for unlocking
+ */
+ private void lockDevice(int userId) {
+ // Require either primary auth or biometric auth to unlock the device again. Keyguard and
+ // bouncer will also check the StrongAuthFlag for the user to display correct strings for
+ // explaining why the device is locked
+ mLockPatternUtils.requireStrongAuth(SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST, userId);
+
+ // If userId is a profile that has a different parent userId (regardless of its profile
+ // type, or whether it's a profile with unified challenges or not), its parent userId that
+ // owns the Keyguard will also be locked
+ final int parentUserId = mUserManager.getProfileParentId(userId);
+ Slog.i(TAG, "lockDevice: userId=" + userId + ", parentUserId=" + parentUserId);
+ if (parentUserId != userId) {
+ mLockPatternUtils.requireStrongAuth(SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST,
+ parentUserId);
+ }
+
+ // Power off the display
+ mPowerManager.goToSleep(SystemClock.uptimeMillis());
+
+ // Lock the device
+ mWindowManager.lockNow();
+ }
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index d97731c85d38..ff837974bf3c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -243,7 +243,7 @@ final class ActivityManagerConstants extends ContentObserver {
/**
* The default value to {@link #KEY_ENABLE_NEW_OOMADJ}.
*/
- private static final boolean DEFAULT_ENABLE_NEW_OOM_ADJ = false;
+ private static final boolean DEFAULT_ENABLE_NEW_OOM_ADJ = Flags.oomadjusterCorrectnessRewrite();
/**
* Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index bfdcb95b099e..2dd2f8fde797 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -930,7 +930,7 @@ public class ActivityManagerService extends IActivityManager.Stub
* Tracks all users with computed color resources by ThemeOverlaycvontroller
*/
@GuardedBy("this")
- private final Set<Integer> mThemeOverlayReadiness = new HashSet<>();
+ private final Set<Integer> mThemeOverlayReadyUsers = new HashSet<>();
/**
* Tracks association information for a particular package along with debuggability.
@@ -2351,7 +2351,7 @@ public class ActivityManagerService extends IActivityManager.Stub
mService.startBroadcastObservers();
} else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
mService.mPackageWatchdog.onPackagesReady();
- mService.setHomeTimeout();
+ mService.scheduleHomeTimeout();
}
}
@@ -5327,40 +5327,43 @@ public class ActivityManagerService extends IActivityManager.Stub
/**
* Starts Home if there is no completion signal from ThemeOverlayController
*/
- private void setHomeTimeout() {
+ private void scheduleHomeTimeout() {
if (enableHomeDelay() && mHasHomeDelay.compareAndSet(false, true)) {
+ int userId = mUserController.getCurrentUserId();
mHandler.postDelayed(() -> {
- if (!getThemeOverlayReadiness()) {
+ if (!isThemeOverlayReady(userId)) {
Slog.d(TAG,
"ThemeHomeDelay: ThemeOverlayController not responding, launching "
+ "Home after "
+ HOME_LAUNCH_TIMEOUT_MS + "ms");
- setThemeOverlayReady(true);
+ setThemeOverlayReady(userId);
}
}, HOME_LAUNCH_TIMEOUT_MS);
}
}
/**
- * Used by ThemeOverlayController to notify all listeners for
- * color palette readiness.
+ * Used by ThemeOverlayController to notify when color
+ * palette is ready.
+ *
+ * @param userId The ID of the user where ThemeOverlayController is ready.
+ *
+ * @throws RemoteException
+ *
* @hide
*/
@Override
- public void setThemeOverlayReady(boolean readiness) {
+ public void setThemeOverlayReady(@UserIdInt int userId) {
enforceCallingPermission(Manifest.permission.SET_THEME_OVERLAY_CONTROLLER_READY,
"setThemeOverlayReady");
- int currentUserId = mUserController.getCurrentUserId();
-
- boolean updateReadiness;
- synchronized (mThemeOverlayReadiness) {
- updateReadiness = readiness ? mThemeOverlayReadiness.add(currentUserId)
- : mThemeOverlayReadiness.remove(currentUserId);
+ boolean updateUser;
+ synchronized (mThemeOverlayReadyUsers) {
+ updateUser = mThemeOverlayReadyUsers.add(userId);
}
- if (updateReadiness && readiness && enableHomeDelay()) {
- mAtmInternal.startHomeOnAllDisplays(currentUserId, "setThemeOverlayReady");
+ if (updateUser && enableHomeDelay()) {
+ mAtmInternal.startHomeOnAllDisplays(userId, "setThemeOverlayReady");
}
}
@@ -5370,10 +5373,9 @@ public class ActivityManagerService extends IActivityManager.Stub
*
* @hide
*/
- public boolean getThemeOverlayReadiness() {
- int uid = mUserController.getCurrentUserId();
- synchronized (mThemeOverlayReadiness) {
- return mThemeOverlayReadiness.contains(uid);
+ public boolean isThemeOverlayReady(int userId) {
+ synchronized (mThemeOverlayReadyUsers) {
+ return mThemeOverlayReadyUsers.contains(userId);
}
}
@@ -5476,6 +5478,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
intents[i] = new Intent(intent);
+ intents[i].removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
}
}
if (resolvedTypes != null && resolvedTypes.length != intents.length) {
@@ -13664,9 +13667,13 @@ public class ActivityManagerService extends IActivityManager.Stub
throws TransactionTooLargeException {
enforceNotIsolatedCaller("startService");
enforceAllowedToStartOrBindServiceIfSdkSandbox(service);
- // Refuse possible leaked file descriptors
- if (service != null && service.hasFileDescriptors() == true) {
- throw new IllegalArgumentException("File descriptors passed in Intent");
+ if (service != null) {
+ // Refuse possible leaked file descriptors
+ if (service.hasFileDescriptors()) {
+ throw new IllegalArgumentException("File descriptors passed in Intent");
+ }
+ // Remove existing mismatch flag so it can be properly updated later
+ service.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
}
if (callingPackage == null) {
@@ -13895,9 +13902,13 @@ public class ActivityManagerService extends IActivityManager.Stub
enforceNotIsolatedCaller("bindService");
enforceAllowedToStartOrBindServiceIfSdkSandbox(service);
- // Refuse possible leaked file descriptors
- if (service != null && service.hasFileDescriptors() == true) {
- throw new IllegalArgumentException("File descriptors passed in Intent");
+ if (service != null) {
+ // Refuse possible leaked file descriptors
+ if (service.hasFileDescriptors()) {
+ throw new IllegalArgumentException("File descriptors passed in Intent");
+ }
+ // Remove existing mismatch flag so it can be properly updated later
+ service.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
}
if (callingPackage == null) {
@@ -15798,9 +15809,13 @@ public class ActivityManagerService extends IActivityManager.Stub
}
final Intent verifyBroadcastLocked(Intent intent) {
- // Refuse possible leaked file descriptors
- if (intent != null && intent.hasFileDescriptors() == true) {
- throw new IllegalArgumentException("File descriptors passed in Intent");
+ if (intent != null) {
+ // Refuse possible leaked file descriptors
+ if (intent.hasFileDescriptors()) {
+ throw new IllegalArgumentException("File descriptors passed in Intent");
+ }
+ // Remove existing mismatch flag so it can be properly updated later
+ intent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
}
int flags = intent.getFlags();
@@ -18114,8 +18129,8 @@ public class ActivityManagerService extends IActivityManager.Stub
// Clean up various services by removing the user
mBatteryStatsService.onUserRemoved(userId);
- synchronized (mThemeOverlayReadiness) {
- mThemeOverlayReadiness.remove(userId);
+ synchronized (mThemeOverlayReadyUsers) {
+ mThemeOverlayReadyUsers.remove(userId);
}
}
@@ -19477,8 +19492,8 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
- public boolean getThemeOverlayReadiness() {
- return ActivityManagerService.this.getThemeOverlayReadiness();
+ public boolean isThemeOverlayReady(int userId) {
+ return ActivityManagerService.this.isThemeOverlayReady(userId);
}
}
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index e4956b348dc9..0ce1407004f5 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -2305,6 +2305,8 @@ public class AppProfiler {
}
ps.addCpuTimeLocked(st.rel_utime, st.rel_stime);
}
+ EventLogTags.writeAmCpu(st.pid, st.uid, st.baseName,
+ st.rel_uptime, st.rel_utime, st.rel_stime);
}
}
diff --git a/services/core/java/com/android/server/am/AppStartInfoTracker.java b/services/core/java/com/android/server/am/AppStartInfoTracker.java
index c85723525aa1..1dc384d61c91 100644
--- a/services/core/java/com/android/server/am/AppStartInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppStartInfoTracker.java
@@ -383,6 +383,11 @@ public final class AppStartInfoTracker {
start.setDefiningUid(definingUid > 0 ? definingUid : app.info.uid);
start.setProcessName(app.processName);
start.setPackageName(app.info.packageName);
+ if (android.content.pm.Flags.stayStopped()) {
+ // TODO: Verify this is created at the right time to have the correct force-stopped
+ // state in the ProcessRecord. Also use the WindowProcessRecord if activity.
+ start.setForceStopped(app.wasForceStopped());
+ }
}
void reportApplicationOnCreateTimeNanos(ProcessRecord app, long timeNs) {
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index 0f75ad481662..80387322b038 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -127,6 +127,9 @@ option java_package com.android.server.am
30102 am_foreground_service_stop (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3),(fgsType|1)
30103 am_foreground_service_timed_out (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3),(fgsType|1)
+# Report collection of cpu used by a process
+30104 am_cpu (Pid|2|5),(UID|2|5),(Base Name|3),(Uptime|2|3),(Stime|2|3),(Utime|2|3)
+
# Intent Sender redirect for UserHandle.USER_CURRENT
30110 am_intent_sender_redirect_user (userId|1|5)
diff --git a/services/core/java/com/android/server/am/LmkdStatsReporter.java b/services/core/java/com/android/server/am/LmkdStatsReporter.java
index 507fd9efaffd..595d16d32e7b 100644
--- a/services/core/java/com/android/server/am/LmkdStatsReporter.java
+++ b/services/core/java/com/android/server/am/LmkdStatsReporter.java
@@ -34,7 +34,6 @@ public final class LmkdStatsReporter {
static final String TAG = TAG_WITH_CLASS_NAME ? "LmkdStatsReporter" : TAG_AM;
public static final int KILL_OCCURRED_MSG_SIZE = 80;
- public static final int STATE_CHANGED_MSG_SIZE = 8;
private static final int PRESSURE_AFTER_KILL = 0;
private static final int NOT_RESPONDING = 1;
@@ -79,16 +78,6 @@ public final class LmkdStatsReporter {
}
}
- /**
- * Processes the LMK_STATE_CHANGED packet
- * Logs the change in LMKD state which is used as start/stop boundaries for logging
- * LMK_KILL_OCCURRED event.
- * Code: LMK_STATE_CHANGED = 54
- */
- public static void logStateChanged(int state) {
- FrameworkStatsLog.write(FrameworkStatsLog.LMK_STATE_CHANGED, state);
- }
-
private static int mapKillReason(int reason) {
switch (reason) {
case PRESSURE_AFTER_KILL:
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 7d82f0c2a63e..9568116288b5 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -19,6 +19,7 @@ package com.android.server.am;
import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL;
import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL_IMPLICIT;
import static android.app.ActivityManager.PROCESS_CAPABILITY_BFSL;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
@@ -67,7 +68,10 @@ import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_UNBIND_SERVICE;
import static android.content.Context.BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION;
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE;
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL;
+import static android.media.audio.Flags.foregroundAudioControl;
import static android.os.Process.SCHED_OTHER;
import static android.os.Process.THREAD_GROUP_BACKGROUND;
import static android.os.Process.THREAD_GROUP_DEFAULT;
@@ -1624,6 +1628,7 @@ public class OomAdjuster {
int appUid;
int logUid;
int processStateCurTop;
+ String mAdjType;
ProcessStateRecord mState;
void initialize(ProcessRecord app, int adj, boolean foregroundActivities,
@@ -1638,6 +1643,7 @@ public class OomAdjuster {
this.appUid = appUid;
this.logUid = logUid;
this.processStateCurTop = processStateCurTop;
+ mAdjType = app.mState.getAdjType();
this.mState = app.mState;
}
@@ -1646,14 +1652,14 @@ public class OomAdjuster {
// App has a visible activity; only upgrade adjustment.
if (adj > VISIBLE_APP_ADJ) {
adj = VISIBLE_APP_ADJ;
- mState.setAdjType("vis-activity");
+ mAdjType = "vis-activity";
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to vis-activity: " + app);
}
}
if (procState > processStateCurTop) {
procState = processStateCurTop;
- mState.setAdjType("vis-activity");
+ mAdjType = "vis-activity";
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise procstate to vis-activity (top): " + app);
@@ -1662,8 +1668,6 @@ public class OomAdjuster {
if (schedGroup < SCHED_GROUP_DEFAULT) {
schedGroup = SCHED_GROUP_DEFAULT;
}
- mState.setCached(false);
- mState.setEmpty(false);
foregroundActivities = true;
mHasVisibleActivities = true;
}
@@ -1672,14 +1676,14 @@ public class OomAdjuster {
public void onPausedActivity() {
if (adj > PERCEPTIBLE_APP_ADJ) {
adj = PERCEPTIBLE_APP_ADJ;
- mState.setAdjType("pause-activity");
+ mAdjType = "pause-activity";
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to pause-activity: " + app);
}
}
if (procState > processStateCurTop) {
procState = processStateCurTop;
- mState.setAdjType("pause-activity");
+ mAdjType = "pause-activity";
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise procstate to pause-activity (top): " + app);
@@ -1688,8 +1692,6 @@ public class OomAdjuster {
if (schedGroup < SCHED_GROUP_DEFAULT) {
schedGroup = SCHED_GROUP_DEFAULT;
}
- mState.setCached(false);
- mState.setEmpty(false);
foregroundActivities = true;
mHasVisibleActivities = false;
}
@@ -1698,7 +1700,7 @@ public class OomAdjuster {
public void onStoppingActivity(boolean finishing) {
if (adj > PERCEPTIBLE_APP_ADJ) {
adj = PERCEPTIBLE_APP_ADJ;
- mState.setAdjType("stop-activity");
+ mAdjType = "stop-activity";
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise adj to stop-activity: " + app);
@@ -1714,15 +1716,13 @@ public class OomAdjuster {
if (!finishing) {
if (procState > PROCESS_STATE_LAST_ACTIVITY) {
procState = PROCESS_STATE_LAST_ACTIVITY;
- mState.setAdjType("stop-activity");
+ mAdjType = "stop-activity";
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise procstate to stop-activity: " + app);
}
}
}
- mState.setCached(false);
- mState.setEmpty(false);
foregroundActivities = true;
mHasVisibleActivities = false;
}
@@ -1731,7 +1731,7 @@ public class OomAdjuster {
public void onOtherActivity() {
if (procState > PROCESS_STATE_CACHED_ACTIVITY) {
procState = PROCESS_STATE_CACHED_ACTIVITY;
- mState.setAdjType("cch-act");
+ mAdjType = "cch-act";
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise procstate to cached activity: " + app);
@@ -1787,8 +1787,6 @@ public class OomAdjuster {
state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN);
state.setAdjSource(null);
state.setAdjTarget(null);
- state.setEmpty(false);
- state.setCached(false);
if (!couldRecurse || !cycleReEval) {
// Don't reset this flag when doing cycles re-evaluation.
state.setNoKillOnBgRestrictedAndIdle(false);
@@ -1940,8 +1938,6 @@ public class OomAdjuster {
adj = cachedAdj;
procState = PROCESS_STATE_CACHED_EMPTY;
if (!couldRecurse || !state.containsCycle()) {
- state.setCached(true);
- state.setEmpty(true);
state.setAdjType("cch-empty");
}
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
@@ -1960,6 +1956,7 @@ public class OomAdjuster {
hasVisibleActivities = state.getCachedHasVisibleActivities();
procState = state.getCachedProcState();
schedGroup = state.getCachedSchedGroup();
+ state.setAdjType(state.getCachedAdjType());
}
if (procState > PROCESS_STATE_CACHED_RECENT && state.getCachedHasRecentTasks()) {
@@ -2017,7 +2014,6 @@ public class OomAdjuster {
adj = newAdj;
procState = newProcState;
state.setAdjType(adjType);
- state.setCached(false);
schedGroup = SCHED_GROUP_DEFAULT;
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
@@ -2075,7 +2071,6 @@ public class OomAdjuster {
// thus out of background check), so we yes the best background level we can.
adj = PERCEPTIBLE_APP_ADJ;
procState = PROCESS_STATE_TRANSIENT_BACKGROUND;
- state.setCached(false);
state.setAdjType("force-imp");
state.setAdjSource(state.getForcingToImportant());
schedGroup = SCHED_GROUP_DEFAULT;
@@ -2090,7 +2085,6 @@ public class OomAdjuster {
// We don't want to kill the current heavy-weight process.
adj = HEAVY_WEIGHT_APP_ADJ;
schedGroup = SCHED_GROUP_BACKGROUND;
- state.setCached(false);
state.setAdjType("heavy");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to heavy: " + app);
@@ -2111,7 +2105,6 @@ public class OomAdjuster {
// home app, so we don't want to let it go into the background.
adj = HOME_APP_ADJ;
schedGroup = SCHED_GROUP_BACKGROUND;
- state.setCached(false);
state.setAdjType("home");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to home: " + app);
@@ -2145,7 +2138,6 @@ public class OomAdjuster {
if (adj > PREVIOUS_APP_ADJ) {
adj = PREVIOUS_APP_ADJ;
schedGroup = SCHED_GROUP_BACKGROUND;
- state.setCached(false);
state.setAdjType("previous");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to prev: " + app);
@@ -2193,7 +2185,6 @@ public class OomAdjuster {
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to backup: " + app);
}
- state.setCached(false);
}
if (procState > ActivityManager.PROCESS_STATE_BACKUP) {
procState = ActivityManager.PROCESS_STATE_BACKUP;
@@ -2247,7 +2238,6 @@ public class OomAdjuster {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
"Raise adj to started service: " + app);
}
- state.setCached(false);
}
}
// If we have let the service slide into the background
@@ -2266,6 +2256,15 @@ public class OomAdjuster {
(fgsType & FOREGROUND_SERVICE_TYPE_LOCATION)
!= 0 ? PROCESS_CAPABILITY_FOREGROUND_LOCATION : 0;
+ if (foregroundAudioControl()) { // flag check
+ final int fgsAudioType = FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
+ | FOREGROUND_SERVICE_TYPE_CAMERA
+ | FOREGROUND_SERVICE_TYPE_MICROPHONE
+ | FOREGROUND_SERVICE_TYPE_PHONE_CALL;
+ capabilityFromFGS |= (psr.getForegroundServiceTypes() & fgsAudioType) != 0
+ ? PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL : 0;
+ }
+
final boolean enabled = state.getCachedCompatChange(
CACHED_COMPAT_CHANGE_CAMERA_MICROPHONE_CAPABILITY);
if (enabled) {
@@ -2363,7 +2362,6 @@ public class OomAdjuster {
adj = FOREGROUND_APP_ADJ;
state.setCurRawAdj(adj);
schedGroup = SCHED_GROUP_DEFAULT;
- state.setCached(false);
state.setAdjType("ext-provider");
state.setAdjTarget(cpr.name);
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
@@ -2386,7 +2384,6 @@ public class OomAdjuster {
if (adj > PREVIOUS_APP_ADJ) {
adj = PREVIOUS_APP_ADJ;
schedGroup = SCHED_GROUP_BACKGROUND;
- state.setCached(false);
state.setAdjType("recent-provider");
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ,
@@ -2694,10 +2691,12 @@ public class OomAdjuster {
if (adj > clientAdj) {
adjType = "cch-bound-ui-services";
}
- if (state.setCached(false, dryRun)) {
+
+ if (state.isCached() && dryRun) {
// Bail out early, as we only care about the return value for a dryrun.
return true;
}
+
clientAdj = adj;
clientProcState = procState;
} else {
@@ -2782,12 +2781,14 @@ public class OomAdjuster {
newAdj = adj;
}
}
+
if (!cstate.isCached()) {
- if (state.setCached(false, dryRun)) {
+ if (state.isCached() && dryRun) {
// Bail out early, as we only care about the return value for a dryrun.
return true;
}
}
+
if (adj > newAdj) {
adj = newAdj;
if (state.setCurRawAdj(adj, dryRun)) {
@@ -2945,8 +2946,8 @@ public class OomAdjuster {
schedGroup = SCHED_GROUP_DEFAULT;
}
}
+
if (!dryRun) {
- state.setCached(false);
state.setAdjType("service");
state.setAdjTypeCode(ActivityManager.RunningAppProcessInfo
.REASON_SERVICE_IN_USE);
@@ -2987,7 +2988,6 @@ public class OomAdjuster {
}
state.setCurCapability(capability);
- state.setEmpty(false);
return updated;
}
@@ -3077,7 +3077,8 @@ public class OomAdjuster {
}
adjType = "provider";
}
- if (state.setCached(state.isCached() & cstate.isCached(), dryRun)) {
+
+ if (state.isCached() && !cstate.isCached() && dryRun) {
// Bail out early, as we only care about the return value for a dryrun.
return true;
}
@@ -3144,7 +3145,6 @@ public class OomAdjuster {
}
state.setCurCapability(capability);
- state.setEmpty(false);
return false;
}
@@ -3601,7 +3601,6 @@ public class OomAdjuster {
state.setCurProcState(initialProcState);
state.setCurRawProcState(initialProcState);
state.setCurCapability(initialCapability);
- state.setCached(initialCached);
state.setCurAdj(ProcessList.FOREGROUND_APP_ADJ);
state.setCurRawAdj(ProcessList.FOREGROUND_APP_ADJ);
diff --git a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
index f85b03e8b4eb..1bf779adcce1 100644
--- a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
+++ b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
@@ -722,14 +722,8 @@ public class OomAdjusterModernImpl extends OomAdjuster {
performNewUpdateOomAdjLSP(oomAdjReason, topApp, targetProcesses, activeUids,
fullUpdate, now, UNKNOWN_ADJ);
- if (fullUpdate) {
- assignCachedAdjIfNecessary(mProcessList.getLruProcessesLOSP());
- } else {
- activeProcesses.clear();
- activeProcesses.addAll(targetProcesses);
- assignCachedAdjIfNecessary(activeProcesses);
- activeProcesses.clear();
- }
+ // TODO: b/319163103 - optimize cache adj assignment to not require the whole lru list.
+ assignCachedAdjIfNecessary(mProcessList.getLruProcessesLOSP());
postUpdateOomAdjInnerLSP(oomAdjReason, activeUids, now, nowElapsed, oldTime);
targetProcesses.clear();
@@ -996,11 +990,11 @@ public class OomAdjusterModernImpl extends OomAdjuster {
&& service.mState.getMaxAdj() < FOREGROUND_APP_ADJ)
|| (service.mState.getCurAdj() <= FOREGROUND_APP_ADJ
&& service.mState.getCurrentSchedulingGroup() > SCHED_GROUP_BACKGROUND
- && service.mState.getCurProcState() <= PROCESS_STATE_TOP)) {
+ && service.mState.getCurProcState() <= PROCESS_STATE_TOP)
+ || (service.isSdkSandbox && cr.binding.attributedClient != null)) {
continue;
}
-
computeServiceHostOomAdjLSP(cr, service, app, now, topApp, fullUpdate, false, false,
oomAdjReason, cachedAdj, false, false);
}
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index 1412259abf89..2ef433cad8ce 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -64,6 +64,9 @@ import com.android.server.wm.WindowProcessController;
import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
@@ -76,6 +79,9 @@ import java.util.concurrent.atomic.AtomicLong;
* The error state of the process, such as if it's crashing/ANR etc.
*/
class ProcessErrorStateRecord {
+ private static final DateTimeFormatter DROPBOX_TIME_FORMATTER =
+ DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSZ");
+
final ProcessRecord mApp;
private final ActivityManagerService mService;
@@ -444,6 +450,13 @@ class ProcessErrorStateRecord {
info.append("ErrorId: ").append(errorId.toString()).append("\n");
}
info.append("Frozen: ").append(mApp.mOptRecord.isFrozen()).append("\n");
+ if (timeoutRecord != null && timeoutRecord.mEndUptimeMillis > 0) {
+ long millisSinceEndUptimeMs = anrTime - timeoutRecord.mEndUptimeMillis;
+ String formattedTime = DROPBOX_TIME_FORMATTER.format(
+ Instant.now().minusMillis(millisSinceEndUptimeMs)
+ .atZone(ZoneId.systemDefault()));
+ info.append("Timestamp: ").append(formattedTime).append("\n");
+ }
// Retrieve controller with max ANR delay from AnrControllers
// Note that we retrieve the controller before dumping stacks because dumping stacks can
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 3adea7a929cd..89c89944e92e 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -348,7 +348,7 @@ public final class ProcessList {
// LMK_PROCKILL
// LMK_UPDATE_PROPS
// LMK_KILL_OCCURRED
- // LMK_STATE_CHANGED
+ // LMK_START_MONITORING
static final byte LMK_TARGET = 0;
static final byte LMK_PROCPRIO = 1;
static final byte LMK_PROCREMOVE = 2;
@@ -358,7 +358,6 @@ public final class ProcessList {
static final byte LMK_PROCKILL = 6; // Note: this is an unsolicited command
static final byte LMK_UPDATE_PROPS = 7;
static final byte LMK_KILL_OCCURRED = 8; // Msg to subscribed clients on kill occurred event
- static final byte LMK_STATE_CHANGED = 9; // Msg to subscribed clients on state changed
static final byte LMK_START_MONITORING = 9; // Start monitoring if delayed earlier
// Low Memory Killer Daemon command codes.
@@ -965,14 +964,6 @@ public final class ProcessList {
foregroundServices.first,
foregroundServices.second);
return true;
- case LMK_STATE_CHANGED:
- if (receivedLen
- != LmkdStatsReporter.STATE_CHANGED_MSG_SIZE) {
- return false;
- }
- final int state = inputData.readInt();
- LmkdStatsReporter.logStateChanged(state);
- return true;
default:
return false;
}
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index d23d9fb16d6c..7009bd0b4695 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1122,11 +1122,6 @@ class ProcessRecord implements WindowProcessListener {
mInFullBackup = inFullBackup;
}
- @GuardedBy("mService")
- public void setCached(boolean cached) {
- mState.setCached(cached);
- }
-
@Override
@GuardedBy("mService")
public boolean isCached() {
@@ -1678,7 +1673,11 @@ class ProcessRecord implements WindowProcessListener {
final ArrayList<ConnectionRecord> clist = serviceConnections.valueAt(j);
for (int k = clist.size() - 1; k >= 0; k--) {
final ConnectionRecord cr = clist.get(k);
- consumer.accept(cr.binding.client);
+ if (isSdkSandbox && cr.binding.attributedClient != null) {
+ consumer.accept(cr.binding.attributedClient);
+ } else {
+ consumer.accept(cr.binding.client);
+ }
}
}
}
@@ -1689,25 +1688,5 @@ class ProcessRecord implements WindowProcessListener {
consumer.accept(conn.client);
}
}
- // If this process is a sandbox itself, also add the app on whose behalf
- // its running
- if (isSdkSandbox) {
- for (int is = mServices.numberOfRunningServices() - 1; is >= 0; is--) {
- ServiceRecord s = mServices.getRunningServiceAt(is);
- ArrayMap<IBinder, ArrayList<ConnectionRecord>> serviceConnections =
- s.getConnections();
- for (int conni = serviceConnections.size() - 1; conni >= 0; conni--) {
- ArrayList<ConnectionRecord> clist = serviceConnections.valueAt(conni);
- for (int i = clist.size() - 1; i >= 0; i--) {
- ConnectionRecord cr = clist.get(i);
- ProcessRecord attributedApp = cr.binding.attributedClient;
- if (attributedApp == null || attributedApp == this) {
- continue;
- }
- consumer.accept(attributedApp);
- }
- }
- }
- }
}
}
diff --git a/services/core/java/com/android/server/am/ProcessServiceRecord.java b/services/core/java/com/android/server/am/ProcessServiceRecord.java
index 57d233e7c503..562beaf50a7f 100644
--- a/services/core/java/com/android/server/am/ProcessServiceRecord.java
+++ b/services/core/java/com/android/server/am/ProcessServiceRecord.java
@@ -205,10 +205,10 @@ final class ProcessServiceRecord {
}
/**
- * Returns the FGS typps, but it doesn't tell if the types include "NONE" or not, so
- * do not use it outside of this class.
+ * Returns the FGS types, but it doesn't tell if the types include "NONE" or not, use
+ * {@link #hasForegroundServices()}
*/
- private int getForegroundServiceTypes() {
+ int getForegroundServiceTypes() {
return mHasForegroundServices ? mFgServiceTypes : 0;
}
diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java
index 8362eaf76d55..8de748e37b5e 100644
--- a/services/core/java/com/android/server/am/ProcessStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessStateRecord.java
@@ -17,6 +17,7 @@
package com.android.server.am;
import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE;
+import static android.app.ActivityManager.PROCESS_STATE_CACHED_EMPTY;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_UI_VISIBILITY;
import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_ACTIVITY;
@@ -24,6 +25,7 @@ import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_BROADCAST_RE
import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_STARTED_SERVICE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
+import static com.android.server.am.ProcessList.CACHED_APP_MIN_ADJ;
import static com.android.server.am.ProcessRecord.TAG;
import android.annotation.ElapsedRealtimeLong;
@@ -283,18 +285,6 @@ final class ProcessStateRecord {
private long mLastTopTime = Long.MIN_VALUE;
/**
- * Is this an empty background process?
- */
- @GuardedBy("mService")
- private boolean mEmpty;
-
- /**
- * Is this a cached process?
- */
- @GuardedBy("mService")
- private boolean mCached;
-
- /**
* This is a system process, but not currently showing UI.
*/
@GuardedBy("mService")
@@ -395,7 +385,7 @@ final class ProcessStateRecord {
private boolean mNoKillOnBgRestrictedAndIdle;
/**
- * Last set value of {@link #mCached}.
+ * Last set value of {@link #isCached()}.
*/
@GuardedBy("mService")
private boolean mSetCached;
@@ -408,7 +398,7 @@ final class ProcessStateRecord {
/**
* The last time when the {@link #mNoKillOnBgRestrictedAndIdle} is false and the
- * {@link #mCached} is true, and either the former state is flipping from true to false
+ * {@link #isCached()} is true, and either the former state is flipping from true to false
* when latter state is true, or the latter state is flipping from false to true when the
* former state is false.
*/
@@ -446,6 +436,8 @@ final class ProcessStateRecord {
};
@GuardedBy("mService")
+ private String mCachedAdjType = null;
+ @GuardedBy("mService")
private int mCachedAdj = ProcessList.INVALID_ADJ;
@GuardedBy("mService")
private boolean mCachedForegroundActivities = false;
@@ -535,7 +527,7 @@ final class ProcessStateRecord {
@GuardedBy(anyOf = {"mService", "mProcLock"})
int getSetAdjWithServices() {
- if (mSetAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
+ if (mSetAdj >= CACHED_APP_MIN_ADJ) {
if (mHasStartedServices) {
return ProcessList.SERVICE_B_ADJ;
}
@@ -915,36 +907,13 @@ final class ProcessStateRecord {
}
@GuardedBy("mService")
- void setEmpty(boolean empty) {
- mEmpty = empty;
- }
-
- @GuardedBy("mService")
boolean isEmpty() {
- return mEmpty;
- }
-
- @GuardedBy("mService")
- void setCached(boolean cached) {
- setCached(cached, false);
- }
-
- /**
- * @return {@code true} if it's a dry run and it's going to uncache the process
- * if it was a real run.
- */
- @GuardedBy("mService")
- boolean setCached(boolean cached, boolean dryRun) {
- if (dryRun) {
- return mCached && !cached;
- }
- mCached = cached;
- return false;
+ return mCurProcState >= PROCESS_STATE_CACHED_EMPTY;
}
@GuardedBy("mService")
boolean isCached() {
- return mCached;
+ return mCurAdj >= CACHED_APP_MIN_ADJ;
}
@GuardedBy("mService")
@@ -1041,6 +1010,7 @@ final class ProcessStateRecord {
mCachedForegroundActivities = false;
mCachedProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
mCachedSchedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
+ mCachedAdjType = null;
}
@GuardedBy("mService")
@@ -1152,6 +1122,7 @@ final class ProcessStateRecord {
mCachedHasVisibleActivities = callback.mHasVisibleActivities ? VALUE_TRUE : VALUE_FALSE;
mCachedProcState = callback.procState;
mCachedSchedGroup = callback.schedGroup;
+ mCachedAdjType = callback.mAdjType;
if (mCachedAdj == ProcessList.VISIBLE_APP_ADJ) {
mCachedAdj += minLayer;
@@ -1179,6 +1150,11 @@ final class ProcessStateRecord {
}
@GuardedBy("mService")
+ String getCachedAdjType() {
+ return mCachedAdjType;
+ }
+
+ @GuardedBy("mService")
boolean shouldScheduleLikeTopApp() {
return mScheduleLikeTopApp;
}
@@ -1381,8 +1357,8 @@ final class ProcessStateRecord {
pw.print(prefix); pw.print("hasShownUi="); pw.print(mHasShownUi);
pw.print(" pendingUiClean="); pw.println(mApp.mProfile.hasPendingUiClean());
}
- pw.print(prefix); pw.print("cached="); pw.print(mCached);
- pw.print(" empty="); pw.println(mEmpty);
+ pw.print(prefix); pw.print("cached="); pw.print(isCached());
+ pw.print(" empty="); pw.println(isEmpty());
if (mServiceB) {
pw.print(prefix); pw.print("serviceb="); pw.print(mServiceB);
pw.print(" serviceHighRam="); pw.println(mServiceHighRam);
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index df5a82485bfc..966fe5bbeecc 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -120,6 +120,7 @@ public class SettingsToPropertiesMapper {
static final String[] sDeviceConfigAconfigScopes = new String[] {
"accessibility",
"android_core_networking",
+ "android_stylus",
"aoc",
"app_widgets",
"arc_next",
@@ -161,6 +162,7 @@ public class SettingsToPropertiesMapper {
"media_solutions",
"nfc",
"pdf_viewer",
+ "perfetto",
"pixel_audio_android",
"pixel_biometrics_face",
"pixel_bluetooth",
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 55ac4cf37283..34ba7f0debb0 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -97,6 +97,7 @@ import android.os.IProgressListener;
import android.os.IRemoteCallback;
import android.os.IUserManager;
import android.os.Message;
+import android.os.PowerManagerInternal;
import android.os.PowerWhitelistManager;
import android.os.Process;
import android.os.RemoteCallbackList;
@@ -1934,9 +1935,12 @@ class UserController implements Handler.Callback {
}
/**
- * Start user, if its not already running, and bring it to foreground.
+ * Start user, if it's not already running, and bring it to foreground.
*/
void startUserInForeground(@UserIdInt int targetUserId) {
+ if (android.multiuser.Flags.setPowerModeDuringUserSwitch()) {
+ mInjector.setPerformancePowerMode(true);
+ }
boolean success = startUser(targetUserId, USER_START_MODE_FOREGROUND);
if (!success) {
mInjector.getWindowManager().setSwitchingUser(false);
@@ -2146,6 +2150,9 @@ class UserController implements Handler.Callback {
}
private void endUserSwitch() {
+ if (android.multiuser.Flags.setPowerModeDuringUserSwitch()) {
+ mInjector.setPerformancePowerMode(false);
+ }
final int nextUserId;
synchronized (mLock) {
nextUserId = ObjectUtils.getOrElse(mPendingTargetUserIds.poll(), UserHandle.USER_NULL);
@@ -3535,6 +3542,7 @@ class UserController implements Handler.Callback {
private final ActivityManagerService mService;
private UserManagerService mUserManager;
private UserManagerInternal mUserManagerInternal;
+ private PowerManagerInternal mPowerManagerInternal;
private Handler mHandler;
private final Object mUserSwitchingDialogLock = new Object();
@GuardedBy("mUserSwitchingDialogLock")
@@ -3636,6 +3644,13 @@ class UserController implements Handler.Callback {
return mUserManagerInternal;
}
+ PowerManagerInternal getPowerManagerInternal() {
+ if (mPowerManagerInternal == null) {
+ mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
+ }
+ return mPowerManagerInternal;
+ }
+
KeyguardManager getKeyguardManager() {
return mService.mContext.getSystemService(KeyguardManager.class);
}
@@ -3829,6 +3844,12 @@ class UserController implements Handler.Callback {
getSystemServiceManager().onUserStarting(TimingsTraceAndSlog.newAsyncLog(), userId);
}
+ void setPerformancePowerMode(boolean enabled) {
+ Slogf.i(TAG, "Setting power mode MODE_FIXED_PERFORMANCE to " + enabled);
+ getPowerManagerInternal().setPowerMode(
+ PowerManagerInternal.MODE_FIXED_PERFORMANCE, enabled);
+ }
+
void onSystemUserVisibilityChanged(boolean visible) {
getUserManagerInternal().onSystemUserVisibilityChanged(visible);
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 5c95d433a6c1..0cc64941cfe6 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -106,6 +106,7 @@ import android.content.pm.PackageManagerInternal;
import android.content.pm.PermissionInfo;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
+import android.hardware.SensorPrivacyManager;
import android.hardware.camera2.CameraDevice.CAMERA_AUDIO_RESTRICTION;
import android.net.Uri;
import android.os.AsyncTask;
@@ -151,6 +152,7 @@ import com.android.internal.app.IAppOpsNotedCallback;
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IAppOpsStartedCallback;
import com.android.internal.app.MessageSamplingConfig;
+import com.android.internal.camera.flags.Flags;
import com.android.internal.compat.IPlatformCompat;
import com.android.internal.os.Clock;
import com.android.internal.pm.pkg.component.ParsedAttribution;
@@ -164,7 +166,6 @@ import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.LocalManagerRegistry;
import com.android.server.LocalServices;
import com.android.server.LockGuard;
-import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemServiceManager;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.pm.PackageList;
@@ -223,6 +224,8 @@ public class AppOpsService extends IAppOpsService.Stub {
*/
private static final int CURRENT_VERSION = 1;
+ private SensorPrivacyManager mSensorPrivacyManager;
+
// Write at most every 30 minutes.
static final long WRITE_DELAY = DEBUG ? 1000 : 30*60*1000;
@@ -655,11 +658,11 @@ public class AppOpsService extends IAppOpsService.Stub {
return attributedOp;
}
- @NonNull OpEntry createEntryLocked() {
+ @NonNull OpEntry createEntryLocked(String persistentDeviceId) {
// TODO(b/308201969): Update this method when we introduce disk persistence of events
// for accesses on external devices.
final ArrayMap<String, AttributedOp> attributedOps = mDeviceAttributedOps.get(
- PERSISTENT_DEVICE_ID_DEFAULT);
+ persistentDeviceId);
final ArrayMap<String, AppOpsManager.AttributedOpEntry> attributionEntries =
new ArrayMap<>(attributedOps.size());
for (int i = 0; i < attributedOps.size(); i++) {
@@ -1034,7 +1037,7 @@ public class AppOpsService extends IAppOpsService.Stub {
new Ops(pkgName, uidState));
}
- createSandboxUidStateIfNotExistsForAppLocked(uid);
+ createSandboxUidStateIfNotExistsForAppLocked(uid, null);
}
} else if (action.equals(ACTION_PACKAGE_REMOVED) && !intent.hasExtra(EXTRA_REPLACING)) {
synchronized (AppOpsService.this) {
@@ -1046,69 +1049,8 @@ public class AppOpsService extends IAppOpsService.Stub {
return;
}
- ArrayMap<String, String> dstAttributionTags = new ArrayMap<>();
- ArraySet<String> attributionTags = new ArraySet<>();
- attributionTags.add(null);
- if (pkg.getAttributions() != null) {
- int numAttributions = pkg.getAttributions().size();
- for (int attributionNum = 0; attributionNum < numAttributions;
- attributionNum++) {
- ParsedAttribution attribution = pkg.getAttributions().get(attributionNum);
- attributionTags.add(attribution.getTag());
-
- int numInheritFrom = attribution.getInheritFrom().size();
- for (int inheritFromNum = 0; inheritFromNum < numInheritFrom;
- inheritFromNum++) {
- dstAttributionTags.put(attribution.getInheritFrom().get(inheritFromNum),
- attribution.getTag());
- }
- }
- }
-
synchronized (AppOpsService.this) {
- UidState uidState = mUidStates.get(uid);
- if (uidState == null) {
- return;
- }
-
- Ops ops = uidState.pkgOps.get(pkgName);
- if (ops == null) {
- return;
- }
-
- // Reset cached package properties to re-initialize when needed
- ops.bypass = null;
- ops.knownAttributionTags.clear();
-
- // Merge data collected for removed attributions into their successor
- // attributions
- int numOps = ops.size();
- for (int opNum = 0; opNum < numOps; opNum++) {
- Op op = ops.valueAt(opNum);
- for (int deviceIndex = op.mDeviceAttributedOps.size() - 1; deviceIndex >= 0;
- deviceIndex--) {
- ArrayMap<String, AttributedOp> attributedOps =
- op.mDeviceAttributedOps.valueAt(deviceIndex);
- for (int tagIndex = attributedOps.size() - 1; tagIndex >= 0;
- tagIndex--) {
- String tag = attributedOps.keyAt(tagIndex);
- if (attributionTags.contains(tag)) {
- // attribution still exist after upgrade
- continue;
- }
-
- String newAttributionTag = dstAttributionTags.get(tag);
-
- AttributedOp newAttributedOp = op.getOrCreateAttribution(op,
- newAttributionTag,
- op.mDeviceAttributedOps.keyAt(deviceIndex));
- newAttributedOp.add(attributedOps.get(tag));
- attributedOps.remove(tag);
-
- scheduleFastWriteLocked();
- }
- }
- }
+ refreshAttributionsLocked(pkg, uid);
}
}
}
@@ -1132,41 +1074,6 @@ public class AppOpsService extends IAppOpsService.Stub {
mContext.registerReceiverAsUser(mOnPackageUpdatedReceiver, UserHandle.ALL,
packageUpdateFilter, null, null);
- synchronized (this) {
- for (int uidNum = mUidStates.size() - 1; uidNum >= 0; uidNum--) {
- int uid = mUidStates.keyAt(uidNum);
- UidState uidState = mUidStates.valueAt(uidNum);
-
- String[] pkgsInUid = getPackagesForUid(uidState.uid);
- if (ArrayUtils.isEmpty(pkgsInUid) && uid >= Process.FIRST_APPLICATION_UID) {
- uidState.clear();
- mUidStates.removeAt(uidNum);
- scheduleFastWriteLocked();
- continue;
- }
-
- ArrayMap<String, Ops> pkgs = uidState.pkgOps;
-
- int numPkgs = pkgs.size();
- for (int pkgNum = 0; pkgNum < numPkgs; pkgNum++) {
- String pkg = pkgs.keyAt(pkgNum);
-
- String action;
- if (!ArrayUtils.contains(pkgsInUid, pkg)) {
- action = ACTION_PACKAGE_REMOVED;
- } else {
- action = Intent.ACTION_PACKAGE_REPLACED;
- }
-
- SystemServerInitThreadPool.submit(
- () -> mOnPackageUpdatedReceiver.onReceive(mContext, new Intent(action)
- .setData(Uri.fromParts("package", pkg, null))
- .putExtra(Intent.EXTRA_UID, uid)),
- "Update app-ops uidState in case package " + pkg + " changed");
- }
- }
- }
-
prepareInternalCallbacks();
final IntentFilter packageSuspendFilter = new IntentFilter();
@@ -1231,6 +1138,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
});
+ mSensorPrivacyManager = SensorPrivacyManager.getInstance(mContext);
}
@VisibleForTesting
@@ -1253,20 +1161,27 @@ public class AppOpsService extends IAppOpsService.Stub {
void initializeUidStates() {
UserManagerInternal umi = getUserManagerInternal();
synchronized (this) {
+ SparseBooleanArray knownUids = new SparseBooleanArray();
+
+ for (int uid : NON_PACKAGE_UIDS) {
+ if (!mUidStates.contains(uid)) {
+ mUidStates.put(uid, new UidState(uid));
+ }
+ knownUids.put(uid, true);
+ }
+
int[] userIds = umi.getUserIds();
try (PackageManagerLocal.UnfilteredSnapshot snapshot =
getPackageManagerLocal().withUnfilteredSnapshot()) {
Map<String, PackageState> packageStates = snapshot.getPackageStates();
for (int i = 0; i < userIds.length; i++) {
int userId = userIds[i];
- initializeUserUidStatesLocked(userId, packageStates);
+ initializeUserUidStatesLocked(userId, packageStates, knownUids);
}
- }
- for (int uid : NON_PACKAGE_UIDS) {
- mUidStates.put(uid, new UidState(uid));
+ trimUidStatesLocked(knownUids, packageStates);
+ mUidStatesInitialized = true;
}
- mUidStatesInitialized = true;
}
}
@@ -1274,26 +1189,34 @@ public class AppOpsService extends IAppOpsService.Stub {
synchronized (this) {
try (PackageManagerLocal.UnfilteredSnapshot snapshot =
getPackageManagerLocal().withUnfilteredSnapshot()) {
- initializeUserUidStatesLocked(userId, snapshot.getPackageStates());
+ initializeUserUidStatesLocked(userId, snapshot.getPackageStates(), null);
}
}
}
private void initializeUserUidStatesLocked(int userId, Map<String,
- PackageState> packageStates) {
+ PackageState> packageStates, SparseBooleanArray knownUids) {
for (Map.Entry<String, PackageState> entry : packageStates.entrySet()) {
- int appId = entry.getValue().getAppId();
+ PackageState packageState = entry.getValue();
+ if (packageState.isApex()) {
+ continue;
+ }
+ int appId = packageState.getAppId();
String packageName = entry.getKey();
- initializePackageUidStateLocked(userId, appId, packageName);
+ initializePackageUidStateLocked(userId, appId, packageName, knownUids);
}
}
/*
Be careful not to clear any existing data; only want to add objects that don't already exist.
*/
- private void initializePackageUidStateLocked(int userId, int appId, String packageName) {
+ private void initializePackageUidStateLocked(int userId, int appId, String packageName,
+ SparseBooleanArray knownUids) {
int uid = UserHandle.getUid(userId, appId);
+ if (knownUids != null) {
+ knownUids.put(uid, true);
+ }
UidState uidState = getUidStateLocked(uid, true);
Ops ops = uidState.pkgOps.get(packageName);
if (ops == null) {
@@ -1311,7 +1234,105 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
- createSandboxUidStateIfNotExistsForAppLocked(uid);
+ createSandboxUidStateIfNotExistsForAppLocked(uid, knownUids);
+ }
+
+ private void trimUidStatesLocked(SparseBooleanArray knownUids,
+ Map<String, PackageState> packageStates) {
+ synchronized (this) {
+ // Remove what may have been added during persistence parsing
+ for (int uidIdx = mUidStates.size() - 1; uidIdx >= 0; uidIdx--) {
+ int uid = mUidStates.keyAt(uidIdx);
+ if (knownUids.get(uid, false)) {
+ if (uid >= Process.FIRST_APPLICATION_UID) {
+ ArrayMap<String, Ops> pkgOps = mUidStates.valueAt(uidIdx).pkgOps;
+ for (int pkgIdx = pkgOps.size() - 1; pkgIdx >= 0; pkgIdx--) {
+ String pkgName = pkgOps.keyAt(pkgIdx);
+ if (!packageStates.containsKey(pkgName)) {
+ pkgOps.removeAt(pkgIdx);
+ continue;
+ }
+ AndroidPackage pkg = packageStates.get(pkgName).getAndroidPackage();
+ if (pkg != null) {
+ refreshAttributionsLocked(pkg, uid);
+ }
+ }
+ if (pkgOps.isEmpty()) {
+ mUidStates.removeAt(uidIdx);
+ }
+ }
+ } else {
+ mUidStates.removeAt(uidIdx);
+ }
+ }
+ }
+ }
+
+ @GuardedBy("this")
+ private void refreshAttributionsLocked(AndroidPackage pkg, int uid) {
+ String pkgName = pkg.getPackageName();
+ ArrayMap<String, String> dstAttributionTags = new ArrayMap<>();
+ ArraySet<String> attributionTags = new ArraySet<>();
+ attributionTags.add(null);
+ if (pkg.getAttributions() != null) {
+ int numAttributions = pkg.getAttributions().size();
+ for (int attributionNum = 0; attributionNum < numAttributions;
+ attributionNum++) {
+ ParsedAttribution attribution = pkg.getAttributions().get(attributionNum);
+ attributionTags.add(attribution.getTag());
+
+ int numInheritFrom = attribution.getInheritFrom().size();
+ for (int inheritFromNum = 0; inheritFromNum < numInheritFrom;
+ inheritFromNum++) {
+ dstAttributionTags.put(attribution.getInheritFrom().get(inheritFromNum),
+ attribution.getTag());
+ }
+ }
+ }
+
+ UidState uidState = mUidStates.get(uid);
+ if (uidState == null) {
+ return;
+ }
+
+ Ops ops = uidState.pkgOps.get(pkgName);
+ if (ops == null) {
+ return;
+ }
+
+ // Reset cached package properties to re-initialize when needed
+ ops.bypass = null;
+ ops.knownAttributionTags.clear();
+
+ // Merge data collected for removed attributions into their successor
+ // attributions
+ int numOps = ops.size();
+ for (int opNum = 0; opNum < numOps; opNum++) {
+ Op op = ops.valueAt(opNum);
+ for (int deviceIndex = op.mDeviceAttributedOps.size() - 1; deviceIndex >= 0;
+ deviceIndex--) {
+ ArrayMap<String, AttributedOp> attributedOps =
+ op.mDeviceAttributedOps.valueAt(deviceIndex);
+ for (int tagIndex = attributedOps.size() - 1; tagIndex >= 0;
+ tagIndex--) {
+ String tag = attributedOps.keyAt(tagIndex);
+ if (attributionTags.contains(tag)) {
+ // attribution still exist after upgrade
+ continue;
+ }
+
+ String newAttributionTag = dstAttributionTags.get(tag);
+
+ AttributedOp newAttributedOp = op.getOrCreateAttribution(op,
+ newAttributionTag,
+ op.mDeviceAttributedOps.keyAt(deviceIndex));
+ newAttributedOp.add(attributedOps.get(tag));
+ attributedOps.remove(tag);
+
+ scheduleFastWriteLocked();
+ }
+ }
+ }
}
/**
@@ -1529,13 +1550,14 @@ public class AppOpsService extends IAppOpsService.Stub {
mHistoricalRegistry.shutdown();
}
- private ArrayList<AppOpsManager.OpEntry> collectOps(Ops pkgOps, int[] ops) {
+ private ArrayList<AppOpsManager.OpEntry> collectOps(Ops pkgOps, int[] ops,
+ String persistentDeviceId) {
ArrayList<AppOpsManager.OpEntry> resOps = null;
if (ops == null) {
resOps = new ArrayList<>();
for (int j=0; j<pkgOps.size(); j++) {
Op curOp = pkgOps.valueAt(j);
- resOps.add(getOpEntryForResult(curOp));
+ resOps.add(getOpEntryForResult(curOp, persistentDeviceId));
}
} else {
for (int j=0; j<ops.length; j++) {
@@ -1544,7 +1566,7 @@ public class AppOpsService extends IAppOpsService.Stub {
if (resOps == null) {
resOps = new ArrayList<>();
}
- resOps.add(getOpEntryForResult(curOp));
+ resOps.add(getOpEntryForResult(curOp, persistentDeviceId));
}
}
}
@@ -1588,16 +1610,23 @@ public class AppOpsService extends IAppOpsService.Stub {
return resOps;
}
- private static @NonNull OpEntry getOpEntryForResult(@NonNull Op op) {
- return op.createEntryLocked();
+ private static @NonNull OpEntry getOpEntryForResult(@NonNull Op op, String persistentDeviceId) {
+ return op.createEntryLocked(persistentDeviceId);
}
@Override
public List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops) {
+ return getPackagesForOpsForDevice(ops, PERSISTENT_DEVICE_ID_DEFAULT);
+ }
+
+ @Override
+ public List<AppOpsManager.PackageOps> getPackagesForOpsForDevice(int[] ops,
+ @NonNull String persistentDeviceId) {
final int callingUid = Binder.getCallingUid();
final boolean hasAllPackageAccess = mContext.checkPermission(
Manifest.permission.GET_APP_OPS_STATS, Binder.getCallingPid(),
Binder.getCallingUid(), null) == PackageManager.PERMISSION_GRANTED;
+
ArrayList<AppOpsManager.PackageOps> res = null;
synchronized (this) {
final int uidStateCount = mUidStates.size();
@@ -1606,21 +1635,24 @@ public class AppOpsService extends IAppOpsService.Stub {
if (uidState.pkgOps.isEmpty()) {
continue;
}
+ // Caller can always see their packages and with a permission all.
+ if (!hasAllPackageAccess && callingUid != uidState.uid) {
+ continue;
+ }
+
ArrayMap<String, Ops> packages = uidState.pkgOps;
final int packageCount = packages.size();
for (int j = 0; j < packageCount; j++) {
Ops pkgOps = packages.valueAt(j);
- ArrayList<AppOpsManager.OpEntry> resOps = collectOps(pkgOps, ops);
+ ArrayList<AppOpsManager.OpEntry> resOps = collectOps(pkgOps, ops,
+ persistentDeviceId);
if (resOps != null) {
if (res == null) {
res = new ArrayList<>();
}
AppOpsManager.PackageOps resPackage = new AppOpsManager.PackageOps(
pkgOps.packageName, pkgOps.uidState.uid, resOps);
- // Caller can always see their packages and with a permission all.
- if (hasAllPackageAccess || callingUid == pkgOps.uidState.uid) {
- res.add(resPackage);
- }
+ res.add(resPackage);
}
}
}
@@ -1642,7 +1674,8 @@ public class AppOpsService extends IAppOpsService.Stub {
if (pkgOps == null) {
return null;
}
- ArrayList<AppOpsManager.OpEntry> resOps = collectOps(pkgOps, ops);
+ ArrayList<AppOpsManager.OpEntry> resOps = collectOps(pkgOps, ops,
+ PERSISTENT_DEVICE_ID_DEFAULT);
if (resOps == null || resOps.size() == 0) {
return null;
}
@@ -4246,8 +4279,15 @@ public class AppOpsService extends IAppOpsService.Stub {
return uidState;
}
- private void createSandboxUidStateIfNotExistsForAppLocked(int uid) {
+ private void createSandboxUidStateIfNotExistsForAppLocked(int uid,
+ SparseBooleanArray knownUids) {
+ if (UserHandle.getAppId(uid) < Process.FIRST_APPLICATION_UID) {
+ return;
+ }
final int sandboxUid = Process.toSdkSandboxUid(uid);
+ if (knownUids != null) {
+ knownUids.put(sandboxUid, true);
+ }
getUidStateLocked(sandboxUid, true);
}
@@ -4642,6 +4682,10 @@ public class AppOpsService extends IAppOpsService.Stub {
return pmi.isPackageSuspended(packageName, UserHandle.getUserId(uid));
}
+ private boolean isAutomotive() {
+ return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+ }
+
private boolean isOpRestrictedLocked(int uid, int code, String packageName,
String attributionTag, int virtualDeviceId, @Nullable RestrictionBypass appBypass,
boolean isCheckOp) {
@@ -4658,6 +4702,13 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
+ if ((code == OP_CAMERA) && isAutomotive()) {
+ if ((Flags.cameraPrivacyAllowlist())
+ && (mSensorPrivacyManager.isCameraPrivacyEnabled(packageName))) {
+ return true;
+ }
+ }
+
int userHandle = UserHandle.getUserId(uid);
restrictionSetCount = mOpUserRestrictions.size();
diff --git a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
index 23a384ff5d3b..bc6ef2005584 100644
--- a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
+++ b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
@@ -16,6 +16,7 @@
package com.android.server.appop;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
@@ -30,6 +31,7 @@ import static android.app.AppOpsManager.OP_CAMERA;
import static android.app.AppOpsManager.OP_NONE;
import static android.app.AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO;
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
+import static android.app.AppOpsManager.OP_TAKE_AUDIO_FOCUS;
import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE;
import static android.app.AppOpsManager.UID_STATE_MAX_LAST_NON_RESTRICTED;
import static android.app.AppOpsManager.UID_STATE_TOP;
@@ -139,7 +141,6 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {
}
private int evalModeInternal(int uid, int code, int uidState, int uidCapability) {
-
if (getUidAppWidgetVisible(uid) || mActivityManagerInternal.isPendingTopUid(uid)
|| mActivityManagerInternal.isTempAllowlistedForFgsWhileInUse(uid)) {
return MODE_ALLOWED;
@@ -173,6 +174,8 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {
case OP_RECORD_AUDIO:
case OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO:
return PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
+ case OP_TAKE_AUDIO_FOCUS:
+ return PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL;
default:
return PROCESS_CAPABILITY_NONE;
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 102a960d35f6..e0c24256f5b1 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -167,8 +167,7 @@ public class AudioDeviceInventory {
ads = findBtDeviceStateForAddress(peerAddress, deviceType);
}
if (ads != null) {
- if (ads.getAudioDeviceCategory() != category
- && category != AUDIO_DEVICE_CATEGORY_UNKNOWN) {
+ if (ads.getAudioDeviceCategory() != category) {
ads.setAudioDeviceCategory(category);
mDeviceBroker.postUpdatedAdiDeviceState(ads);
mDeviceBroker.postPersistAudioDeviceSettings();
@@ -892,7 +891,7 @@ public class AudioDeviceInventory {
if (mDeviceBroker.hasScheduledA2dpConnection(btDevice)) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"A2dp config change ignored (scheduled connection change)")
- .printLog(TAG));
+ .printSlog(EventLogger.Event.ALOGI, TAG));
mmi.set(MediaMetrics.Property.EARLY_RETURN, "A2dp config change ignored")
.record();
return;
@@ -929,7 +928,7 @@ public class AudioDeviceInventory {
"APM handleDeviceConfigChange failed for A2DP device addr="
+ address + " codec="
+ AudioSystem.audioFormatToString(codec))
- .printLog(TAG));
+ .printSlog(EventLogger.Event.ALOGE, TAG));
// force A2DP device disconnection in case of error so that AudioService
// state is consistent with audio policy manager state
@@ -940,7 +939,7 @@ public class AudioDeviceInventory {
"APM handleDeviceConfigChange success for A2DP device addr="
+ address
+ " codec=" + AudioSystem.audioFormatToString(codec))
- .printLog(TAG));
+ .printSlog(EventLogger.Event.ALOGI, TAG));
}
}
}
@@ -1707,10 +1706,13 @@ public class AudioDeviceInventory {
if (res != AudioSystem.AUDIO_STATUS_OK) {
final String reason = "not connecting device 0x" + Integer.toHexString(device)
+ " due to command error " + res;
- Slog.e(TAG, reason);
mmi.set(MediaMetrics.Property.EARLY_RETURN, reason)
.set(MediaMetrics.Property.STATE, MediaMetrics.Value.DISCONNECTED)
.record();
+ AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+ "APM failed to make available device 0x" + Integer.toHexString(device)
+ + "addr=" + address + " error=" + res)
+ .printSlog(EventLogger.Event.ALOGE, TAG));
return false;
}
mConnectedDevices.put(deviceKey, new DeviceInfo(device, deviceName, address));
@@ -1736,7 +1738,8 @@ public class AudioDeviceInventory {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"SCO " + (AudioSystem.isInputDevice(device) ? "source" : "sink")
+ " device addr=" + address
- + (connect ? " now available" : " made unavailable")).printLog(TAG));
+ + (connect ? " now available" : " made unavailable"))
+ .printSlog(EventLogger.Event.ALOGI, TAG));
}
mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record();
} else {
@@ -1992,14 +1995,15 @@ public class AudioDeviceInventory {
// double connection is made.
if (res != AudioSystem.AUDIO_STATUS_OK) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
- "APM failed to make available A2DP device addr=" + address
- + " error=" + res).printLog(TAG));
+ "APM failed to make available A2DP device addr="
+ + Utils.anonymizeBluetoothAddress(address)
+ + " error=" + res).printSlog(EventLogger.Event.ALOGE, TAG));
// TODO: connection failed, stop here
// TODO: return;
} else {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"A2DP sink device addr=" + Utils.anonymizeBluetoothAddress(address)
- + " now available").printLog(TAG));
+ + " now available").printSlog(EventLogger.Event.ALOGI, TAG));
}
// Reset A2DP suspend state each time a new sink is connected
@@ -2240,7 +2244,8 @@ public class AudioDeviceInventory {
// removing A2DP device not currently used by AudioPolicy, log but don't act on it
AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
"A2DP device " + Utils.anonymizeBluetoothAddress(address)
- + " made unavailable, was not used")).printLog(TAG));
+ + " made unavailable, was not used"))
+ .printSlog(EventLogger.Event.ALOGI, TAG));
mmi.set(MediaMetrics.Property.EARLY_RETURN,
"A2DP device made unavailable, was not used")
.record();
@@ -2258,13 +2263,13 @@ public class AudioDeviceInventory {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"APM failed to make unavailable A2DP device addr="
+ Utils.anonymizeBluetoothAddress(address)
- + " error=" + res).printLog(TAG));
+ + " error=" + res).printSlog(EventLogger.Event.ALOGE, TAG));
// TODO: failed to disconnect, stop here
// TODO: return;
} else {
AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
"A2DP device addr=" + Utils.anonymizeBluetoothAddress(address)
- + " made unavailable")).printLog(TAG));
+ + " made unavailable")).printSlog(EventLogger.Event.ALOGI, TAG));
}
mApmConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
@@ -2297,10 +2302,22 @@ public class AudioDeviceInventory {
@GuardedBy("mDevicesLock")
private void makeA2dpSrcAvailable(String address) {
- mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
+ final int res = mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address),
AudioSystem.DEVICE_STATE_AVAILABLE,
AudioSystem.AUDIO_FORMAT_DEFAULT);
+ if (res != AudioSystem.AUDIO_STATUS_OK) {
+ AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+ "APM failed to make available A2DP source device addr="
+ + Utils.anonymizeBluetoothAddress(address)
+ + " error=" + res).printSlog(EventLogger.Event.ALOGE, TAG));
+ // TODO: connection failed, stop here
+ // TODO: return
+ } else {
+ AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+ "A2DP source device addr=" + Utils.anonymizeBluetoothAddress(address)
+ + " now available").printSlog(EventLogger.Event.ALOGI, TAG));
+ }
mConnectedDevices.put(
DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address),
new DeviceInfo(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, "", address));
@@ -2453,14 +2470,14 @@ public class AudioDeviceInventory {
if (res != AudioSystem.AUDIO_STATUS_OK) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"APM failed to make available LE Audio device addr=" + address
- + " error=" + res).printLog(TAG));
+ + " error=" + res).printSlog(EventLogger.Event.ALOGE, TAG));
// TODO: connection failed, stop here
// TODO: return;
} else {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"LE Audio " + (AudioSystem.isInputDevice(device) ? "source" : "sink")
+ " device addr=" + Utils.anonymizeBluetoothAddress(address)
- + " now available").printLog(TAG));
+ + " now available").printSlog(EventLogger.Event.ALOGI, TAG));
}
// Reset LEA suspend state each time a new sink is connected
mDeviceBroker.clearLeAudioSuspended(true /* internalOnly */);
@@ -2502,13 +2519,13 @@ public class AudioDeviceInventory {
if (res != AudioSystem.AUDIO_STATUS_OK) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"APM failed to make unavailable LE Audio device addr=" + address
- + " error=" + res).printLog(TAG));
+ + " error=" + res).printSlog(EventLogger.Event.ALOGE, TAG));
// TODO: failed to disconnect, stop here
// TODO: return;
} else {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"LE Audio device addr=" + Utils.anonymizeBluetoothAddress(address)
- + " made unavailable").printLog(TAG));
+ + " made unavailable").printSlog(EventLogger.Event.ALOGI, TAG));
}
mConnectedDevices.remove(DeviceInfo.makeDeviceListKey(device, address));
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index c59f4f7888ce..cb6d26f61314 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -34,6 +34,7 @@ import static android.media.audio.Flags.autoPublicVolumeApiHardening;
import static android.media.audio.Flags.automaticBtDeviceType;
import static android.media.audio.Flags.featureSpatialAudioHeadtrackingLowLatency;
import static android.media.audio.Flags.focusFreezeTestApi;
+import static android.media.audio.Flags.foregroundAudioControl;
import static android.media.audiopolicy.Flags.enableFadeManagerConfiguration;
import static android.os.Process.FIRST_APPLICATION_UID;
import static android.os.Process.INVALID_UID;
@@ -963,6 +964,8 @@ public class AudioService extends IAudioService.Stub
private final HardeningEnforcer mHardeningEnforcer;
+ private final AudioVolumeGroupHelperBase mAudioVolumeGroupHelper;
+
private final Object mSupportedSystemUsagesLock = new Object();
@GuardedBy("mSupportedSystemUsagesLock")
private @AttributeSystemUsage int[] mSupportedSystemUsages =
@@ -973,6 +976,13 @@ public class AudioService extends IAudioService.Stub
return "card=" + card + ";device=" + device;
}
+ private static class AudioVolumeGroupHelper extends AudioVolumeGroupHelperBase {
+ @Override
+ public List<AudioVolumeGroup> getAudioVolumeGroups() {
+ return AudioVolumeGroup.getAudioVolumeGroups();
+ }
+ }
+
public static final class Lifecycle extends SystemService {
private AudioService mService;
@@ -982,6 +992,7 @@ public class AudioService extends IAudioService.Stub
AudioSystemAdapter.getDefaultAdapter(),
SystemServerAdapter.getDefaultAdapter(context),
SettingsAdapter.getDefaultAdapter(),
+ new AudioVolumeGroupHelper(),
new DefaultAudioPolicyFacade(),
null);
@@ -1061,16 +1072,19 @@ public class AudioService extends IAudioService.Stub
/**
* @param context
* @param audioSystem Adapter for {@link AudioSystem}
- * @param systemServer Adapter for privilieged functionality for system server components
+ * @param systemServer Adapter for privileged functionality for system server components
* @param settings Adapter for {@link Settings}
+ * @param audioVolumeGroupHelper Adapter for {@link AudioVolumeGroup}
+ * @param audioPolicy Interface of a facade to IAudioPolicyManager
* @param looper Looper to use for the service's message handler. If this is null, an
* {@link AudioSystemThread} is created as the messaging thread instead.
*/
public AudioService(Context context, AudioSystemAdapter audioSystem,
SystemServerAdapter systemServer, SettingsAdapter settings,
- AudioPolicyFacade audioPolicy, @Nullable Looper looper) {
- this (context, audioSystem, systemServer, settings, audioPolicy, looper,
- context.getSystemService(AppOpsManager.class),
+ AudioVolumeGroupHelperBase audioVolumeGroupHelper, AudioPolicyFacade audioPolicy,
+ @Nullable Looper looper) {
+ this (context, audioSystem, systemServer, settings, audioVolumeGroupHelper,
+ audioPolicy, looper, context.getSystemService(AppOpsManager.class),
PermissionEnforcer.fromContext(context));
}
@@ -1079,14 +1093,18 @@ public class AudioService extends IAudioService.Stub
* @param audioSystem Adapter for {@link AudioSystem}
* @param systemServer Adapter for privilieged functionality for system server components
* @param settings Adapter for {@link Settings}
+ * @param audioVolumeGroupHelper Adapter for {@link AudioVolumeGroup}
+ * @param audioPolicy Interface of a facade to IAudioPolicyManager
* @param looper Looper to use for the service's message handler. If this is null, an
* {@link AudioSystemThread} is created as the messaging thread instead.
+ * @param appOps {@link AppOpsManager} system service
+ * @param enforcer Used for permission enforcing
*/
@RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
public AudioService(Context context, AudioSystemAdapter audioSystem,
SystemServerAdapter systemServer, SettingsAdapter settings,
- AudioPolicyFacade audioPolicy, @Nullable Looper looper, AppOpsManager appOps,
- @NonNull PermissionEnforcer enforcer) {
+ AudioVolumeGroupHelperBase audioVolumeGroupHelper, AudioPolicyFacade audioPolicy,
+ @Nullable Looper looper, AppOpsManager appOps, @NonNull PermissionEnforcer enforcer) {
super(enforcer);
sLifecycleLogger.enqueue(new EventLogger.StringEvent("AudioService()"));
mContext = context;
@@ -1095,6 +1113,7 @@ public class AudioService extends IAudioService.Stub
mAudioSystem = audioSystem;
mSystemServer = systemServer;
+ mAudioVolumeGroupHelper = audioVolumeGroupHelper;
mSettings = settings;
mAudioPolicy = audioPolicy;
mPlatformType = AudioSystem.getPlatformType(context);
@@ -1356,7 +1375,8 @@ public class AudioService extends IAudioService.Stub
mMusicFxHelper = new MusicFxHelper(mContext, mAudioHandler);
- mHardeningEnforcer = new HardeningEnforcer(mContext, isPlatformAutomotive());
+ mHardeningEnforcer = new HardeningEnforcer(mContext, isPlatformAutomotive(), mAppOps,
+ context.getPackageManager());
}
private void initVolumeStreamStates() {
@@ -2102,7 +2122,7 @@ public class AudioService extends IAudioService.Stub
// verify permissions
super.getAudioVolumeGroups_enforcePermission();
- return AudioVolumeGroup.getAudioVolumeGroups();
+ return mAudioVolumeGroupHelper.getAudioVolumeGroups();
}
private void checkAllAliasStreamVolumes() {
@@ -3801,7 +3821,7 @@ public class AudioService extends IAudioService.Stub
}
/**
- * Loops on aliasted stream, update the mute cache attribute of each
+ * Loops on aliased stream, update the mute cache attribute of each
* {@see AudioService#VolumeStreamState}, and then apply the change.
* It prevents to unnecessary {@see AudioSystem#setStreamVolume} done for each stream
* and aliases before mute change changed and after.
@@ -4038,18 +4058,6 @@ public class AudioService extends IAudioService.Stub
}
}
- @Nullable
- private AudioVolumeGroup getAudioVolumeGroupById(int volumeGroupId) {
- for (AudioVolumeGroup avg : AudioVolumeGroup.getAudioVolumeGroups()) {
- if (avg.getId() == volumeGroupId) {
- return avg;
- }
- }
-
- Log.e(TAG, ": invalid volume group id: " + volumeGroupId + " requested");
- return null;
- }
-
@Override
@android.annotation.EnforcePermission(anyOf = {
MODIFY_AUDIO_SETTINGS_PRIVILEGED,
@@ -4517,7 +4525,8 @@ public class AudioService extends IAudioService.Stub
}
private void dumpFlags(PrintWriter pw) {
- pw.println("\nFun with Flags: ");
+
+ pw.println("\nFun with Flags:");
pw.println("\tandroid.media.audio.autoPublicVolumeApiHardening:"
+ autoPublicVolumeApiHardening());
pw.println("\tandroid.media.audio.Flags.automaticBtDeviceType:"
@@ -4528,8 +4537,8 @@ public class AudioService extends IAudioService.Stub
+ focusFreezeTestApi());
pw.println("\tcom.android.media.audio.disablePrescaleAbsoluteVolume:"
+ disablePrescaleAbsoluteVolume());
- pw.println("\tandroid.media.audiopolicy.enableFadeManagerConfiguration:"
- + enableFadeManagerConfiguration());
+ pw.println("\tandroid.media.audio.foregroundAudioControl:"
+ + foregroundAudioControl());
}
private void dumpAudioMode(PrintWriter pw) {
@@ -5664,7 +5673,7 @@ public class AudioService extends IAudioService.Stub
final boolean isMuted = isStreamMutedByRingerOrZenMode(streamType);
final boolean muteAllowedBySco =
!(shouldRingSco && streamType == AudioSystem.STREAM_RING);
- final boolean shouldZenMute = shouldZenMuteStream(streamType);
+ final boolean shouldZenMute = isStreamAffectedByCurrentZen(streamType);
final boolean shouldMute = shouldZenMute || (ringerModeMute
&& isStreamAffectedByRingerMode(streamType) && muteAllowedBySco);
if (isMuted == shouldMute) continue;
@@ -6928,24 +6937,8 @@ public class AudioService extends IAudioService.Stub
return (mRingerModeAffectedStreams & (1 << streamType)) != 0;
}
- private boolean shouldZenMuteStream(int streamType) {
- if (mNm.getZenMode() != Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
- return false;
- }
-
- NotificationManager.Policy zenPolicy = mNm.getConsolidatedNotificationPolicy();
- final boolean muteAlarms = (zenPolicy.priorityCategories
- & NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS) == 0;
- final boolean muteMedia = (zenPolicy.priorityCategories
- & NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA) == 0;
- final boolean muteSystem = (zenPolicy.priorityCategories
- & NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM) == 0;
- final boolean muteNotificationAndRing = ZenModeConfig
- .areAllPriorityOnlyRingerSoundsMuted(zenPolicy);
- return muteAlarms && isAlarm(streamType)
- || muteMedia && isMedia(streamType)
- || muteSystem && isSystem(streamType)
- || muteNotificationAndRing && isNotificationOrRinger(streamType);
+ public boolean isStreamAffectedByCurrentZen(int streamType) {
+ return (mZenModeAffectedStreams & (1 << streamType)) != 0;
}
private boolean isStreamMutedByRingerOrZenMode(int streamType) {
@@ -6953,11 +6946,9 @@ public class AudioService extends IAudioService.Stub
}
/**
- * Notifications, ringer and system sounds are controlled by the ringer:
- * {@link ZenModeHelper.RingerModeDelegate#getRingerModeAffectedStreams(int)} but can
- * also be muted by DND based on the DND mode:
- * DND total silence: media and alarms streams can be muted by DND
- * DND alarms only: no streams additionally controlled by DND
+ * Volume streams can be muted based on the current DND state:
+ * DND total silence: ringer, notification, system, media and alarms streams muted by DND
+ * DND alarms only: ringer, notification, system streams muted by DND
* DND priority only: alarms, media, system, ringer and notification streams can be muted by
* DND. The current applied zenPolicy determines which streams will be muted by DND.
* @return true if changed, else false
@@ -6967,12 +6958,20 @@ public class AudioService extends IAudioService.Stub
return false;
}
+ // If DND is off, no streams are muted by DND
int zenModeAffectedStreams = 0;
final int zenMode = mNm.getZenMode();
if (zenMode == Settings.Global.ZEN_MODE_NO_INTERRUPTIONS) {
+ zenModeAffectedStreams |= 1 << AudioManager.STREAM_SYSTEM;
+ zenModeAffectedStreams |= 1 << AudioManager.STREAM_NOTIFICATION;
+ zenModeAffectedStreams |= 1 << AudioManager.STREAM_RING;
zenModeAffectedStreams |= 1 << AudioManager.STREAM_ALARM;
zenModeAffectedStreams |= 1 << AudioManager.STREAM_MUSIC;
+ } else if (zenMode == Settings.Global.ZEN_MODE_ALARMS) {
+ zenModeAffectedStreams |= 1 << AudioManager.STREAM_SYSTEM;
+ zenModeAffectedStreams |= 1 << AudioManager.STREAM_NOTIFICATION;
+ zenModeAffectedStreams |= 1 << AudioManager.STREAM_RING;
} else if (zenMode == Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
NotificationManager.Policy zenPolicy = mNm.getConsolidatedNotificationPolicy();
if ((zenPolicy.priorityCategories
@@ -7014,7 +7013,6 @@ public class AudioService extends IAudioService.Stub
((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
(1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)),
UserHandle.USER_CURRENT);
-
if (mIsSingleVolume) {
ringerModeAffectedStreams = 0;
} else if (mRingerModeDelegate != null) {
@@ -8249,7 +8247,7 @@ public class AudioService extends IAudioService.Stub
index = 1;
}
// Set the volume index
- AudioSystem.setVolumeIndexForAttributes(mAudioAttributes, index, device);
+ mAudioSystem.setVolumeIndexForAttributes(mAudioAttributes, index, device);
}
@GuardedBy("AudioService.VolumeStreamState.class")
@@ -10175,10 +10173,38 @@ public class AudioService extends IAudioService.Stub
.record();
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
+
+ // does caller have system privileges to bypass HardeningEnforcer
+ boolean permissionOverridesCheck = false;
+ if ((mContext.checkCallingOrSelfPermission(
+ Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ == PackageManager.PERMISSION_GRANTED)
+ || (mContext.checkCallingOrSelfPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
+ == PackageManager.PERMISSION_GRANTED)) {
+ permissionOverridesCheck = true;
+ } else if (uid < UserHandle.AID_APP_START) {
+ permissionOverridesCheck = true;
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (!permissionOverridesCheck && mHardeningEnforcer.blockFocusMethod(uid,
+ HardeningEnforcer.METHOD_AUDIO_MANAGER_REQUEST_AUDIO_FOCUS,
+ clientId, durationHint, callingPackageName)) {
+ final String reason = "Audio focus request blocked by hardening";
+ Log.w(TAG, reason);
+ mmi.set(MediaMetrics.Property.EARLY_RETURN, reason).record();
+ return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+
mmi.record();
return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,
clientId, callingPackageName, attributionTag, flags, sdk,
- forceFocusDuckingForAccessibility(aa, durationHint, uid), -1 /*testUid, ignored*/);
+ forceFocusDuckingForAccessibility(aa, durationHint, uid), -1 /*testUid, ignored*/,
+ permissionOverridesCheck);
}
/** see {@link AudioManager#requestAudioFocusForTest(AudioFocusRequest, String, int, int)} */
@@ -10195,7 +10221,7 @@ public class AudioService extends IAudioService.Stub
}
return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,
clientId, callingPackageName, null, flags,
- sdk, false /*forceDuck*/, fakeUid);
+ sdk, false /*forceDuck*/, fakeUid, true /*permissionOverridesCheck*/);
}
public int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId, AudioAttributes aa,
@@ -11639,6 +11665,7 @@ public class AudioService extends IAudioService.Stub
pw.println("\nMessage handler is null");
}
dumpFlags(pw);
+ mHardeningEnforcer.dump(pw);
mMediaFocusControl.dump(pw);
dumpStreamStates(pw);
dumpVolumeGroups(pw);
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index 49ab19a816dc..7202fa286453 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -551,6 +551,11 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback,
return AudioSystem.setStreamVolumeIndexAS(stream, index, device);
}
+ /** Same as {@link AudioSystem#setVolumeIndexForAttributes(AudioAttributes, int, int)} */
+ public int setVolumeIndexForAttributes(AudioAttributes attributes, int index, int device) {
+ return AudioSystem.setVolumeIndexForAttributes(attributes, index, device);
+ }
+
/**
* Same as {@link AudioSystem#setPhoneState(int, int)}
* @param state
diff --git a/services/core/java/com/android/server/audio/AudioVolumeGroupHelperBase.java b/services/core/java/com/android/server/audio/AudioVolumeGroupHelperBase.java
new file mode 100644
index 000000000000..6f4de5bc5f81
--- /dev/null
+++ b/services/core/java/com/android/server/audio/AudioVolumeGroupHelperBase.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.audio;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+
+import android.media.audiopolicy.AudioVolumeGroup;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Abstract class for {@link AudioVolumeGroup} related helper methods. */
+@VisibleForTesting(visibility = PACKAGE)
+public class AudioVolumeGroupHelperBase {
+ public List<AudioVolumeGroup> getAudioVolumeGroups() {
+ return new ArrayList<>();
+ }
+}
diff --git a/services/core/java/com/android/server/audio/HardeningEnforcer.java b/services/core/java/com/android/server/audio/HardeningEnforcer.java
index 4ceb83b2e1c9..409ed17001b7 100644
--- a/services/core/java/com/android/server/audio/HardeningEnforcer.java
+++ b/services/core/java/com/android/server/audio/HardeningEnforcer.java
@@ -18,13 +18,21 @@ package com.android.server.audio;
import static android.media.audio.Flags.autoPublicVolumeApiHardening;
import android.Manifest;
+import android.annotation.NonNull;
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.media.AudioFocusRequest;
import android.media.AudioManager;
import android.os.Binder;
import android.os.UserHandle;
import android.text.TextUtils;
-import android.util.Log;
+import android.util.Slog;
+
+import com.android.server.utils.EventLogger;
+
+import java.io.PrintWriter;
/**
* Class to encapsulate all audio API hardening operations
@@ -32,10 +40,19 @@ import android.util.Log;
public class HardeningEnforcer {
private static final String TAG = "AS.HardeningEnforcer";
+ private static final boolean DEBUG = false;
+ private static final int LOG_NB_EVENTS = 20;
final Context mContext;
+ final AppOpsManager mAppOps;
final boolean mIsAutomotive;
+ final ActivityManager mActivityManager;
+ final PackageManager mPackageManager;
+
+ final EventLogger mEventLogger = new EventLogger(LOG_NB_EVENTS,
+ "Hardening enforcement");
+
/**
* Matches calls from {@link AudioManager#setStreamVolume(int, int, int)}
*/
@@ -56,10 +73,24 @@ public class HardeningEnforcer {
* Matches calls from {@link AudioManager#setRingerMode(int)}
*/
public static final int METHOD_AUDIO_MANAGER_SET_RINGER_MODE = 200;
+ /**
+ * Matches calls from {@link AudioManager#requestAudioFocus(AudioFocusRequest)}
+ * and legacy variants
+ */
+ public static final int METHOD_AUDIO_MANAGER_REQUEST_AUDIO_FOCUS = 300;
- public HardeningEnforcer(Context ctxt, boolean isAutomotive) {
+ public HardeningEnforcer(Context ctxt, boolean isAutomotive, AppOpsManager appOps,
+ PackageManager pm) {
mContext = ctxt;
mIsAutomotive = isAutomotive;
+ mAppOps = appOps;
+ mActivityManager = ctxt.getSystemService(ActivityManager.class);
+ mPackageManager = pm;
+ }
+
+ protected void dump(PrintWriter pw) {
+ // log
+ mEventLogger.dump(pw);
}
/**
@@ -84,7 +115,7 @@ public class HardeningEnforcer {
}
// TODO metrics?
// TODO log for audio dumpsys?
- Log.e(TAG, "Preventing volume method " + volumeMethod + " for "
+ Slog.e(TAG, "Preventing volume method " + volumeMethod + " for "
+ getPackNameForUid(Binder.getCallingUid()));
return true;
}
@@ -92,10 +123,40 @@ public class HardeningEnforcer {
return false;
}
+ /**
+ * Checks whether the call in the current thread should be allowed or blocked
+ * @param focusMethod name of the method to check, for logging purposes
+ * @param clientId id of the requester
+ * @param durationHint focus type being requested
+ * @return false if the method call is allowed, true if it should be a no-op
+ */
+ protected boolean blockFocusMethod(int callingUid, int focusMethod, @NonNull String clientId,
+ int durationHint, @NonNull String packageName) {
+ if (packageName.isEmpty()) {
+ packageName = getPackNameForUid(callingUid);
+ }
+
+ if (checkAppOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, callingUid, packageName)) {
+ if (DEBUG) {
+ Slog.i(TAG, "blockFocusMethod pack:" + packageName + " NOT blocking");
+ }
+ return false;
+ }
+
+ String errorMssg = "Focus request DENIED for uid:" + callingUid
+ + " clientId:" + clientId + " req:" + durationHint
+ + " procState:" + mActivityManager.getUidProcessState(callingUid);
+
+ // TODO metrics
+ mEventLogger.enqueueAndSlog(errorMssg, EventLogger.Event.ALOGI, TAG);
+
+ return true;
+ }
+
private String getPackNameForUid(int uid) {
final long token = Binder.clearCallingIdentity();
try {
- final String[] names = mContext.getPackageManager().getPackagesForUid(uid);
+ final String[] names = mPackageManager.getPackagesForUid(uid);
if (names == null
|| names.length == 0
|| TextUtils.isEmpty(names[0])) {
@@ -106,4 +167,18 @@ public class HardeningEnforcer {
Binder.restoreCallingIdentity(token);
}
}
+
+ /**
+ * Checks the given op without throwing
+ * @param op the appOp code
+ * @param uid the calling uid
+ * @param packageName the package name of the caller
+ * @return return false if the operation is not allowed
+ */
+ private boolean checkAppOp(int op, int uid, @NonNull String packageName) {
+ if (mAppOps.checkOpNoThrow(op, uid, packageName) != AppOpsManager.MODE_ALLOWED) {
+ return false;
+ }
+ return true;
+ }
}
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index 1376bde2fb71..35d38e2373f5 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -1090,11 +1090,14 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
* accessibility.
* @param testUid ignored if flags doesn't contain AudioManager.AUDIOFOCUS_FLAG_TEST
* otherwise the UID being injected for testing
+ * @param permissionOverridesCheck true if permission checks guaranteed that the call should
+ * go through, false otherwise (e.g. non-privileged caller)
* @return
*/
protected int requestAudioFocus(@NonNull AudioAttributes aa, int focusChangeHint, IBinder cb,
IAudioFocusDispatcher fd, @NonNull String clientId, @NonNull String callingPackageName,
- String attributionTag, int flags, int sdk, boolean forceDuck, int testUid) {
+ String attributionTag, int flags, int sdk, boolean forceDuck, int testUid,
+ boolean permissionOverridesCheck) {
new MediaMetrics.Item(mMetricsId)
.setUid(Binder.getCallingUid())
.set(MediaMetrics.Property.CALLING_PACKAGE, callingPackageName)
@@ -1126,10 +1129,9 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
- if ((flags != AudioManager.AUDIOFOCUS_FLAG_TEST)
- // note we're using the real uid for appOp evaluation
- && (mAppOps.noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, Binder.getCallingUid(),
- callingPackageName, attributionTag, null) != AppOpsManager.MODE_ALLOWED)) {
+ final int res = mAppOps.noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, Binder.getCallingUid(),
+ callingPackageName, attributionTag, null);
+ if (!permissionOverridesCheck && res != AppOpsManager.MODE_ALLOWED) {
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 3f3540e2868c..48bf9f4967bc 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -216,6 +216,13 @@ public class AuthService extends SystemService {
public String[] getFaceAidlInstances() {
return ServiceManager.getDeclaredInstances(IFace.DESCRIPTOR);
}
+
+ /**
+ * Allows to test with handlers.
+ */
+ public BiometricHandlerProvider getBiometricHandlerProvider() {
+ return BiometricHandlerProvider.getInstance();
+ }
}
private final class AuthServiceImpl extends IAuthService.Stub {
@@ -772,7 +779,6 @@ public class AuthService extends SystemService {
}
if (com.android.server.biometrics.Flags.deHidl()) {
- Slog.d(TAG, "deHidl flag is on.");
registerAuthenticators();
} else {
// Registers HIDL and AIDL authenticators, but only HIDL configs need to be provided.
@@ -783,10 +789,16 @@ public class AuthService extends SystemService {
}
private void registerAuthenticators() {
- registerFingerprintSensors(mInjector.getFingerprintAidlInstances(),
- mInjector.getFingerprintConfiguration(getContext()));
- registerFaceSensors(mInjector.getFaceAidlInstances(),
- mInjector.getFaceConfiguration(getContext()));
+ BiometricHandlerProvider handlerProvider = mInjector.getBiometricHandlerProvider();
+
+ handlerProvider.getFingerprintHandler().post(() ->
+ registerFingerprintSensors(mInjector.getFingerprintAidlInstances(),
+ mInjector.getFingerprintConfiguration(getContext()), getContext(),
+ mInjector.getFingerprintService()));
+ handlerProvider.getFaceHandler().post(() ->
+ registerFaceSensors(mInjector.getFaceAidlInstances(),
+ mInjector.getFaceConfiguration(getContext()), getContext(),
+ mInjector.getFaceService()));
registerIrisSensors(mInjector.getIrisConfiguration(getContext()));
}
@@ -837,15 +849,18 @@ public class AuthService extends SystemService {
}
}
- private void registerFaceSensors(final String[] faceAidlInstances,
- final String[] hidlConfigStrings) {
+ /**
+ * This method is invoked on {@link BiometricHandlerProvider.mFaceHandler}.
+ */
+ private static void registerFaceSensors(final String[] faceAidlInstances,
+ final String[] hidlConfigStrings, final Context context,
+ final IFaceService faceService) {
final FaceSensorConfigurations mFaceSensorConfigurations =
new FaceSensorConfigurations(hidlConfigStrings != null
&& hidlConfigStrings.length > 0);
if (hidlConfigStrings != null && hidlConfigStrings.length > 0) {
- mFaceSensorConfigurations.addHidlConfigs(
- hidlConfigStrings, getContext());
+ mFaceSensorConfigurations.addHidlConfigs(hidlConfigStrings, context);
}
if (faceAidlInstances != null && faceAidlInstances.length > 0) {
@@ -854,7 +869,6 @@ public class AuthService extends SystemService {
ServiceManager.waitForDeclaredService(name))));
}
- final IFaceService faceService = mInjector.getFaceService();
if (faceService != null) {
try {
faceService.registerAuthenticatorsLegacy(mFaceSensorConfigurations);
@@ -866,14 +880,18 @@ public class AuthService extends SystemService {
}
}
- private void registerFingerprintSensors(final String[] fingerprintAidlInstances,
- final String[] hidlConfigStrings) {
+ /**
+ * This method is invoked on {@link BiometricHandlerProvider.mFingerprintHandler}.
+ */
+ private static void registerFingerprintSensors(final String[] fingerprintAidlInstances,
+ final String[] hidlConfigStrings, final Context context,
+ final IFingerprintService fingerprintService) {
final FingerprintSensorConfigurations mFingerprintSensorConfigurations =
new FingerprintSensorConfigurations(!(hidlConfigStrings != null
&& hidlConfigStrings.length > 0));
if (hidlConfigStrings != null && hidlConfigStrings.length > 0) {
- mFingerprintSensorConfigurations.addHidlSensors(hidlConfigStrings, getContext());
+ mFingerprintSensorConfigurations.addHidlSensors(hidlConfigStrings, context);
}
if (fingerprintAidlInstances != null && fingerprintAidlInstances.length > 0) {
@@ -882,7 +900,6 @@ public class AuthService extends SystemService {
ServiceManager.waitForDeclaredService(name))));
}
- final IFingerprintService fingerprintService = mInjector.getFingerprintService();
if (fingerprintService != null) {
try {
fingerprintService.registerAuthenticatorsLegacy(mFingerprintSensorConfigurations);
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
index 5d609bca334c..526264d67318 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
@@ -153,7 +153,6 @@ public class AuthenticationStatsCollector {
if (authenticationStats.getTotalAttempts() < MINIMUM_ATTEMPTS) {
return;
}
-
// Don't send notification if FRR below the threshold.
if (authenticationStats.getEnrollmentNotifications() >= MAXIMUM_ENROLLMENT_NOTIFICATIONS
|| authenticationStats.getFrr() < mThreshold) {
@@ -161,6 +160,7 @@ public class AuthenticationStatsCollector {
return;
}
+
authenticationStats.resetData();
final boolean hasEnrolledFace = hasEnrolledFace(userId);
@@ -186,6 +186,28 @@ public class AuthenticationStatsCollector {
}
}
+ /**
+ * This is meant for debug purposes only, this will bypass many checks.
+ * The origination of this call should be from an adb shell command sent from
+ * FaceService.
+ *
+ * adb shell cmd face notification
+ */
+ public void sendFaceReEnrollNotification() {
+ mBiometricNotification.sendFaceEnrollNotification(mContext);
+ }
+
+ /**
+ * This is meant for debug purposes only, this will bypass many checks.
+ * The origination of this call should be from an adb shell command sent from
+ * FingerprintService.
+ *
+ * adb shell cmd fingerprint notification
+ */
+ public void sendFingerprintReEnrollNotification() {
+ mBiometricNotification.sendFpEnrollNotification(mContext);
+ }
+
private void onUserRemoved(final int userId) {
mUserAuthenticationStatsMap.remove(userId);
mAuthenticationStatsPersister.removeFrrStats(userId);
diff --git a/services/core/java/com/android/server/biometrics/BiometricNotificationLogger.java b/services/core/java/com/android/server/biometrics/BiometricNotificationLogger.java
new file mode 100644
index 000000000000..51a2b1ac2dfc
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/BiometricNotificationLogger.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics;
+
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.biometrics.sensors.BiometricNotificationUtils;
+import com.android.server.biometrics.log.BiometricFrameworkStatsLogger;
+
+/**
+ * A class that logs metric info related to the posting/dismissal of Biometric FRR notifications.
+ */
+public class BiometricNotificationLogger extends NotificationListenerService {
+ private static final String TAG = "FRRNotificationListener";
+ private BiometricFrameworkStatsLogger mLogger;
+
+ BiometricNotificationLogger() {
+ this(BiometricFrameworkStatsLogger.getInstance());
+ }
+
+ @VisibleForTesting
+ BiometricNotificationLogger(BiometricFrameworkStatsLogger logger) {
+ mLogger = logger;
+ }
+
+ @Override
+ public void onNotificationPosted(StatusBarNotification sbn, RankingMap map) {
+ if (sbn == null || sbn.getTag() == null) {
+ return;
+ }
+ switch (sbn.getTag()) {
+ case BiometricNotificationUtils.FACE_ENROLL_NOTIFICATION_TAG:
+ case BiometricNotificationUtils.FINGERPRINT_ENROLL_NOTIFICATION_TAG:
+ final int modality =
+ sbn.getTag() == BiometricNotificationUtils.FACE_ENROLL_NOTIFICATION_TAG
+ ? BiometricsProtoEnums.MODALITY_FACE
+ : BiometricsProtoEnums.MODALITY_FINGERPRINT;
+ Slog.d(TAG, "onNotificationPosted, tag=(" + sbn.getTag() + ")");
+ mLogger.logFrameworkNotification(
+ BiometricsProtoEnums.FRR_NOTIFICATION_ACTION_SHOWN,
+ modality
+ );
+ break;
+ default:
+ break;
+ }
+ }
+
+ @Override
+ public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap,
+ int reason) {
+ if (sbn == null || sbn.getTag() == null) {
+ return;
+ }
+ switch (sbn.getTag()) {
+ case BiometricNotificationUtils.FACE_ENROLL_NOTIFICATION_TAG:
+ case BiometricNotificationUtils.FINGERPRINT_ENROLL_NOTIFICATION_TAG:
+ Slog.d(TAG, "onNotificationRemoved, tag=("
+ + sbn.getTag() + "), reason=(" + reason + ")");
+ final int modality =
+ sbn.getTag() == BiometricNotificationUtils.FACE_ENROLL_NOTIFICATION_TAG
+ ? BiometricsProtoEnums.MODALITY_FACE
+ : BiometricsProtoEnums.MODALITY_FINGERPRINT;
+ switch (reason) {
+ // REASON_CLICK = 1
+ case NotificationListenerService.REASON_CLICK:
+ mLogger.logFrameworkNotification(
+ BiometricsProtoEnums.FRR_NOTIFICATION_ACTION_CLICKED,
+ modality);
+ break;
+ // REASON_CANCEL = 2
+ case NotificationListenerService.REASON_CANCEL:
+ mLogger.logFrameworkNotification(
+ BiometricsProtoEnums.FRR_NOTIFICATION_ACTION_DISMISSED,
+ modality);
+ break;
+ default:
+ Slog.d(TAG, "unhandled reason, ignoring logging");
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index fc948da260e6..894b4d5d0b36 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -32,6 +32,7 @@ import android.app.UserSwitchObserver;
import android.app.admin.DevicePolicyManager;
import android.app.trust.ITrustManager;
import android.content.ContentResolver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
@@ -143,6 +144,8 @@ public class BiometricService extends SystemService {
private final BiometricCameraManager mBiometricCameraManager;
+ private final BiometricNotificationLogger mBiometricNotificationLogger;
+
/**
* Tracks authenticatorId invalidation. For more details, see
* {@link com.android.server.biometrics.sensors.InvalidationRequesterClient}.
@@ -1100,6 +1103,10 @@ public class BiometricService extends SystemService {
return new BiometricCameraManagerImpl(context.getSystemService(CameraManager.class),
context.getSystemService(SensorPrivacyManager.class));
}
+
+ public BiometricNotificationLogger getNotificationLogger() {
+ return new BiometricNotificationLogger();
+ }
}
/**
@@ -1133,6 +1140,7 @@ public class BiometricService extends SystemService {
mBiometricCameraManager = injector.getBiometricCameraManager(context);
mKeystoreAuthorization = injector.getKeystoreAuthorizationService();
mGateKeeper = injector.getGateKeeperService();
+ mBiometricNotificationLogger = injector.getNotificationLogger();
try {
injector.getActivityManagerService().registerUserSwitchObserver(
@@ -1157,6 +1165,20 @@ public class BiometricService extends SystemService {
mInjector.publishBinderService(this, mImpl);
mBiometricStrengthController = mInjector.getBiometricStrengthController(this);
mBiometricStrengthController.startListening();
+
+ mHandler.post(new Runnable(){
+ @Override
+ public void run() {
+ try {
+ mBiometricNotificationLogger.registerAsSystemService(getContext(),
+ new ComponentName(getContext(), BiometricNotificationLogger.class),
+ UserHandle.USER_ALL);
+ } catch (RemoteException e) {
+ // Intra-process call, should never happen.
+ }
+ }
+
+ });
}
private boolean isStrongBiometric(int id) {
@@ -1508,4 +1530,5 @@ public class BiometricService extends SystemService {
pw.println("CurrentSession: " + mAuthSession);
pw.println();
}
+
}
diff --git a/services/core/java/com/android/server/biometrics/TEST_MAPPING b/services/core/java/com/android/server/biometrics/TEST_MAPPING
deleted file mode 100644
index 9e60ba8b5d87..000000000000
--- a/services/core/java/com/android/server/biometrics/TEST_MAPPING
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "presubmit": [
- {
- "name": "CtsBiometricsTestCases"
- },
- {
- "name": "CtsBiometricsHostTestCases"
- }
- ]
-}
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java b/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java
index 6bd48802baf5..f31b2e11b021 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java
@@ -113,14 +113,16 @@ public class BiometricFrameworkStatsLogger {
/** {@see FrameworkStatsLog.BIOMETRIC_ENROLLED}. */
public void enroll(int statsModality, int statsAction, int statsClient,
- int targetUserId, long latency, boolean enrollSuccessful, float ambientLightLux) {
+ int targetUserId, long latency, boolean enrollSuccessful, float ambientLightLux,
+ int source) {
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ENROLLED,
statsModality,
targetUserId,
sanitizeLatency(latency),
enrollSuccessful,
-1, /* sensorId */
- ambientLightLux);
+ ambientLightLux,
+ source);
}
/** {@see FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED}. */
@@ -239,6 +241,12 @@ public class BiometricFrameworkStatsLogger {
-1 /* sensorId */);
}
+ /** {@see FrameworkStatsLog.BIOMETRIC_FRR_NOTIFICATION}. */
+ public void logFrameworkNotification(int action, int modality) {
+ FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_FRR_NOTIFICATION,
+ action, modality);
+ }
+
private long sanitizeLatency(long latency) {
if (latency < 0) {
Slog.w(TAG, "found a negative latency : " + latency);
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricLogger.java b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
index dbef7178efd0..cd5d0c8dc3f2 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
@@ -252,7 +252,8 @@ public class BiometricLogger {
}
/** Log enrollment outcome. */
- public void logOnEnrolled(int targetUserId, long latency, boolean enrollSuccessful) {
+ public void logOnEnrolled(int targetUserId, long latency, boolean enrollSuccessful,
+ int source) {
if (!mShouldLogMetrics) {
return;
}
@@ -273,7 +274,7 @@ public class BiometricLogger {
}
mSink.enroll(mStatsModality, mStatsAction, mStatsClient,
- targetUserId, latency, enrollSuccessful, mALSProbe.getMostRecentLux());
+ targetUserId, latency, enrollSuccessful, mALSProbe.getMostRecentLux(), source);
}
/** Report unexpected enrollment reported by the HAL. */
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java b/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java
index 2aec9aefa8d1..0e22f7511af9 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java
@@ -23,6 +23,9 @@ import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.hardware.biometrics.BiometricManager;
+import android.hardware.face.FaceEnrollOptions;
+import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Slog;
@@ -36,25 +39,28 @@ public class BiometricNotificationUtils {
private static final String TAG = "BiometricNotificationUtils";
private static final String FACE_RE_ENROLL_NOTIFICATION_TAG = "FaceReEnroll";
- private static final String FACE_ENROLL_NOTIFICATION_TAG = "FaceEnroll";
- private static final String FINGERPRINT_ENROLL_NOTIFICATION_TAG = "FingerprintEnroll";
private static final String BAD_CALIBRATION_NOTIFICATION_TAG = "FingerprintBadCalibration";
private static final String KEY_RE_ENROLL_FACE = "re_enroll_face_unlock";
private static final String FACE_SETTINGS_ACTION = "android.settings.FACE_SETTINGS";
- private static final String FINGERPRINT_SETTINGS_ACTION =
- "android.settings.FINGERPRINT_SETTINGS";
private static final String FACE_ENROLL_ACTION = "android.settings.FACE_ENROLL";
private static final String FINGERPRINT_ENROLL_ACTION = "android.settings.FINGERPRINT_ENROLL";
+ private static final String FINGERPRINT_SETTINGS_ACTION =
+ "android.settings.FINGERPRINT_SETTINGS";
private static final String SETTINGS_PACKAGE = "com.android.settings";
private static final String FACE_ENROLL_CHANNEL = "FaceEnrollNotificationChannel";
private static final String FACE_RE_ENROLL_CHANNEL = "FaceReEnrollNotificationChannel";
private static final String FINGERPRINT_ENROLL_CHANNEL = "FingerprintEnrollNotificationChannel";
private static final String FINGERPRINT_BAD_CALIBRATION_CHANNEL =
"FingerprintBadCalibrationNotificationChannel";
- private static final int NOTIFICATION_ID = 1;
private static final long NOTIFICATION_INTERVAL_MS = 24 * 60 * 60 * 1000;
private static long sLastAlertTime = 0;
+ private static final String ACTION_BIOMETRIC_FRR_DISMISS = "action_biometric_frr_dismiss";
+ // Dismissal action for FRR notification.
+ private static final Intent DISMISS_FRR_INTENT = new Intent(ACTION_BIOMETRIC_FRR_DISMISS);
+ public static final int NOTIFICATION_ID = 1;
+ public static final String FACE_ENROLL_NOTIFICATION_TAG = "FaceEnroll";
+ public static final String FINGERPRINT_ENROLL_NOTIFICATION_TAG = "FingerprintEnroll";
/**
* Shows a face re-enrollment notification.
*/
@@ -71,7 +77,6 @@ public class BiometricNotificationUtils {
final Intent intent = new Intent(FACE_SETTINGS_ACTION);
intent.setPackage(SETTINGS_PACKAGE);
- intent.putExtra(KEY_RE_ENROLL_FACE, true);
final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context,
0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE /* flags */,
@@ -79,13 +84,14 @@ public class BiometricNotificationUtils {
showNotificationHelper(context, name, title, content, pendingIntent, FACE_RE_ENROLL_CHANNEL,
Notification.CATEGORY_SYSTEM, FACE_RE_ENROLL_NOTIFICATION_TAG,
- Notification.VISIBILITY_SECRET);
+ Notification.VISIBILITY_SECRET, false);
}
/**
* Shows a face enrollment notification.
*/
public static void showFaceEnrollNotification(@NonNull Context context) {
+ Slog.d(TAG, "Showing Face Enroll Notification");
final String name =
context.getString(R.string.device_unlock_notification_name);
@@ -96,6 +102,8 @@ public class BiometricNotificationUtils {
final Intent intent = new Intent(FACE_ENROLL_ACTION);
intent.setPackage(SETTINGS_PACKAGE);
+ intent.putExtra(BiometricManager.EXTRA_ENROLL_REASON,
+ FaceEnrollOptions.ENROLL_REASON_RE_ENROLL_NOTIFICATION);
final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context,
0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE /* flags */,
@@ -103,14 +111,15 @@ public class BiometricNotificationUtils {
showNotificationHelper(context, name, title, content, pendingIntent, FACE_ENROLL_CHANNEL,
Notification.CATEGORY_RECOMMENDATION, FACE_ENROLL_NOTIFICATION_TAG,
- Notification.VISIBILITY_PUBLIC);
+ Notification.VISIBILITY_PUBLIC, true);
+
}
/**
* Shows a fingerprint enrollment notification.
*/
public static void showFingerprintEnrollNotification(@NonNull Context context) {
-
+ Slog.d(TAG, "Showing Fingerprint Enroll Notification");
final String name =
context.getString(R.string.device_unlock_notification_name);
final String title =
@@ -120,6 +129,8 @@ public class BiometricNotificationUtils {
final Intent intent = new Intent(FINGERPRINT_ENROLL_ACTION);
intent.setPackage(SETTINGS_PACKAGE);
+ intent.putExtra(BiometricManager.EXTRA_ENROLL_REASON,
+ FingerprintEnrollOptions.ENROLL_REASON_RE_ENROLL_NOTIFICATION);
final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context,
0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE /* flags */,
@@ -127,7 +138,8 @@ public class BiometricNotificationUtils {
showNotificationHelper(context, name, title, content, pendingIntent,
Notification.CATEGORY_RECOMMENDATION, FINGERPRINT_ENROLL_CHANNEL,
- FINGERPRINT_ENROLL_NOTIFICATION_TAG, Notification.VISIBILITY_PUBLIC);
+ FINGERPRINT_ENROLL_NOTIFICATION_TAG, Notification.VISIBILITY_PUBLIC, true);
+
}
/**
@@ -162,17 +174,22 @@ public class BiometricNotificationUtils {
showNotificationHelper(context, name, title, content, pendingIntent,
Notification.CATEGORY_SYSTEM, FINGERPRINT_BAD_CALIBRATION_CHANNEL,
- BAD_CALIBRATION_NOTIFICATION_TAG, Notification.VISIBILITY_SECRET);
+ BAD_CALIBRATION_NOTIFICATION_TAG, Notification.VISIBILITY_SECRET, false);
}
private static void showNotificationHelper(Context context, String name, String title,
String content, PendingIntent pendingIntent, String category,
- String channelName, String notificationTag, int visibility) {
+ String channelName, String notificationTag, int visibility,
+ boolean listenToDismissEvent) {
+ Slog.v(TAG," listenToDismissEvent = " + listenToDismissEvent);
+ final PendingIntent dismissIntent = PendingIntent.getActivityAsUser(context,
+ 0 /* requestCode */, DISMISS_FRR_INTENT, PendingIntent.FLAG_IMMUTABLE /* flags */,
+ null /* options */, UserHandle.CURRENT);
final NotificationManager notificationManager =
context.getSystemService(NotificationManager.class);
final NotificationChannel channel = new NotificationChannel(channelName, name,
NotificationManager.IMPORTANCE_HIGH);
- final Notification notification = new Notification.Builder(context, channelName)
+ final Notification.Builder builder = new Notification.Builder(context, channelName)
.setSmallIcon(R.drawable.ic_lock)
.setContentTitle(title)
.setContentText(content)
@@ -183,12 +200,17 @@ public class BiometricNotificationUtils {
.setAutoCancel(true)
.setCategory(category)
.setContentIntent(pendingIntent)
- .setVisibility(visibility)
- .build();
+ .setVisibility(visibility);
+
+ if (listenToDismissEvent) {
+ builder.setDeleteIntent(dismissIntent);
+ }
+ final Notification notification = builder.build();
notificationManager.createNotificationChannel(channel);
notificationManager.notifyAsUser(notificationTag, NOTIFICATION_ID, notification,
UserHandle.CURRENT);
+
}
/**
diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
index 8e7004d8fde5..af6de5c7316a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
@@ -45,6 +45,7 @@ public abstract class EnrollClient<T> extends AcquisitionClient<T> implements En
private long mEnrollmentStartTimeMs;
private final boolean mHasEnrollmentsBeforeStarting;
+ private final int mEnrollReason;
/**
* @return true if the user has already enrolled the maximum number of templates.
@@ -55,13 +56,15 @@ public abstract class EnrollClient<T> extends AcquisitionClient<T> implements En
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils utils,
int timeoutSec, int sensorId, boolean shouldVibrate,
- @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ int enrollReason) {
super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
shouldVibrate, logger, biometricContext);
mBiometricUtils = utils;
mHardwareAuthToken = Arrays.copyOf(hardwareAuthToken, hardwareAuthToken.length);
mTimeoutSec = timeoutSec;
mHasEnrollmentsBeforeStarting = hasEnrollments();
+ mEnrollReason = enrollReason;
}
@Override
@@ -91,7 +94,7 @@ public abstract class EnrollClient<T> extends AcquisitionClient<T> implements En
mBiometricUtils.addBiometricForUser(getContext(), getTargetUserId(), identifier);
getLogger().logOnEnrolled(getTargetUserId(),
System.currentTimeMillis() - mEnrollmentStartTimeMs,
- true /* enrollSuccessful */);
+ true /* enrollSuccessful */, mEnrollReason);
mCallback.onClientFinished(this, true /* success */);
}
notifyUserActivity();
@@ -119,7 +122,7 @@ public abstract class EnrollClient<T> extends AcquisitionClient<T> implements En
public void onError(int error, int vendorCode) {
getLogger().logOnEnrolled(getTargetUserId(),
System.currentTimeMillis() - mEnrollmentStartTimeMs,
- false /* enrollSuccessful */);
+ false /* enrollSuccessful */, mEnrollReason);
super.onError(error, vendorCode);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
index b0649b90c466..0f01510bd908 100644
--- a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
@@ -22,7 +22,6 @@ import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.os.IBinder;
-import com.android.server.biometrics.Flags;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.log.OperationContextExt;
@@ -94,9 +93,6 @@ public abstract class HalClientMonitor<T> extends BaseClientMonitor {
}
protected OperationContextExt getOperationContext() {
- if (Flags.deHidl()) {
- return mOperationContext;
- }
return getBiometricContext().updateContext(mOperationContext, isCryptoOperation());
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index 321e951ec09b..68b4e3fb51ba 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -37,6 +37,7 @@ import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.SensorProps;
import android.hardware.face.Face;
import android.hardware.face.FaceAuthenticateOptions;
+import android.hardware.face.FaceEnrollOptions;
import android.hardware.face.FaceSensorConfigurations;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.face.FaceServiceReceiver;
@@ -44,6 +45,7 @@ import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
import android.hardware.face.IFaceService;
import android.hardware.face.IFaceServiceReceiver;
import android.os.Binder;
+import android.os.Build;
import android.os.IBinder;
import android.os.NativeHandle;
import android.os.RemoteException;
@@ -210,7 +212,8 @@ public class FaceService extends SystemService {
@Override // Binder call
public long enroll(int userId, final IBinder token, final byte[] hardwareAuthToken,
final IFaceServiceReceiver receiver, final String opPackageName,
- final int[] disabledFeatures, Surface previewSurface, boolean debugConsent) {
+ final int[] disabledFeatures, Surface previewSurface, boolean debugConsent,
+ FaceEnrollOptions options) {
super.enroll_enforcePermission();
final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
@@ -220,7 +223,8 @@ public class FaceService extends SystemService {
}
return provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
- receiver, opPackageName, disabledFeatures, previewSurface, debugConsent);
+ receiver, opPackageName, disabledFeatures, previewSurface, debugConsent,
+ options);
}
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@@ -958,4 +962,25 @@ public class FaceService extends SystemService {
}
}
}
+
+ /**
+ * This should only be called from FaceShellCommand class.
+ */
+ void sendFaceReEnrollNotification() {
+ Utils.checkPermissionOrShell(getContext(), MANAGE_FACE);
+ if (Build.IS_DEBUGGABLE) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
+ if (provider != null) {
+ FaceProvider faceProvider = (FaceProvider) provider.second;
+ faceProvider.sendFaceReEnrollNotification();
+ } else {
+ Slog.w(TAG, "Null provider for notification");
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceShellCommand.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceShellCommand.java
index 187575dcd664..e167bbafc02e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceShellCommand.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceShellCommand.java
@@ -42,6 +42,8 @@ public class FaceShellCommand extends ShellCommand {
return doHelp();
case "sync":
return doSync();
+ case "notification":
+ return doNotify();
default:
getOutPrintWriter().println("Unrecognized command: " + cmd);
}
@@ -59,6 +61,8 @@ public class FaceShellCommand extends ShellCommand {
pw.println(" Print this help text.");
pw.println(" sync");
pw.println(" Sync enrollments now (virtualized sensors only).");
+ pw.println(" notification");
+ pw.println(" Sends a Face re-enrollment notification");
}
private int doHelp() {
@@ -70,4 +74,9 @@ public class FaceShellCommand extends ShellCommand {
mService.syncEnrollmentsNow();
return 0;
}
+
+ private int doNotify() {
+ mService.sendFaceReEnrollNotification();
+ return 0;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
index 2cf64b72d01f..6f76cdae4539 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
@@ -23,6 +23,7 @@ import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.face.Face;
import android.hardware.face.FaceAuthenticateOptions;
+import android.hardware.face.FaceEnrollOptions;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.face.IFaceServiceReceiver;
@@ -79,7 +80,7 @@ public interface ServiceProvider extends BiometricServiceProvider<FaceSensorProp
long scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken,
int userId, @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName,
@NonNull int[] disabledFeatures, @Nullable Surface previewSurface,
- boolean debugConsent);
+ boolean debugConsent, FaceEnrollOptions options);
void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
index d11f0991a5d4..0fdd57d64d8d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
@@ -26,6 +26,7 @@ import android.hardware.biometrics.face.EnrollmentFrame;
import android.hardware.face.Face;
import android.hardware.face.FaceAuthenticationFrame;
import android.hardware.face.FaceEnrollFrame;
+import android.hardware.face.FaceEnrollOptions;
import android.hardware.face.IFaceServiceReceiver;
import android.os.Binder;
import android.os.RemoteException;
@@ -155,7 +156,8 @@ public class BiometricTestSessionImpl extends ITestSession.Stub {
mProvider.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver,
mContext.getOpPackageName(), new int[0] /* disabledFeatures */,
- null /* previewSurface */, false /* debugConsent */);
+ null /* previewSurface */, false /* debugConsent */,
+ (new FaceEnrollOptions.Builder()).build());
}
@android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
index 5f370f23134c..781e3f491ec8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
@@ -93,9 +93,11 @@ public class FaceEnrollClient extends EnrollClient<AidlSession> {
@NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec,
@Nullable Surface previewSurface, int sensorId,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
- int maxTemplatesPerUser, boolean debugConsent) {
+ int maxTemplatesPerUser, boolean debugConsent,
+ android.hardware.face.FaceEnrollOptions options) {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, opPackageName, utils,
- timeoutSec, sensorId, false /* shouldVibrate */, logger, biometricContext);
+ timeoutSec, sensorId, false /* shouldVibrate */, logger, biometricContext,
+ BiometricFaceConstants.reasonToMetric(options.getEnrollReason()));
setRequestId(requestId);
mEnrollIgnoreList = getContext().getResources()
.getIntArray(R.array.config_face_acquire_enroll_ignorelist);
@@ -105,6 +107,9 @@ public class FaceEnrollClient extends EnrollClient<AidlSession> {
mDebugConsent = debugConsent;
mDisabledFeatures = disabledFeatures;
mPreviewSurface = previewSurface;
+ Slog.w(TAG, "EnrollOptions "
+ + android.hardware.face.FaceEnrollOptions.enrollReasonToString(
+ options.getEnrollReason()));
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index f469f6224e4c..007b7462f637 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -35,6 +35,7 @@ import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.SensorProps;
import android.hardware.face.Face;
import android.hardware.face.FaceAuthenticateOptions;
+import android.hardware.face.FaceEnrollOptions;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.face.IFaceServiceReceiver;
import android.os.Binder;
@@ -519,7 +520,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
public long scheduleEnroll(int sensorId, @NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver,
@NonNull String opPackageName, @NonNull int[] disabledFeatures,
- @Nullable Surface previewSurface, boolean debugConsent) {
+ @Nullable Surface previewSurface, boolean debugConsent, FaceEnrollOptions options) {
final long id = mRequestCounter.incrementAndGet();
mHandler.post(() -> {
mFaceSensors.get(sensorId).scheduleFaceUpdateActiveUserClient(userId);
@@ -533,7 +534,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
createLogger(BiometricsProtoEnums.ACTION_ENROLL,
BiometricsProtoEnums.CLIENT_UNKNOWN,
mAuthenticationStatsCollector),
- mBiometricContext, maxTemplatesPerUser, debugConsent);
+ mBiometricContext, maxTemplatesPerUser, debugConsent, options);
if (Flags.deHidl()) {
scheduleForSensor(sensorId, client, mBiometricStateCallback);
} else {
@@ -903,4 +904,11 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
public boolean getTestHalEnabled() {
return mTestHalEnabled;
}
+
+ /**
+ * Sends a face re enroll notification.
+ */
+ public void sendFaceReEnrollNotification() {
+ mAuthenticationStatsCollector.sendFaceReEnrollNotification();
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
index 151ffaa1cb28..0e2367a599a5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
@@ -23,6 +23,7 @@ import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.face.Face;
import android.hardware.face.FaceAuthenticationFrame;
import android.hardware.face.FaceEnrollFrame;
+import android.hardware.face.FaceEnrollOptions;
import android.hardware.face.IFaceServiceReceiver;
import android.os.Binder;
import android.os.RemoteException;
@@ -143,7 +144,8 @@ public class BiometricTestSessionImpl extends ITestSession.Stub {
mFace10.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver,
mContext.getOpPackageName(), new int[0] /* disabledFeatures */,
- null /* previewSurface */, false /* debugConsent */);
+ null /* previewSurface */, false /* debugConsent */,
+ (new FaceEnrollOptions.Builder()).build());
}
@android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index 48a676ce4937..306ddfa1e083 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -32,6 +32,7 @@ import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback;
import android.hardware.face.Face;
import android.hardware.face.FaceAuthenticateOptions;
+import android.hardware.face.FaceEnrollOptions;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.face.IFaceServiceReceiver;
import android.os.Binder;
@@ -711,16 +712,17 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
public long scheduleEnroll(int sensorId, @NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver,
@NonNull String opPackageName, @NonNull int[] disabledFeatures,
- @Nullable Surface previewSurface, boolean debugConsent) {
+ @Nullable Surface previewSurface, boolean debugConsent,
+ @NonNull FaceEnrollOptions options) {
final long id = mRequestCounter.incrementAndGet();
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
if (Flags.deHidl()) {
scheduleEnrollAidl(token, hardwareAuthToken, userId, receiver,
- opPackageName, disabledFeatures, previewSurface, id);
+ opPackageName, disabledFeatures, previewSurface, id, options);
} else {
scheduleEnrollHidl(token, hardwareAuthToken, userId, receiver,
- opPackageName, disabledFeatures, previewSurface, id);
+ opPackageName, disabledFeatures, previewSurface, id, options);
}
});
return id;
@@ -729,7 +731,8 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
private void scheduleEnrollAidl(@NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver,
@NonNull String opPackageName, @NonNull int[] disabledFeatures,
- @Nullable Surface previewSurface, long id) {
+ @Nullable Surface previewSurface, long id,
+ @NonNull FaceEnrollOptions options) {
final com.android.server.biometrics.sensors.face.aidl.FaceEnrollClient client =
new com.android.server.biometrics.sensors.face.aidl.FaceEnrollClient(
mContext, this::getSession, token,
@@ -742,7 +745,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
mAuthenticationStatsCollector), mBiometricContext,
mContext.getResources().getInteger(
com.android.internal.R.integer.config_faceMaxTemplatesPerUser),
- false);
+ false, options);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
@@ -770,14 +773,14 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
private void scheduleEnrollHidl(@NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver,
@NonNull String opPackageName, @NonNull int[] disabledFeatures,
- @Nullable Surface previewSurface, long id) {
+ @Nullable Surface previewSurface, long id, FaceEnrollOptions options) {
final FaceEnrollClient client = new FaceEnrollClient(mContext, mLazyDaemon, token,
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
opPackageName, id, FaceUtils.getLegacyInstance(mSensorId), disabledFeatures,
ENROLL_TIMEOUT_SEC, previewSurface, mSensorId,
createLogger(BiometricsProtoEnums.ACTION_ENROLL,
BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
- mBiometricContext);
+ mBiometricContext, options);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
index 27b9c79516af..815cf9180130 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
@@ -23,6 +23,7 @@ import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.biometrics.face.V1_0.Status;
import android.hardware.face.Face;
+import android.hardware.face.FaceEnrollOptions;
import android.hardware.face.FaceManager;
import android.os.IBinder;
import android.os.RemoteException;
@@ -61,15 +62,20 @@ public class FaceEnrollClient extends EnrollClient<IBiometricsFace> {
@NonNull byte[] hardwareAuthToken, @NonNull String owner, long requestId,
@NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec,
@Nullable Surface previewSurface, int sensorId,
- @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
+ @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+ @NonNull FaceEnrollOptions options) {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
- timeoutSec, sensorId, false /* shouldVibrate */, logger, biometricContext);
+ timeoutSec, sensorId, false /* shouldVibrate */, logger, biometricContext,
+ BiometricFaceConstants.reasonToMetric(options.getEnrollReason()));
setRequestId(requestId);
mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length);
mEnrollIgnoreList = getContext().getResources()
.getIntArray(R.array.config_face_acquire_enroll_ignorelist);
mEnrollIgnoreListVendor = getContext().getResources()
.getIntArray(R.array.config_face_acquire_vendor_enroll_ignorelist);
+
+ Slog.w(TAG, "EnrollOptions "
+ + FaceEnrollOptions.enrollReasonToString(options.getEnrollReason()));
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index e01d672d7e34..1ba12134ab29 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -48,6 +48,7 @@ import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.biometrics.fingerprint.SensorProps;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintAuthenticateOptions;
+import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorConfigurations;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -239,7 +240,8 @@ public class FingerprintService extends SystemService {
@Override // Binder call
public long enroll(final IBinder token, @NonNull final byte[] hardwareAuthToken,
final int userId, final IFingerprintServiceReceiver receiver,
- final String opPackageName, @FingerprintManager.EnrollReason int enrollReason) {
+ final String opPackageName, @FingerprintManager.EnrollReason int enrollReason,
+ FingerprintEnrollOptions options) {
super.enroll_enforcePermission();
final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
@@ -249,7 +251,7 @@ public class FingerprintService extends SystemService {
}
return provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
- receiver, opPackageName, enrollReason);
+ receiver, opPackageName, enrollReason, options);
}
@android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_FINGERPRINT)
@@ -1322,4 +1324,25 @@ public class FingerprintService extends SystemService {
}
}
}
+
+ /**
+ * This should only be called from FingerprintShellCommand
+ */
+ void sendFingerprintReEnrollNotification() {
+ Utils.checkPermissionOrShell(getContext(), MANAGE_FINGERPRINT);
+ if (Build.IS_DEBUGGABLE) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
+ if (provider != null) {
+ FingerprintProvider fingerprintProvider = (FingerprintProvider) provider.second;
+ fingerprintProvider.sendFingerprintReEnrollNotification();
+ } else {
+ Slog.w(TAG, "Null provider for notification");
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintShellCommand.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintShellCommand.java
index dc6a63f82bb1..766b3a1f1fbf 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintShellCommand.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintShellCommand.java
@@ -47,6 +47,8 @@ public class FingerprintShellCommand extends ShellCommand {
return doSync();
case "fingerdown":
return doSimulateVhalFingerDown();
+ case "notification":
+ return doNotify();
default:
getOutPrintWriter().println("Unrecognized command: " + cmd);
}
@@ -66,6 +68,8 @@ public class FingerprintShellCommand extends ShellCommand {
pw.println(" Sync enrollments now (virtualized sensors only).");
pw.println(" fingerdown");
pw.println(" Simulate finger down event (virtualized sensors only).");
+ pw.println(" notification");
+ pw.println(" Sends a Fingerprint re-enrollment notification");
}
private int doHelp() {
@@ -82,4 +86,9 @@ public class FingerprintShellCommand extends ShellCommand {
mService.simulateVhalFingerDown();
return 0;
}
+
+ private int doNotify() {
+ mService.sendFingerprintReEnrollNotification();
+ return 0;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index fc37d7020a21..c2d11699b91f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -24,6 +24,7 @@ import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintAuthenticateOptions;
+import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
@@ -74,7 +75,8 @@ public interface ServiceProvider extends
*/
long scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken,
int userId, @NonNull IFingerprintServiceReceiver receiver,
- @NonNull String opPackageName, @FingerprintManager.EnrollReason int enrollReason);
+ @NonNull String opPackageName, @FingerprintManager.EnrollReason int enrollReason,
+ @NonNull FingerprintEnrollOptions options);
void cancelEnrollment(int sensorId, @NonNull IBinder token, long requestId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
index ec1eeb138505..d64b6c29c840 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
@@ -16,13 +16,12 @@
package com.android.server.biometrics.sensors.fingerprint.aidl;
-import static android.Manifest.permission.TEST_BIOMETRIC;
-
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.os.Binder;
@@ -30,7 +29,6 @@ import android.os.RemoteException;
import android.util.Slog;
import com.android.server.biometrics.HardwareAuthTokenUtils;
-import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricStateCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
@@ -153,7 +151,8 @@ class BiometricTestSessionImpl extends ITestSession.Stub {
super.startEnroll_enforcePermission();
mProvider.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver,
- mContext.getOpPackageName(), FingerprintManager.ENROLL_ENROLL);
+ mContext.getOpPackageName(), FingerprintManager.ENROLL_ENROLL,
+ (new FingerprintEnrollOptions.Builder()).build());
}
@android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index 79975e515c70..a24ab1d022c6 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -29,6 +29,7 @@ import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.common.OperationState;
import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.ISidefpsController;
@@ -98,11 +99,13 @@ public class FingerprintEnrollClient extends EnrollClient<AidlSession> implement
// TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
@Nullable ISidefpsController sidefpsController,
@NonNull AuthenticationStateListeners authenticationStateListeners,
- int maxTemplatesPerUser, @FingerprintManager.EnrollReason int enrollReason) {
+ int maxTemplatesPerUser, @FingerprintManager.EnrollReason int enrollReason,
+ @NonNull FingerprintEnrollOptions options) {
// UDFPS haptics occur when an image is acquired (instead of when the result is known)
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
0 /* timeoutSec */, sensorId, shouldVibrateFor(context, sensorProps),
- logger, biometricContext);
+ logger, biometricContext,
+ BiometricFingerprintConstants.reasonToMetric(options.getEnrollReason()));
setRequestId(requestId);
mSensorProps = sensorProps;
if (sidefpsControllerRefactor()) {
@@ -120,6 +123,8 @@ public class FingerprintEnrollClient extends EnrollClient<AidlSession> implement
if (enrollReason == FingerprintManager.ENROLL_FIND_SENSOR) {
getLogger().disableMetrics();
}
+ Slog.w(TAG, "EnrollOptions "
+ + FingerprintEnrollOptions.enrollReasonToString(options.getEnrollReason()));
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index fd938ed9c389..a104cf4e1726 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -39,6 +39,7 @@ import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.biometrics.fingerprint.SensorProps;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintAuthenticateOptions;
+import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
@@ -515,7 +516,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
public long scheduleEnroll(int sensorId, @NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId,
@NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
- @FingerprintManager.EnrollReason int enrollReason) {
+ @FingerprintManager.EnrollReason int enrollReason,
+ @NonNull FingerprintEnrollOptions options) {
final long id = mRequestCounter.incrementAndGet();
mHandler.post(() -> {
final int maxTemplatesPerUser = mFingerprintSensors.get(sensorId).getSensorProperties()
@@ -529,7 +531,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
mBiometricContext,
mFingerprintSensors.get(sensorId).getSensorProperties(),
mUdfpsOverlayController, mSidefpsController,
- mAuthenticationStateListeners, maxTemplatesPerUser, enrollReason);
+ mAuthenticationStateListeners, maxTemplatesPerUser, enrollReason, options);
if (Flags.deHidl()) {
scheduleForSensor(sensorId, client, mBiometricStateCallback);
} else {
@@ -1021,4 +1023,11 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
Slog.e(getTag(), "failed hal operation ", e);
}
}
+
+ /**
+ * Sends a fingerprint enroll notification.
+ */
+ public void sendFingerprintReEnrollNotification() {
+ mAuthenticationStatsCollector.sendFingerprintReEnrollNotification();
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
index c20a9eb958c4..fc037aeb7e17 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
@@ -16,20 +16,18 @@
package com.android.server.biometrics.sensors.fingerprint.hidl;
-import static android.Manifest.permission.TEST_BIOMETRIC;
-
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.ITestSession;
import android.hardware.biometrics.ITestSessionCallback;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.os.Binder;
import android.os.RemoteException;
import android.util.Slog;
-import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricStateCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
@@ -153,7 +151,8 @@ public class BiometricTestSessionImpl extends ITestSession.Stub {
super.startEnroll_enforcePermission();
mFingerprint21.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver,
- mContext.getOpPackageName(), FingerprintManager.ENROLL_ENROLL);
+ mContext.getOpPackageName(), FingerprintManager.ENROLL_ENROLL,
+ (new FingerprintEnrollOptions.Builder()).build());
}
@android.annotation.EnforcePermission(android.Manifest.permission.TEST_BIOMETRIC)
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 4accf8f7ff30..33e448bbe612 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -35,6 +35,7 @@ import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprintClientCallback;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintAuthenticateOptions;
+import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -700,17 +701,18 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
public long scheduleEnroll(int sensorId, @NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId,
@NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
- @FingerprintManager.EnrollReason int enrollReason) {
+ @FingerprintManager.EnrollReason int enrollReason,
+ @NonNull FingerprintEnrollOptions options) {
final long id = mRequestCounter.incrementAndGet();
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
if (Flags.deHidl()) {
scheduleEnrollAidl(token, hardwareAuthToken, userId, receiver,
- opPackageName, enrollReason, id);
+ opPackageName, enrollReason, id, options);
} else {
scheduleEnrollHidl(token, hardwareAuthToken, userId, receiver,
- opPackageName, enrollReason, id);
+ opPackageName, enrollReason, id, options);
}
});
return id;
@@ -719,7 +721,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
private void scheduleEnrollHidl(@NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId,
@NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
- @FingerprintManager.EnrollReason int enrollReason, long id) {
+ @FingerprintManager.EnrollReason int enrollReason, long id,
+ @NonNull FingerprintEnrollOptions options) {
final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext,
mLazyDaemon, token, id, new ClientMonitorCallbackConverter(receiver),
userId, hardwareAuthToken, opPackageName,
@@ -730,7 +733,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
mBiometricContext, mUdfpsOverlayController,
// TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
mSidefpsController,
- mAuthenticationStateListeners, enrollReason);
+ mAuthenticationStateListeners, enrollReason, options);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
@@ -758,7 +761,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
private void scheduleEnrollAidl(@NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId,
@NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
- @FingerprintManager.EnrollReason int enrollReason, long id) {
+ @FingerprintManager.EnrollReason int enrollReason, long id,
+ @NonNull FingerprintEnrollOptions options) {
final com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintEnrollClient
client =
new com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintEnrollClient(
@@ -778,8 +782,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
mAuthenticationStateListeners,
mContext.getResources().getInteger(
com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser),
- enrollReason);
-
+ enrollReason, options);
mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
@Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
index 26332ff6893c..8f937fc65cbb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
@@ -27,6 +27,7 @@ import android.hardware.biometrics.BiometricStateListener;
import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.ISidefpsController;
import android.hardware.fingerprint.IUdfpsOverlayController;
@@ -75,10 +76,12 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint
// TODO(b/288175061): remove with Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
@Nullable ISidefpsController sidefpsController,
@NonNull AuthenticationStateListeners authenticationStateListeners,
- @FingerprintManager.EnrollReason int enrollReason) {
+ @FingerprintManager.EnrollReason int enrollReason,
+ @NonNull FingerprintEnrollOptions options) {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
timeoutSec, sensorId, true /* shouldVibrate */, biometricLogger,
- biometricContext);
+ biometricContext,
+ BiometricFingerprintConstants.reasonToMetric(options.getEnrollReason()));
setRequestId(requestId);
if (sidefpsControllerRefactor()) {
mSensorOverlays = new SensorOverlays(udfpsOverlayController);
@@ -91,6 +94,8 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint
if (enrollReason == FingerprintManager.ENROLL_FIND_SENSOR) {
getLogger().disableMetrics();
}
+ Slog.w(TAG, "EnrollOptions "
+ + FingerprintEnrollOptions.enrollReasonToString(options.getEnrollReason()));
}
@Override
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index cd064ae38aa5..38051c13049c 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -21,8 +21,8 @@ import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREG
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.hardware.devicestate.DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS;
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
-import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
-import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
+import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE_IDENTIFIER;
+import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE_IDENTIFIER;
import static com.android.server.devicestate.OverrideRequest.OVERRIDE_REQUEST_TYPE_BASE_STATE;
import static com.android.server.devicestate.OverrideRequest.OVERRIDE_REQUEST_TYPE_EMULATED_STATE;
@@ -1065,7 +1065,8 @@ public final class DeviceStateManagerService extends SystemService {
}
private final class DeviceStateProviderListener implements DeviceStateProvider.Listener {
- @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int mCurrentBaseState;
+ @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to = MAXIMUM_DEVICE_STATE_IDENTIFIER)
+ int mCurrentBaseState;
@Override
public void onSupportedDeviceStatesChanged(DeviceState[] newDeviceStates,
@@ -1078,8 +1079,10 @@ public final class DeviceStateManagerService extends SystemService {
@Override
public void onStateChanged(
- @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier) {
- if (identifier < MINIMUM_DEVICE_STATE || identifier > MAXIMUM_DEVICE_STATE) {
+ @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to =
+ MAXIMUM_DEVICE_STATE_IDENTIFIER) int identifier) {
+ if (identifier < MINIMUM_DEVICE_STATE_IDENTIFIER
+ || identifier > MAXIMUM_DEVICE_STATE_IDENTIFIER) {
throw new IllegalArgumentException("Invalid identifier: " + identifier);
}
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateProvider.java b/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
index 65b393ad94b9..b865c1d90a49 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
@@ -16,8 +16,8 @@
package com.android.server.devicestate;
-import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
-import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
+import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE_IDENTIFIER;
+import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE_IDENTIFIER;
import android.annotation.IntDef;
import android.annotation.IntRange;
@@ -133,10 +133,12 @@ public interface DeviceStateProvider extends Dumpable {
*
* @param identifier the identifier of the new device state.
*
- * @throws IllegalArgumentException if the state is less than {@link MINIMUM_DEVICE_STATE}
- * or greater than {@link MAXIMUM_DEVICE_STATE}.
+ * @throws IllegalArgumentException if the state is less than
+ * {@link MINIMUM_DEVICE_STATE_IDENTIFIER} or greater than
+ * {@link MAXIMUM_DEVICE_STATE_IDENTIFIER}.
*/
void onStateChanged(
- @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier);
+ @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to =
+ MAXIMUM_DEVICE_STATE_IDENTIFIER) int identifier);
}
}
diff --git a/services/core/java/com/android/server/display/BrightnessThrottler.java b/services/core/java/com/android/server/display/BrightnessThrottler.java
index bba5ba35dbc7..631e7518b746 100644
--- a/services/core/java/com/android/server/display/BrightnessThrottler.java
+++ b/services/core/java/com/android/server/display/BrightnessThrottler.java
@@ -37,9 +37,11 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel;
+import com.android.server.display.config.SensorData;
import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.display.utils.DebugUtils;
import com.android.server.display.utils.DeviceConfigParsingUtils;
+import com.android.server.display.utils.SensorUtils;
import java.io.PrintWriter;
import java.util.HashMap;
@@ -79,7 +81,7 @@ class BrightnessThrottler {
// Maps the throttling ID to the data. Sourced from DisplayDeviceConfig.
@NonNull
- private HashMap<String, ThermalBrightnessThrottlingData> mDdcThermalThrottlingDataMap;
+ private Map<String, ThermalBrightnessThrottlingData> mDdcThermalThrottlingDataMap;
// Current throttling data being used.
// Null if we do not support throttling.
@@ -97,6 +99,10 @@ class BrightnessThrottler {
// The brightness throttling configuration that should be used.
private String mThermalBrightnessThrottlingDataId;
+ // Temperature Sensor to be monitored for throttling.
+ @NonNull
+ private SensorData mTempSensor;
+
// This is a collection of brightness throttling data that has been written as overrides from
// the DeviceConfig. This will always take priority over the display device config data.
// We need to store the data for every display device, so we do not need to update this each
@@ -121,17 +127,19 @@ class BrightnessThrottler {
BrightnessThrottler(Handler handler, Runnable throttlingChangeCallback, String uniqueDisplayId,
String throttlingDataId,
- @NonNull HashMap<String, ThermalBrightnessThrottlingData>
- thermalBrightnessThrottlingDataMap) {
- this(new Injector(), handler, handler, throttlingChangeCallback,
- uniqueDisplayId, throttlingDataId, thermalBrightnessThrottlingDataMap);
+ @NonNull DisplayDeviceConfig displayDeviceConfig) {
+ this(new Injector(), handler, handler, throttlingChangeCallback, uniqueDisplayId,
+ throttlingDataId,
+ displayDeviceConfig.getThermalBrightnessThrottlingDataMapByThrottlingId(),
+ displayDeviceConfig.getTempSensor());
}
@VisibleForTesting
BrightnessThrottler(Injector injector, Handler handler, Handler deviceConfigHandler,
Runnable throttlingChangeCallback, String uniqueDisplayId, String throttlingDataId,
- @NonNull HashMap<String, ThermalBrightnessThrottlingData>
- thermalBrightnessThrottlingDataMap) {
+ @NonNull Map<String, ThermalBrightnessThrottlingData>
+ thermalBrightnessThrottlingDataMap,
+ @NonNull SensorData tempSensor) {
mInjector = injector;
mHandler = handler;
@@ -147,7 +155,7 @@ class BrightnessThrottler {
mDdcThermalThrottlingDataMap = thermalBrightnessThrottlingDataMap;
loadThermalBrightnessThrottlingDataFromDeviceConfig();
loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig(mDdcThermalThrottlingDataMap,
- mThermalBrightnessThrottlingDataId, mUniqueDisplayId);
+ tempSensor, mThermalBrightnessThrottlingDataId, mUniqueDisplayId);
}
boolean deviceSupportsThrottling() {
@@ -180,12 +188,14 @@ class BrightnessThrottler {
}
void loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig(
- HashMap<String, ThermalBrightnessThrottlingData> ddcThrottlingDataMap,
+ Map<String, ThermalBrightnessThrottlingData> ddcThrottlingDataMap,
+ SensorData tempSensor,
String brightnessThrottlingDataId,
String uniqueDisplayId) {
mDdcThermalThrottlingDataMap = ddcThrottlingDataMap;
mThermalBrightnessThrottlingDataId = brightnessThrottlingDataId;
mUniqueDisplayId = uniqueDisplayId;
+ mTempSensor = tempSensor;
resetThermalThrottlingData();
}
@@ -310,7 +320,7 @@ class BrightnessThrottler {
}
if (deviceSupportsThrottling()) {
- mSkinThermalStatusObserver.startObserving();
+ mSkinThermalStatusObserver.startObserving(mTempSensor);
}
}
@@ -357,6 +367,7 @@ class BrightnessThrottler {
private final class SkinThermalStatusObserver extends IThermalEventListener.Stub {
private final Injector mInjector;
private final Handler mHandler;
+ private SensorData mObserverTempSensor;
private IThermalService mThermalService;
private boolean mStarted;
@@ -371,28 +382,51 @@ class BrightnessThrottler {
if (DEBUG) {
Slog.d(TAG, "New thermal throttling status = " + temp.getStatus());
}
+
+ if (mObserverTempSensor.name != null
+ && !mObserverTempSensor.name.equals(temp.getName())) {
+ Slog.i(TAG, "Skipping thermal throttling notification as monitored sensor: "
+ + mObserverTempSensor.name
+ + " != notified sensor: "
+ + temp.getName());
+ return;
+ }
mHandler.post(() -> {
final @Temperature.ThrottlingStatus int status = temp.getStatus();
thermalStatusChanged(status);
});
}
- void startObserving() {
- if (mStarted) {
+ void startObserving(SensorData tempSensor) {
+ if (!mStarted || mObserverTempSensor == null) {
+ mObserverTempSensor = tempSensor;
+ registerThermalListener();
+ return;
+ }
+
+ String curType = mObserverTempSensor.type;
+ mObserverTempSensor = tempSensor;
+ if (curType.equals(tempSensor.type)) {
if (DEBUG) {
Slog.d(TAG, "Thermal status observer already started");
}
return;
}
+ stopObserving();
+ registerThermalListener();
+ }
+
+ void registerThermalListener() {
mThermalService = mInjector.getThermalService();
if (mThermalService == null) {
Slog.e(TAG, "Could not observe thermal status. Service not available");
return;
}
+ int temperatureType = SensorUtils.getSensorTemperatureType(mObserverTempSensor);
try {
// We get a callback immediately upon registering so there's no need to query
// for the current value.
- mThermalService.registerThermalEventListenerWithType(this, Temperature.TYPE_SKIN);
+ mThermalService.registerThermalEventListenerWithType(this, temperatureType);
mStarted = true;
} catch (RemoteException e) {
Slog.e(TAG, "Failed to register thermal status listener", e);
@@ -418,6 +452,7 @@ class BrightnessThrottler {
void dump(PrintWriter writer) {
writer.println(" SkinThermalStatusObserver:");
writer.println(" mStarted: " + mStarted);
+ writer.println(" mObserverTempSensor: " + mObserverTempSensor);
if (mThermalService != null) {
writer.println(" ThermalService available");
} else {
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 4c4cf6080965..9b2dcc53f456 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -384,6 +384,10 @@ import javax.xml.datatype.DatatypeConfigurationException;
* </point>
* </supportedModes>
* </proxSensor>
+ * <tempSensor>
+ * <type>DISPLAY</type>
+ * <name>VIRTUAL-SKIN-DISPLAY</name>
+ * </tempSensor>
*
* <ambientLightHorizonLong>10001</ambientLightHorizonLong>
* <ambientLightHorizonShort>2001</ambientLightHorizonShort>
@@ -625,6 +629,12 @@ public class DisplayDeviceConfig {
@Nullable
private SensorData mProximitySensor;
+ // The details of the temperature sensor associated with this display.
+ // Throttling will be based on thermal status of this sensor.
+ // For empty values default back to sensor of TYPE_SKIN.
+ @NonNull
+ private SensorData mTempSensor;
+
private final List<RefreshRateLimitation> mRefreshRateLimitations =
new ArrayList<>(2 /*initialCapacity*/);
@@ -821,10 +831,10 @@ public class DisplayDeviceConfig {
private String mLowBlockingZoneThermalMapId = null;
private String mHighBlockingZoneThermalMapId = null;
- private final HashMap<String, ThermalBrightnessThrottlingData>
+ private final Map<String, ThermalBrightnessThrottlingData>
mThermalBrightnessThrottlingDataMapByThrottlingId = new HashMap<>();
- private final HashMap<String, PowerThrottlingData>
+ private final Map<String, PowerThrottlingData>
mPowerThrottlingDataMapByThrottlingId = new HashMap<>();
private final Map<String, SparseArray<SurfaceControl.RefreshRateRange>>
@@ -1489,6 +1499,13 @@ public class DisplayDeviceConfig {
return mProximitySensor;
}
+ /**
+ * @return temperature sensor data associated with the display.
+ */
+ public SensorData getTempSensor() {
+ return mTempSensor;
+ }
+
boolean isAutoBrightnessAvailable() {
return mAutoBrightnessAvailable;
}
@@ -1539,7 +1556,7 @@ public class DisplayDeviceConfig {
/**
* @return brightness throttling configuration data for this display, for each throttling id.
*/
- public HashMap<String, ThermalBrightnessThrottlingData>
+ public Map<String, ThermalBrightnessThrottlingData>
getThermalBrightnessThrottlingDataMapByThrottlingId() {
return mThermalBrightnessThrottlingDataMapByThrottlingId;
}
@@ -1558,7 +1575,7 @@ public class DisplayDeviceConfig {
/**
* @return power throttling configuration data for this display, for each throttling id.
**/
- public HashMap<String, PowerThrottlingData>
+ public Map<String, PowerThrottlingData>
getPowerThrottlingDataMapByThrottlingId() {
return mPowerThrottlingDataMapByThrottlingId;
}
@@ -1871,6 +1888,7 @@ public class DisplayDeviceConfig {
+ "mAmbientLightSensor=" + mAmbientLightSensor
+ ", mScreenOffBrightnessSensor=" + mScreenOffBrightnessSensor
+ ", mProximitySensor=" + mProximitySensor
+ + ", mTempSensor=" + mTempSensor
+ ", mRefreshRateLimitations= " + Arrays.toString(mRefreshRateLimitations.toArray())
+ ", mDensityMapping= " + mDensityMapping
+ ", mAutoBrightnessBrighteningLightDebounce= "
@@ -1972,6 +1990,7 @@ public class DisplayDeviceConfig {
mContext.getResources());
mScreenOffBrightnessSensor = SensorData.loadScreenOffBrightnessSensorConfig(config);
mProximitySensor = SensorData.loadProxSensorConfig(config);
+ mTempSensor = SensorData.loadTempSensorConfig(mFlags, config);
loadAmbientHorizonFromDdc(config);
loadBrightnessChangeThresholds(config);
loadAutoBrightnessConfigValues(config);
@@ -1999,6 +2018,7 @@ public class DisplayDeviceConfig {
loadBrightnessRampsFromConfigXml();
mAmbientLightSensor = SensorData.loadAmbientLightSensorConfig(mContext.getResources());
mProximitySensor = SensorData.loadSensorUnspecifiedConfig();
+ mTempSensor = SensorData.loadTempSensorUnspecifiedConfig();
loadBrightnessChangeThresholdsFromXml();
loadAutoBrightnessConfigsFromConfigXml();
loadAutoBrightnessAvailableFromConfigXml();
@@ -2026,6 +2046,7 @@ public class DisplayDeviceConfig {
setSimpleMappingStrategyValues();
mAmbientLightSensor = SensorData.loadAmbientLightSensorConfig(mContext.getResources());
mProximitySensor = SensorData.loadSensorUnspecifiedConfig();
+ mTempSensor = SensorData.loadTempSensorUnspecifiedConfig();
loadAutoBrightnessAvailableFromConfigXml();
}
@@ -2161,7 +2182,7 @@ public class DisplayDeviceConfig {
final List<SdrHdrRatioPoint> points = sdrHdrRatioMap.getPoint();
final int size = points.size();
- if (size <= 0) {
+ if (size == 0) {
return null;
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 1ca3923b325c..3965d55b0c28 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -861,6 +861,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mThermalBrightnessThrottlingDataId = thermalBrightnessThrottlingDataId;
mBrightnessThrottler.loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig(
config.getThermalBrightnessThrottlingDataMapByThrottlingId(),
+ config.getTempSensor(),
mThermalBrightnessThrottlingDataId,
mUniqueDisplayId);
}
@@ -923,6 +924,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mBrightnessRangeController.loadFromConfig(hbmMetadata, token, info, mDisplayDeviceConfig);
mBrightnessThrottler.loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig(
mDisplayDeviceConfig.getThermalBrightnessThrottlingDataMapByThrottlingId(),
+ mDisplayDeviceConfig.getTempSensor(),
mThermalBrightnessThrottlingDataId, mUniqueDisplayId);
}
@@ -1638,21 +1640,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// SDR brightness is unchanged, so animate quickly as this is only impacting
// a likely minority amount of display content
// ie, the highlights of an HDR video or UltraHDR image
+ // Ideally we'd do this as fast as possible (ie, skip the animation entirely),
+ // but this requires display support and would need an entry in the
+ // display configuration. For now just do the fast animation
slowChange = false;
-
- // Going from HDR to no HDR; visually this should be a "no-op" anyway
- // as the remaining SDR content's brightness should be holding steady
- // due to the sdr brightness not shifting
- if (BrightnessSynchronizer.floatEquals(sdrAnimateValue, animateValue)) {
- skipAnimation = true;
- }
-
- // Going from no HDR to HDR; visually this is a significant scene change
- // and the animation just prevents advanced clients from doing their own
- // handling of enter/exit animations if they would like to do such a thing
- if (BrightnessSynchronizer.floatEquals(sdrAnimateValue, currentBrightness)) {
- skipAnimation = true;
- }
}
if (skipAnimation) {
animateScreenBrightness(animateValue, sdrAnimateValue,
@@ -2007,7 +1998,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
postBrightnessChangeRunnable();
}, mUniqueDisplayId,
mLogicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId,
- ddConfig.getThermalBrightnessThrottlingDataMapByThrottlingId());
+ ddConfig);
}
private void blockScreenOn() {
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 3a6333099b77..88c24e0a7eff 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -842,7 +842,8 @@ final class LocalDisplayAdapter extends DisplayAdapter {
// We must tell sidekick/displayoffload to stop controlling the display
// before we can change its power mode, so do that first.
if (isDisplayOffloadEnabled) {
- if (displayOffloadSession != null) {
+ if (displayOffloadSession != null
+ && !DisplayOffloadSession.isSupportedOffloadState(state)) {
displayOffloadSession.stopOffload();
}
} else {
@@ -874,8 +875,8 @@ final class LocalDisplayAdapter extends DisplayAdapter {
// have a sidekick/displayoffload available, tell it now that it can take
// control.
if (isDisplayOffloadEnabled) {
- if (DisplayOffloadSession.isSupportedOffloadState(state)
- && displayOffloadSession != null) {
+ if (displayOffloadSession != null
+ && DisplayOffloadSession.isSupportedOffloadState(state)) {
displayOffloadSession.startOffload();
}
} else {
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 fab769e8bc4f..40e9198ea921 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
@@ -75,6 +75,6 @@ abstract class BrightnessClamper<T> {
protected enum Type {
THERMAL,
POWER,
- BEDTIME_MODE,
+ WEAR_BEDTIME_MODE,
}
}
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 2c02fc610a51..18e8fab54e3e 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
@@ -38,6 +38,7 @@ import com.android.server.display.DisplayDeviceConfig.PowerThrottlingConfigData;
import com.android.server.display.DisplayDeviceConfig.PowerThrottlingData;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
import com.android.server.display.brightness.BrightnessReason;
+import com.android.server.display.config.SensorData;
import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.display.feature.DisplayManagerFlags;
@@ -156,6 +157,8 @@ public class BrightnessClamperController {
return BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL;
} else if (mClamperType == Type.POWER) {
return BrightnessInfo.BRIGHTNESS_MAX_REASON_POWER_IC;
+ } else if (mClamperType == Type.WEAR_BEDTIME_MODE) {
+ return BrightnessInfo.BRIGHTNESS_MAX_REASON_WEAR_BEDTIME_MODE;
} else {
Slog.wtf(TAG, "BrightnessMaxReason not mapped for type=" + mClamperType);
return BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
@@ -334,5 +337,10 @@ public class BrightnessClamperController {
public float getBrightnessWearBedtimeModeCap() {
return mDisplayDeviceConfig.getBrightnessCapForWearBedtimeMode();
}
+
+ @NonNull
+ public SensorData getTempSensor() {
+ return mDisplayDeviceConfig.getTempSensor();
+ }
}
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java
index 944a8a65693b..449825831182 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java
@@ -35,8 +35,10 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel;
+import com.android.server.display.config.SensorData;
import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.display.utils.DeviceConfigParsingUtils;
+import com.android.server.display.utils.SensorUtils;
import java.io.PrintWriter;
import java.util.List;
@@ -49,9 +51,8 @@ class BrightnessThermalClamper extends
BrightnessClamper<BrightnessThermalClamper.ThermalData> {
private static final String TAG = "BrightnessThermalClamper";
-
- @Nullable
- private final IThermalService mThermalService;
+ @NonNull
+ private final ThermalStatusObserver mThermalStatusObserver;
@NonNull
private final DeviceConfigParameterProvider mConfigParameterProvider;
// data from DeviceConfig, for all displays, for all dataSets
@@ -66,7 +67,6 @@ class BrightnessThermalClamper extends
// otherwise mDataFromDeviceConfig
@Nullable
private ThermalBrightnessThrottlingData mThermalThrottlingDataActive = null;
- private boolean mStarted = false;
@Nullable
private String mUniqueDisplayId = null;
@Nullable
@@ -74,14 +74,6 @@ class BrightnessThermalClamper extends
@Temperature.ThrottlingStatus
private int mThrottlingStatus = Temperature.THROTTLING_NONE;
- private final IThermalEventListener mThermalEventListener = new IThermalEventListener.Stub() {
- @Override
- public void notifyThrottling(Temperature temperature) {
- @Temperature.ThrottlingStatus int status = temperature.getStatus();
- mHandler.post(() -> thermalStatusChanged(status));
- }
- };
-
private final BiFunction<String, String, ThrottlingLevel> mDataPointMapper = (key, value) -> {
try {
int status = DeviceConfigParsingUtils.parseThermalStatus(key);
@@ -105,12 +97,11 @@ class BrightnessThermalClamper extends
BrightnessThermalClamper(Injector injector, Handler handler,
ClamperChangeListener listener, ThermalData thermalData) {
super(handler, listener);
- mThermalService = injector.getThermalService();
mConfigParameterProvider = injector.getDeviceConfigParameterProvider();
+ mThermalStatusObserver = new ThermalStatusObserver(injector, handler);
mHandler.post(() -> {
setDisplayData(thermalData);
loadOverrideData();
- start();
});
}
@@ -139,32 +130,19 @@ class BrightnessThermalClamper extends
@Override
void stop() {
- if (!mStarted) {
- return;
- }
- try {
- mThermalService.unregisterThermalEventListener(mThermalEventListener);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to unregister thermal status listener", e);
- }
- mStarted = false;
+ mThermalStatusObserver.stopObserving();
}
@Override
void dump(PrintWriter writer) {
writer.println("BrightnessThermalClamper:");
- writer.println(" mStarted: " + mStarted);
- if (mThermalService != null) {
- writer.println(" ThermalService available");
- } else {
- writer.println(" ThermalService not available");
- }
writer.println(" mThrottlingStatus: " + mThrottlingStatus);
writer.println(" mUniqueDisplayId: " + mUniqueDisplayId);
writer.println(" mDataId: " + mDataId);
writer.println(" mDataOverride: " + mThermalThrottlingDataOverride);
writer.println(" mDataFromDeviceConfig: " + mThermalThrottlingDataFromDeviceConfig);
writer.println(" mDataActive: " + mThermalThrottlingDataActive);
+ mThermalStatusObserver.dump(writer);
super.dump(writer);
}
@@ -193,6 +171,7 @@ class BrightnessThermalClamper extends
Slog.wtf(TAG,
"Thermal throttling data is missing for thermalThrottlingDataId=" + mDataId);
}
+ mThermalStatusObserver.registerSensor(data.getTempSensor());
}
private void recalculateBrightnessCap() {
@@ -226,19 +205,91 @@ class BrightnessThermalClamper extends
}
}
- private void start() {
- if (mThermalService == null) {
- Slog.e(TAG, "Could not observe thermal status. Service not available");
- return;
+
+ private final class ThermalStatusObserver extends IThermalEventListener.Stub {
+ private final Injector mInjector;
+ private final Handler mHandler;
+ private IThermalService mThermalService;
+ private boolean mStarted;
+ private SensorData mObserverTempSensor;
+
+ ThermalStatusObserver(Injector injector, Handler handler) {
+ mInjector = injector;
+ mHandler = handler;
+ mStarted = false;
}
- try {
- // We get a callback immediately upon registering so there's no need to query
- // for the current value.
- mThermalService.registerThermalEventListenerWithType(mThermalEventListener,
- Temperature.TYPE_SKIN);
- mStarted = true;
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to register thermal status listener", e);
+
+ void registerSensor(SensorData tempSensor) {
+ if (!mStarted || mObserverTempSensor == null) {
+ mObserverTempSensor = tempSensor;
+ registerThermalListener();
+ return;
+ }
+
+ String curType = mObserverTempSensor.type;
+ mObserverTempSensor = tempSensor;
+ if (curType.equals(tempSensor.type)) {
+ Slog.d(TAG, "Thermal status observer already started");
+ return;
+ }
+ stopObserving();
+ registerThermalListener();
+ }
+
+ void registerThermalListener() {
+ mThermalService = mInjector.getThermalService();
+ if (mThermalService == null) {
+ Slog.e(TAG, "Could not observe thermal status. Service not available");
+ return;
+ }
+ int temperatureType = SensorUtils.getSensorTemperatureType(mObserverTempSensor);
+ try {
+ // We get a callback immediately upon registering so there's no need to query
+ // for the current value.
+ mThermalService.registerThermalEventListenerWithType(this, temperatureType);
+ mStarted = true;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to register thermal status listener", e);
+ }
+ }
+
+ @Override
+ public void notifyThrottling(Temperature temp) {
+ Slog.d(TAG, "New thermal throttling status = " + temp.getStatus());
+ if (mObserverTempSensor.name != null
+ && !mObserverTempSensor.name.equals(temp.getName())) {
+ Slog.i(TAG, "Skipping thermal throttling notification as monitored sensor: "
+ + mObserverTempSensor.name
+ + " != notified sensor: "
+ + temp.getName());
+ return;
+ }
+ @Temperature.ThrottlingStatus int status = temp.getStatus();
+ mHandler.post(() -> thermalStatusChanged(status));
+ }
+
+ void stopObserving() {
+ if (!mStarted) {
+ return;
+ }
+ try {
+ mThermalService.unregisterThermalEventListener(this);
+ mStarted = false;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to unregister thermal status listener", e);
+ }
+ mThermalService = null;
+ }
+
+ void dump(PrintWriter writer) {
+ writer.println(" ThermalStatusObserver:");
+ writer.println(" mStarted: " + mStarted);
+ writer.println(" mObserverTempSensor: " + mObserverTempSensor);
+ if (mThermalService != null) {
+ writer.println(" ThermalService available");
+ } else {
+ writer.println(" ThermalService not available");
+ }
}
}
@@ -251,6 +302,9 @@ class BrightnessThermalClamper extends
@Nullable
ThermalBrightnessThrottlingData getThermalBrightnessThrottlingData();
+
+ @NonNull
+ SensorData getTempSensor();
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java
index 7e853bfc4ad6..1902e35ed397 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java
@@ -64,7 +64,7 @@ public class BrightnessWearBedtimeModeClamper extends
@NonNull
@Override
Type getType() {
- return Type.BEDTIME_MODE;
+ return Type.WEAR_BEDTIME_MODE;
}
@Override
diff --git a/services/core/java/com/android/server/display/config/SensorData.java b/services/core/java/com/android/server/display/config/SensorData.java
index 3bb35bf7c49f..8e716f8380b6 100644
--- a/services/core/java/com/android/server/display/config/SensorData.java
+++ b/services/core/java/com/android/server/display/config/SensorData.java
@@ -22,6 +22,7 @@ import android.content.res.Resources;
import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.feature.DisplayManagerFlags;
import java.util.ArrayList;
import java.util.Collections;
@@ -32,6 +33,9 @@ import java.util.List;
*/
public class SensorData {
+ public static final String TEMPERATURE_TYPE_DISPLAY = "DISPLAY";
+ public static final String TEMPERATURE_TYPE_SKIN = "SKIN";
+
@Nullable
public final String type;
@Nullable
@@ -143,6 +147,32 @@ public class SensorData {
}
/**
+ * Loads temperature sensor data for no config case. (Type: SKIN, Name: null)
+ */
+ public static SensorData loadTempSensorUnspecifiedConfig() {
+ return new SensorData(TEMPERATURE_TYPE_SKIN, null);
+ }
+
+ /**
+ * Loads temperature sensor data from given display config.
+ * If empty or null config given default to (Type: SKIN, Name: null)
+ */
+ public static SensorData loadTempSensorConfig(DisplayManagerFlags flags,
+ DisplayConfiguration config) {
+ SensorDetails sensorDetails = config.getTempSensor();
+ if (!flags.isSensorBasedBrightnessThrottlingEnabled() || sensorDetails == null) {
+ return new SensorData(TEMPERATURE_TYPE_SKIN, null);
+ }
+ String name = sensorDetails.getName();
+ String type = sensorDetails.getType();
+ if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) {
+ type = TEMPERATURE_TYPE_SKIN;
+ name = null;
+ }
+ return new SensorData(type, name);
+ }
+
+ /**
* Loads sensor unspecified config, this means system should use default sensor.
* See also {@link com.android.server.display.utils.SensorUtils}
*/
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index 1ae255933f66..516d4b1d4125 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -121,6 +121,11 @@ public class DisplayManagerFlags {
Flags::refreshRateVotingTelemetry
);
+ private final FlagState mSensorBasedBrightnessThrottling = new FlagState(
+ Flags.FLAG_SENSOR_BASED_BRIGHTNESS_THROTTLING,
+ Flags::sensorBasedBrightnessThrottling
+ );
+
/**
* @return {@code true} if 'port' is allowed in display layout configuration file.
*/
@@ -247,6 +252,10 @@ public class DisplayManagerFlags {
return mRefreshRateVotingTelemetry.isEnabled();
}
+ public boolean isSensorBasedBrightnessThrottlingEnabled() {
+ return mSensorBasedBrightnessThrottling.isEnabled();
+ }
+
/**
* dumps all flagstates
* @param pw printWriter
@@ -270,6 +279,7 @@ public class DisplayManagerFlags {
pw.println(" " + mAutoBrightnessModesFlagState);
pw.println(" " + mFastHdrTransitions);
pw.println(" " + mRefreshRateVotingTelemetry);
+ pw.println(" " + mSensorBasedBrightnessThrottling);
}
private static class FlagState {
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index c2f52b5ad8a0..63ab3a95822f 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -184,3 +184,11 @@ flag {
bug: "310029108"
is_fixed_read_only: true
}
+
+flag {
+ name: "sensor_based_brightness_throttling"
+ namespace: "display_manager"
+ description: "Feature flag for enabling brightness throttling using sensor from config."
+ bug: "294900859"
+ is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/display/utils/SensorUtils.java b/services/core/java/com/android/server/display/utils/SensorUtils.java
index 8b9fe1083187..c63473a4b3d7 100644
--- a/services/core/java/com/android/server/display/utils/SensorUtils.java
+++ b/services/core/java/com/android/server/display/utils/SensorUtils.java
@@ -16,9 +16,11 @@
package com.android.server.display.utils;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.Sensor;
import android.hardware.SensorManager;
+import android.os.Temperature;
import android.text.TextUtils;
import com.android.server.display.config.SensorData;
@@ -70,4 +72,17 @@ public class SensorUtils {
return null;
}
+ /**
+ * Convert string temperature type to its corresponding integer value.
+ */
+ public static int getSensorTemperatureType(@NonNull SensorData tempSensor) {
+ if (tempSensor.type.equalsIgnoreCase(SensorData.TEMPERATURE_TYPE_DISPLAY)) {
+ return Temperature.TYPE_DISPLAY;
+ } else if (tempSensor.type.equalsIgnoreCase(SensorData.TEMPERATURE_TYPE_SKIN)) {
+ return Temperature.TYPE_SKIN;
+ }
+ throw new IllegalArgumentException(
+ "tempSensor doesn't support type: " + tempSensor.type);
+ }
+
}
diff --git a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionManagerInternal.java b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionManagerInternal.java
index 6a6e6ab23687..c2c82edee33d 100644
--- a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionManagerInternal.java
+++ b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionManagerInternal.java
@@ -16,6 +16,7 @@
package com.android.server.grammaticalinflection;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.Configuration;
@@ -55,11 +56,11 @@ public abstract class GrammaticalInflectionManagerInternal {
*
*/
public abstract @Configuration.GrammaticalGender int retrieveSystemGrammaticalGender(
- Configuration configuration);
+ @NonNull Configuration configuration);
/**
* Whether the package can get the system grammatical gender or not.
*/
- public abstract boolean canGetSystemGrammaticalGender(int uid, String packageName);
+ public abstract boolean canGetSystemGrammaticalGender(int uid, @Nullable String packageName);
}
diff --git a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java
index d01f54f09679..252ea4b08464 100644
--- a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java
+++ b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java
@@ -222,7 +222,7 @@ public class GrammaticalInflectionService extends SystemService {
}
final int uid = mPackageManagerInternal.getPackageUid(appPackageName, 0, userId);
- FrameworkStatsLog.write(FrameworkStatsLog.GRAMMATICAL_INFLECTION_CHANGED,
+ FrameworkStatsLog.write(FrameworkStatsLog.APPLICATION_GRAMMATICAL_INFLECTION_CHANGED,
FrameworkStatsLog.APPLICATION_GRAMMATICAL_INFLECTION_CHANGED__SOURCE_ID__OTHERS,
uid,
gender != GRAMMATICAL_GENDER_NOT_SPECIFIED,
@@ -266,8 +266,14 @@ public class GrammaticalInflectionService extends SystemService {
try {
Configuration config = new Configuration();
+ int preValue = config.getGrammaticalGender();
config.setGrammaticalGender(grammaticalGender);
ActivityTaskManager.getService().updateConfiguration(config);
+ FrameworkStatsLog.write(FrameworkStatsLog.SYSTEM_GRAMMATICAL_INFLECTION_CHANGED,
+ FrameworkStatsLog.SYSTEM_GRAMMATICAL_INFLECTION_CHANGED__SOURCE_ID__SYSTEM,
+ userId,
+ grammaticalGender != GRAMMATICAL_GENDER_NOT_SPECIFIED,
+ preValue != GRAMMATICAL_GENDER_NOT_SPECIFIED);
} catch (RemoteException e) {
Log.w(TAG, "Can not update configuration", e);
}
@@ -329,8 +335,9 @@ public class GrammaticalInflectionService extends SystemService {
private void checkCallerIsSystem() {
int callingUid = Binder.getCallingUid();
- if (callingUid != Process.SYSTEM_UID && callingUid != Process.SHELL_UID) {
- throw new SecurityException("Caller is not system and shell.");
+ if (callingUid != Process.SYSTEM_UID && callingUid != Process.SHELL_UID
+ && callingUid != Process.ROOT_UID) {
+ throw new SecurityException("Caller is not system, shell and root.");
}
}
@@ -354,12 +361,11 @@ public class GrammaticalInflectionService extends SystemService {
final File file = getGrammaticalGenderFile(userId);
synchronized (mLock) {
if (!file.exists()) {
- Log.d(TAG, "User " + userId + "doesn't have the grammatical gender file.");
+ Log.d(TAG, "User " + userId + " doesn't have the grammatical gender file.");
return;
}
if (mGrammaticalGenderCache.indexOfKey(userId) < 0) {
- try {
- InputStream in = new FileInputStream(file);
+ try (FileInputStream in = new FileInputStream(file)) {
final TypedXmlPullParser parser = Xml.resolvePullParser(in);
mGrammaticalGenderCache.put(userId, getGrammaticalGenderFromXml(parser));
} catch (IOException | XmlPullParserException e) {
diff --git a/services/core/java/com/android/server/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java
index b963a4b614e8..7e190ddbb24f 100644
--- a/services/core/java/com/android/server/input/InputManagerInternal.java
+++ b/services/core/java/com/android/server/input/InputManagerInternal.java
@@ -61,20 +61,26 @@ public abstract class InputManagerInternal {
public abstract void setPulseGestureEnabled(boolean enabled);
/**
- * Atomically transfers touch focus from one window to another as identified by
- * their input channels. It is possible for multiple windows to have
- * touch focus if they support split touch dispatch
- * {@link android.view.WindowManager.LayoutParams#FLAG_SPLIT_TOUCH} but this
- * method only transfers touch focus of the specified window without affecting
- * other windows that may also have touch focus at the same time.
+ * Atomically transfers an active touch gesture from one window to another, as identified by
+ * their input channels.
*
- * @param fromChannelToken The channel token of a window that currently has touch focus.
- * @param toChannelToken The channel token of the window that should receive touch focus in
- * place of the first.
- * @return {@code true} if the transfer was successful. {@code false} if the window with the
- * specified channel did not actually have touch focus at the time of the request.
+ * <p>Only the touch gesture that is currently being dispatched to a window associated with
+ * {@code fromChannelToken} will be effected. That window will no longer receive
+ * the touch gesture (i.e. it will receive {@link android.view.MotionEvent#ACTION_CANCEL}).
+ * A window associated with the {@code toChannelToken} will receive the rest of the gesture
+ * (i.e. beginning with {@link android.view.MotionEvent#ACTION_DOWN} or
+ * {@link android.view.MotionEvent#ACTION_POINTER_DOWN}).
+ *
+ * <p>Transferring touch gestures will have no impact on focused windows. If the {@code
+ * toChannelToken} window is focusable, this will not bring focus to that window.
+ *
+ * @param fromChannelToken The channel token of a window that has an active touch gesture.
+ * @param toChannelToken The channel token of the window that should receive the gesture in
+ * place of the first.
+ * @return True if the transfer was successful. False if the specified windows don't exist, or
+ * if the source window is not actively receiving a touch gesture at the time of the request.
*/
- public abstract boolean transferTouchFocus(@NonNull IBinder fromChannelToken,
+ public abstract boolean transferTouchGesture(@NonNull IBinder fromChannelToken,
@NonNull IBinder toChannelToken);
/**
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 574be34e56e5..dbef427e23a7 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -86,7 +86,9 @@ import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
import android.view.Display;
+import android.view.DisplayInfo;
import android.view.IInputFilter;
import android.view.IInputFilterHost;
import android.view.IInputMonitorHost;
@@ -115,6 +117,7 @@ import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
import com.android.server.DisplayThread;
import com.android.server.LocalServices;
+import com.android.server.UiThread;
import com.android.server.Watchdog;
import com.android.server.input.InputManagerInternal.LidSwitchCallback;
import com.android.server.input.debug.FocusEventDebugView;
@@ -415,11 +418,15 @@ public class InputManagerService extends IInputManager.Stub
boolean mUseLargePointerIcons = false;
@GuardedBy("mLoadedPointerIconsByDisplayAndType")
final SparseArray<Context> mDisplayContexts = new SparseArray<>();
+ @GuardedBy("mLoadedPointerIconsByDisplayAndType")
+ final SparseIntArray mDisplayDensities = new SparseIntArray();
final DisplayManager.DisplayListener mDisplayListener = new DisplayManager.DisplayListener() {
@Override
public void onDisplayAdded(int displayId) {
-
+ synchronized (mLoadedPointerIconsByDisplayAndType) {
+ updateDisplayDensity(displayId);
+ }
}
@Override
@@ -427,14 +434,20 @@ public class InputManagerService extends IInputManager.Stub
synchronized (mLoadedPointerIconsByDisplayAndType) {
mLoadedPointerIconsByDisplayAndType.remove(displayId);
mDisplayContexts.remove(displayId);
+ mDisplayDensities.delete(displayId);
}
}
@Override
public void onDisplayChanged(int displayId) {
synchronized (mLoadedPointerIconsByDisplayAndType) {
- // The display density could have changed, so force all cached pointer icons to be
+ if (!updateDisplayDensity(displayId)) {
+ return;
+ }
+ // The display density changed, so force all cached pointer icons to be
// reloaded for the display.
+ Slog.i(TAG,
+ "Reloading pointer icons due to density change on display: " + displayId);
var iconsByType = mLoadedPointerIconsByDisplayAndType.get(displayId);
if (iconsByType == null) {
return;
@@ -444,6 +457,26 @@ public class InputManagerService extends IInputManager.Stub
}
mNative.reloadPointerIcons();
}
+
+ // Updates the cached display density for the given displayId, and returns true if
+ // the cached density changed.
+ @GuardedBy("mLoadedPointerIconsByDisplayAndType")
+ private boolean updateDisplayDensity(int displayId) {
+ final DisplayManager displayManager = Objects.requireNonNull(
+ mContext.getSystemService(DisplayManager.class));
+ final Display display = displayManager.getDisplay(displayId);
+ if (display == null) {
+ return false;
+ }
+ DisplayInfo info = new DisplayInfo();
+ display.getDisplayInfo(info);
+ final int oldDensity = mDisplayDensities.get(displayId, 0 /* default */);
+ if (oldDensity == info.logicalDensityDpi) {
+ return false;
+ }
+ mDisplayDensities.put(displayId, info.logicalDensityDpi);
+ return true;
+ }
};
/** Point of injection for test dependencies. */
@@ -613,9 +646,13 @@ public class InputManagerService extends IInputManager.Stub
mWiredAccessoryCallbacks.systemReady();
}
- Objects.requireNonNull(
- mContext.getSystemService(DisplayManager.class)).registerDisplayListener(
- mDisplayListener, mHandler);
+ final DisplayManager displayManager = Objects.requireNonNull(
+ mContext.getSystemService(DisplayManager.class));
+ displayManager.registerDisplayListener(mDisplayListener, UiThread.getHandler());
+ final Display[] displays = displayManager.getDisplays();
+ for (int i = 0; i < displays.length; i++) {
+ mDisplayListener.onDisplayAdded(displays[i].getDisplayId());
+ }
mKeyboardLayoutManager.systemRunning();
mBatteryController.systemRunning();
@@ -737,7 +774,9 @@ public class InputManagerService extends IInputManager.Stub
* @param destChannelToken The token of the window or input channel that should receive the
* gesture
* @return True if the transfer succeeded, false if there was no active touch gesture happening
+ * @deprecated Use {@link #transferTouchGesture(IBinder, IBinder)}
*/
+ @Deprecated
public boolean transferTouch(IBinder destChannelToken, int displayId) {
// TODO(b/162194035): Replace this with a SPY window
Objects.requireNonNull(destChannelToken, "destChannelToken must not be null");
@@ -1343,43 +1382,44 @@ public class InputManagerService extends IInputManager.Stub
}
/**
- * Atomically transfers touch focus from one window to another as identified by
- * their input channels. It is possible for multiple windows to have
- * touch focus if they support split touch dispatch
- * {@link android.view.WindowManager.LayoutParams#FLAG_SPLIT_TOUCH} but this
- * method only transfers touch focus of the specified window without affecting
- * other windows that may also have touch focus at the same time.
- * @param fromChannel The channel of a window that currently has touch focus.
- * @param toChannel The channel of the window that should receive touch focus in
- * place of the first.
- * @param isDragDrop True if transfer touch focus for drag and drop.
- * @return True if the transfer was successful. False if the window with the
- * specified channel did not actually have touch focus at the time of the request.
+ * Start drag and drop.
+ *
+ * @param fromChannel The input channel that is currently receiving a touch gesture that should
+ * be turned into the drag pointer.
+ * @param dragAndDropChannel The input channel associated with the system drag window.
+ * @return true if drag and drop was successfully started, false otherwise.
*/
- public boolean transferTouchFocus(@NonNull InputChannel fromChannel,
- @NonNull InputChannel toChannel, boolean isDragDrop) {
- return mNative.transferTouchFocus(fromChannel.getToken(), toChannel.getToken(),
- isDragDrop);
+ public boolean startDragAndDrop(@NonNull InputChannel fromChannel,
+ @NonNull InputChannel dragAndDropChannel) {
+ return mNative.transferTouchGesture(fromChannel.getToken(), dragAndDropChannel.getToken(),
+ true /* isDragDrop */);
}
/**
- * Atomically transfers touch focus from one window to another as identified by
- * their input channels. It is possible for multiple windows to have
- * touch focus if they support split touch dispatch
- * {@link android.view.WindowManager.LayoutParams#FLAG_SPLIT_TOUCH} but this
- * method only transfers touch focus of the specified window without affecting
- * other windows that may also have touch focus at the same time.
- * @param fromChannelToken The channel token of a window that currently has touch focus.
- * @param toChannelToken The channel token of the window that should receive touch focus in
- * place of the first.
- * @return True if the transfer was successful. False if the window with the
- * specified channel did not actually have touch focus at the time of the request.
+ * Atomically transfers an active touch gesture from one window to another, as identified by
+ * their input channels.
+ *
+ * <p>Only the touch gesture that is currently being dispatched to a window associated with
+ * {@code fromChannelToken} will be effected. That window will no longer receive
+ * the touch gesture (i.e. it will receive {@link android.view.MotionEvent#ACTION_CANCEL}).
+ * A window associated with the {@code toChannelToken} will receive the rest of the gesture
+ * (i.e. beginning with {@link android.view.MotionEvent#ACTION_DOWN} or
+ * {@link android.view.MotionEvent#ACTION_POINTER_DOWN}).
+ *
+ * <p>Transferring touch gestures will have no impact on focused windows. If the {@code
+ * toChannelToken} window is focusable, this will not bring focus to that window.
+ *
+ * @param fromChannelToken The channel token of a window that has an active touch gesture.
+ * @param toChannelToken The channel token of the window that should receive the gesture in
+ * place of the first.
+ * @return True if the transfer was successful. False if the specified windows don't exist, or
+ * if the source window is not actively receiving a touch gesture at the time of the request.
*/
- public boolean transferTouchFocus(@NonNull IBinder fromChannelToken,
+ public boolean transferTouchGesture(@NonNull IBinder fromChannelToken,
@NonNull IBinder toChannelToken) {
Objects.requireNonNull(fromChannelToken);
Objects.requireNonNull(toChannelToken);
- return mNative.transferTouchFocus(fromChannelToken, toChannelToken,
+ return mNative.transferTouchGesture(fromChannelToken, toChannelToken,
false /* isDragDrop */);
}
@@ -3312,9 +3352,9 @@ public class InputManagerService extends IInputManager.Stub
}
@Override
- public boolean transferTouchFocus(@NonNull IBinder fromChannelToken,
+ public boolean transferTouchGesture(@NonNull IBinder fromChannelToken,
@NonNull IBinder toChannelToken) {
- return InputManagerService.this.transferTouchFocus(fromChannelToken, toChannelToken);
+ return InputManagerService.this.transferTouchGesture(fromChannelToken, toChannelToken);
}
@Override
@@ -3633,7 +3673,7 @@ public class InputManagerService extends IInputManager.Stub
// Clear all cached icons on all displays.
mLoadedPointerIconsByDisplayAndType.clear();
}
- mNative.reloadPointerIcons();
+ UiThread.getHandler().post(mNative::reloadPointerIcons);
}
interface KeyboardBacklightControllerInterface {
diff --git a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
index ebc784d763ef..4b9f2cf9d0c0 100644
--- a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
+++ b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
@@ -227,7 +227,12 @@ public final class KeyboardMetricsCollector {
"LAUNCH_DEFAULT_FITNESS"),
LAUNCH_APPLICATION_BY_PACKAGE_NAME(
FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_APPLICATION_BY_PACKAGE_NAME,
- "LAUNCH_APPLICATION_BY_PACKAGE_NAME");
+ "LAUNCH_APPLICATION_BY_PACKAGE_NAME"),
+ DESKTOP_MODE(
+ FrameworkStatsLog
+ .KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__DESKTOP_MODE,
+ "DESKTOP_MODE");
+
private final int mValue;
private final String mName;
diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java
index b16df0f8a28a..972a9e34f496 100644
--- a/services/core/java/com/android/server/input/NativeInputManagerService.java
+++ b/services/core/java/com/android/server/input/NativeInputManagerService.java
@@ -110,13 +110,15 @@ interface NativeInputManagerService {
void setMinTimeBetweenUserActivityPokes(long millis);
- boolean transferTouchFocus(IBinder fromChannelToken, IBinder toChannelToken,
+ boolean transferTouchGesture(IBinder fromChannelToken, IBinder toChannelToken,
boolean isDragDrop);
/**
* Transfer the current touch gesture to the window identified by 'destChannelToken' positioned
* on display with id 'displayId'.
+ * @deprecated Use {@link #transferTouchGesture(IBinder, IBinder, boolean)}
*/
+ @Deprecated
boolean transferTouch(IBinder destChannelToken, int displayId);
int getMousePointerSpeed();
@@ -359,10 +361,11 @@ interface NativeInputManagerService {
public native void setMinTimeBetweenUserActivityPokes(long millis);
@Override
- public native boolean transferTouchFocus(IBinder fromChannelToken, IBinder toChannelToken,
+ public native boolean transferTouchGesture(IBinder fromChannelToken, IBinder toChannelToken,
boolean isDragDrop);
@Override
+ @Deprecated
public native boolean transferTouch(IBinder destChannelToken, int displayId);
@Override
diff --git a/services/core/java/com/android/server/input/debug/FocusEventDebugView.java b/services/core/java/com/android/server/input/debug/FocusEventDebugView.java
index 6eec0dee9152..b30f5ecb2dd5 100644
--- a/services/core/java/com/android/server/input/debug/FocusEventDebugView.java
+++ b/services/core/java/com/android/server/input/debug/FocusEventDebugView.java
@@ -34,6 +34,7 @@ import android.util.Slog;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.InputDevice;
+import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.RoundedCorner;
@@ -312,7 +313,7 @@ public class FocusEventDebugView extends RelativeLayout {
case KeyEvent.KEYCODE_FORWARD_DEL:
return "\u2326";
case KeyEvent.KEYCODE_ESCAPE:
- return "ESC";
+ return "esc";
case KeyEvent.KEYCODE_DPAD_UP:
return "\u2191";
case KeyEvent.KEYCODE_DPAD_DOWN:
@@ -329,13 +330,29 @@ public class FocusEventDebugView extends RelativeLayout {
return "\u2198";
case KeyEvent.KEYCODE_DPAD_DOWN_LEFT:
return "\u2199";
+ case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+ return "\u23ef";
+ case KeyEvent.KEYCODE_HOME:
+ return "\u25ef";
+ case KeyEvent.KEYCODE_BACK:
+ return "\u25c1";
+ case KeyEvent.KEYCODE_RECENT_APPS:
+ return "\u25a1";
default:
break;
}
final int unicodeChar = event.getUnicodeChar();
if (unicodeChar != 0) {
- return new String(Character.toChars(unicodeChar));
+ if ((unicodeChar & KeyCharacterMap.COMBINING_ACCENT) != 0) {
+ // Show combining character
+ final int combiningChar = KeyCharacterMap.getCombiningChar(
+ unicodeChar & KeyCharacterMap.COMBINING_ACCENT_MASK);
+ // Return the Unicode dotted circle as part of the label as it is used is used to
+ // illustrate the effect of a combining marks
+ return "\u25cc" + String.valueOf((char) combiningChar);
+ }
+ return String.valueOf((char) unicodeChar);
}
final var label = KeyEvent.keyCodeToString(event.getKeyCode());
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java
index 977dbff0b02e..7251ac42c582 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java
@@ -43,6 +43,9 @@ import com.android.internal.inputmethod.InputBindResult;
* the given {@link Handler} thread if {@link IInputMethodClient} is not a proxy object. Be careful
* about its call ordering characteristics.</p>
*/
+// TODO(b/322895594) Mark this class to be host side test compatible once enabling fw/services in
+// Ravenwood (mark this class with @RavenwoodKeepWholeClass and #create with @RavenwoodReplace,
+// so Ravenwood can properly swap create method during test execution).
final class IInputMethodClientInvoker {
private static final String TAG = InputMethodManagerService.TAG;
private static final boolean DEBUG = InputMethodManagerService.DEBUG;
@@ -64,6 +67,16 @@ final class IInputMethodClientInvoker {
return new IInputMethodClientInvoker(inputMethodClient, isProxy, isProxy ? null : handler);
}
+ @AnyThread
+ @Nullable
+ static IInputMethodClientInvoker create$ravenwood(
+ @Nullable IInputMethodClient inputMethodClient, @NonNull Handler handler) {
+ if (inputMethodClient == null) {
+ return null;
+ }
+ return new IInputMethodClientInvoker(inputMethodClient, true, null);
+ }
+
private IInputMethodClientInvoker(@NonNull IInputMethodClient target,
boolean isProxy, @Nullable Handler handler) {
mTarget = target;
@@ -117,6 +130,30 @@ final class IInputMethodClientInvoker {
}
@AnyThread
+ void onStartInputResult(@NonNull InputBindResult res, int startInputSeq) {
+ if (mIsProxy) {
+ onStartInputResultInternal(res, startInputSeq);
+ } else {
+ mHandler.post(() -> onStartInputResultInternal(res, startInputSeq));
+ }
+ }
+
+ @AnyThread
+ private void onStartInputResultInternal(@NonNull InputBindResult res, int startInputSeq) {
+ try {
+ mTarget.onStartInputResult(res, startInputSeq);
+ } catch (RemoteException e) {
+ logRemoteException(e);
+ } finally {
+ // Dispose the channel if the input method is not local to this process
+ // because the remote proxy will get its own copy when unparceled.
+ if (res.channel != null && mIsProxy) {
+ res.channel.dispose();
+ }
+ }
+ }
+
+ @AnyThread
void onBindAccessibilityService(@NonNull InputBindResult res, int id) {
if (mIsProxy) {
onBindAccessibilityServiceInternal(res, id);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 4bb8e1913199..76956c88695d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -148,6 +148,7 @@ import com.android.internal.content.PackageMonitor;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.inputmethod.DirectBootAwareness;
import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
+import com.android.internal.inputmethod.IBooleanListener;
import com.android.internal.inputmethod.IConnectionlessHandwritingCallback;
import com.android.internal.inputmethod.IImeTracker;
import com.android.internal.inputmethod.IInlineSuggestionsRequestCallback;
@@ -1539,7 +1540,13 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@Override
public void onStart() {
mService.publishLocalService();
- publishBinderService(Context.INPUT_METHOD_SERVICE, mService, false /*allowIsolated*/,
+ IInputMethodManager.Stub service;
+ if (Flags.useZeroJankProxy()) {
+ service = new ZeroJankProxy(mService.mHandler::post, mService);
+ } else {
+ service = mService;
+ }
+ publishBinderService(Context.INPUT_METHOD_SERVICE, service, false /*allowIsolated*/,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PROTO);
}
@@ -2216,6 +2223,14 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
}
+ @Nullable
+ ClientState getClientState(IInputMethodClient client) {
+ synchronized (ImfLock.class) {
+ return mClientController.getClient(client.asBinder());
+ }
+ }
+
+ // TODO(b/314150112): Move this to ClientController.
@GuardedBy("ImfLock.class")
void unbindCurrentClientLocked(@UnbindReason int unbindClientReason) {
if (mCurClient != null) {
@@ -2421,20 +2436,17 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
return InputBindResult.NOT_IME_TARGET_WINDOW;
}
final int csDisplayId = cs.mSelfReportedDisplayId;
- final int oldDisplayIdToShowIme = mDisplayIdToShowIme;
mDisplayIdToShowIme = mVisibilityStateComputer.computeImeDisplayId(winState, csDisplayId);
// Potentially override the selected input method if the new display belongs to a virtual
// device with a custom IME.
String selectedMethodId = getSelectedMethodIdLocked();
- if (oldDisplayIdToShowIme != mDisplayIdToShowIme) {
- final String deviceMethodId = computeCurrentDeviceMethodIdLocked(selectedMethodId);
- if (deviceMethodId == null) {
- mVisibilityStateComputer.getImePolicy().setImeHiddenByDisplayPolicy(true);
- } else if (!Objects.equals(deviceMethodId, selectedMethodId)) {
- setInputMethodLocked(deviceMethodId, NOT_A_SUBTYPE_ID, mDeviceIdToShowIme);
- selectedMethodId = deviceMethodId;
- }
+ final String deviceMethodId = computeCurrentDeviceMethodIdLocked(selectedMethodId);
+ if (deviceMethodId == null) {
+ mVisibilityStateComputer.getImePolicy().setImeHiddenByDisplayPolicy(true);
+ } else if (!Objects.equals(deviceMethodId, selectedMethodId)) {
+ setInputMethodLocked(deviceMethodId, NOT_A_SUBTYPE_ID, mDeviceIdToShowIme);
+ selectedMethodId = deviceMethodId;
}
if (mVisibilityStateComputer.getImePolicy().isImeHiddenByDisplayPolicy()) {
@@ -2534,10 +2546,10 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
final int oldDeviceId = mDeviceIdToShowIme;
mDeviceIdToShowIme = mVdmInternal.getDeviceIdForDisplayId(mDisplayIdToShowIme);
- if (mDeviceIdToShowIme == oldDeviceId) {
- return currentMethodId;
- }
if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) {
+ if (oldDeviceId == DEVICE_ID_DEFAULT) {
+ return currentMethodId;
+ }
final String defaultDeviceMethodId = mSettings.getSelectedDefaultDeviceInputMethod();
if (DEBUG) {
Slog.v(TAG, "Restoring default device input method: " + defaultDeviceMethodId);
@@ -2901,16 +2913,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@GuardedBy("ImfLock.class")
void clearClientSessionsLocked() {
if (getCurMethodLocked() != null) {
- // TODO(b/322816970): Replace this with lambda.
- mClientController.forAllClients(new Consumer<ClientState>() {
-
- @GuardedBy("ImfLock.class")
- @Override
- public void accept(ClientState c) {
- clearClientSessionLocked(c);
- clearClientSessionForAccessibilityLocked(c);
- }
- });
+ // TODO(b/324907325): Remove the suppress warnings once b/324907325 is fixed.
+ @SuppressWarnings("GuardedBy") Consumer<ClientState> clearClientSession = c -> {
+ clearClientSessionLocked(c);
+ clearClientSessionForAccessibilityLocked(c);
+ };
+ mClientController.forAllClients(clearClientSession);
finishSessionLocked(mEnabledSession);
for (int i = 0; i < mEnabledAccessibilitySessions.size(); i++) {
@@ -3534,6 +3542,19 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@Override
+ public void acceptStylusHandwritingDelegationAsync(
+ @NonNull IInputMethodClient client,
+ @UserIdInt int userId,
+ @NonNull String delegatePackageName,
+ @NonNull String delegatorPackageName,
+ @InputMethodManager.HandwritingDelegateFlags int flags, IBooleanListener callback)
+ throws RemoteException {
+ boolean result = acceptStylusHandwritingDelegation(
+ client, userId, delegatePackageName, delegatorPackageName, flags);
+ callback.onResult(result);
+ }
+
+ @Override
public boolean acceptStylusHandwritingDelegation(
@NonNull IInputMethodClient client,
@UserIdInt int userId,
@@ -3745,6 +3766,20 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
return imeClientFocus == WindowManagerInternal.ImeClientFocusResult.HAS_IME_FOCUS;
}
+ //TODO(b/293640003): merge with startInputOrWindowGainedFocus once Flags.useZeroJankProxy()
+ // is enabled.
+ @Override
+ public void startInputOrWindowGainedFocusAsync(
+ @StartInputReason int startInputReason, IInputMethodClient client, IBinder windowToken,
+ @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
+ int windowFlags, @Nullable EditorInfo editorInfo,
+ IRemoteInputConnection inputConnection,
+ IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
+ int unverifiedTargetSdkVersion, @UserIdInt int userId,
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq) {
+ // implemented by ZeroJankProxy
+ }
+
@NonNull
@Override
public InputBindResult startInputOrWindowGainedFocus(
@@ -4653,15 +4688,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
super.startImeTrace_enforcePermission();
ImeTracing.getInstance().startTrace(null /* printwriter */);
synchronized (ImfLock.class) {
- // TODO(b/322816970): Replace this with lambda.
- mClientController.forAllClients(new Consumer<ClientState>() {
-
- @GuardedBy("ImfLock.class")
- @Override
- public void accept(ClientState c) {
- c.mClient.setImeTraceEnabled(true /* enabled */);
- }
- });
+ mClientController.forAllClients(c -> c.mClient.setImeTraceEnabled(true /* enabled */));
}
}
@@ -4673,15 +4700,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
ImeTracing.getInstance().stopTrace(null /* printwriter */);
synchronized (ImfLock.class) {
- // TODO(b/322816970): Replace this with lambda.
- mClientController.forAllClients(new Consumer<ClientState>() {
-
- @GuardedBy("ImfLock.class")
- @Override
- public void accept(ClientState c) {
- c.mClient.setImeTraceEnabled(false /* enabled */);
- }
- });
+ mClientController.forAllClients(c -> c.mClient.setImeTraceEnabled(false /* enabled */));
}
}
@@ -5828,7 +5847,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
curHostInputToken = mCurHostInputToken;
}
- return mInputManagerInternal.transferTouchFocus(sourceInputToken, curHostInputToken);
+ return mInputManagerInternal.transferTouchGesture(sourceInputToken, curHostInputToken);
}
@Override
@@ -5916,15 +5935,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
// We only have sessions when we bound to an input method. Remove this session
// from all clients.
if (getCurMethodLocked() != null) {
- // TODO(b/322816970): Replace this with lambda.
- mClientController.forAllClients(new Consumer<ClientState>() {
+ // TODO(b/324907325): Remove the suppress warnings once b/324907325 is fixed.
+ @SuppressWarnings("GuardedBy") Consumer<ClientState> clearClientSession =
+ c -> clearClientSessionForAccessibilityLocked(c,
+ accessibilityConnectionId);
+ mClientController.forAllClients(clearClientSession);
- @GuardedBy("ImfLock.class")
- @Override
- public void accept(ClientState c) {
- clearClientSessionForAccessibilityLocked(c, accessibilityConnectionId);
- }
- });
AccessibilitySessionState session = mEnabledAccessibilitySessions.get(
accessibilityConnectionId);
if (session != null) {
@@ -6126,24 +6142,21 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
// Dump ClientController#mClients
p.println(" ClientStates:");
- // TODO(b/322816970): Replace this with lambda.
- mClientController.forAllClients(new Consumer<ClientState>() {
+ // TODO(b/324907325): Remove the suppress warnings once b/324907325 is fixed.
+ @SuppressWarnings("GuardedBy") Consumer<ClientState> clientControllerDump = c -> {
+ p.println(" " + c + ":");
+ p.println(" client=" + c.mClient);
+ p.println(" fallbackInputConnection="
+ + c.mFallbackInputConnection);
+ p.println(" sessionRequested="
+ + c.mSessionRequested);
+ p.println(
+ " sessionRequestedForAccessibility="
+ + c.mSessionRequestedForAccessibility);
+ p.println(" curSession=" + c.mCurSession);
+ };
+ mClientController.forAllClients(clientControllerDump);
- @GuardedBy("ImfLock.class")
- @Override
- public void accept(ClientState c) {
- p.println(" " + c + ":");
- p.println(" client=" + c.mClient);
- p.println(" fallbackInputConnection="
- + c.mFallbackInputConnection);
- p.println(" sessionRequested="
- + c.mSessionRequested);
- p.println(
- " sessionRequestedForAccessibility="
- + c.mSessionRequestedForAccessibility);
- p.println(" curSession=" + c.mCurSession);
- }
- });
p.println(" mCurMethodId=" + getSelectedMethodIdLocked());
client = mCurClient;
p.println(" mCurClient=" + client + " mCurSeq=" + getSequenceNumberLocked());
diff --git a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
new file mode 100644
index 000000000000..62d44557ae4c
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
@@ -0,0 +1,420 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Copyright (C) 2024 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.inputmethod;
+
+import static com.android.server.inputmethod.InputMethodManagerService.TAG;
+
+import android.Manifest;
+import android.annotation.BinderThread;
+import android.annotation.EnforcePermission;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.UserIdInt;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.util.ExceptionUtils;
+import android.util.Slog;
+import android.view.WindowManager;
+import android.view.inputmethod.CursorAnchorInfo;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.ImeTracker;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodSubtype;
+import android.window.ImeOnBackInvokedDispatcher;
+
+import com.android.internal.inputmethod.DirectBootAwareness;
+import com.android.internal.inputmethod.IBooleanListener;
+import com.android.internal.inputmethod.IConnectionlessHandwritingCallback;
+import com.android.internal.inputmethod.IImeTracker;
+import com.android.internal.inputmethod.IInputMethodClient;
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
+import com.android.internal.inputmethod.IRemoteInputConnection;
+import com.android.internal.inputmethod.InputBindResult;
+import com.android.internal.inputmethod.SoftInputShowHideReason;
+import com.android.internal.inputmethod.StartInputFlags;
+import com.android.internal.inputmethod.StartInputReason;
+import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
+import com.android.internal.view.IInputMethodManager;
+
+import java.io.FileDescriptor;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+
+/**
+ * A proxy that processes all {@link IInputMethodManager} calls asynchronously.
+ * @hide
+ */
+public class ZeroJankProxy extends IInputMethodManager.Stub {
+
+ private final IInputMethodManager mInner;
+ private final Executor mExecutor;
+
+ ZeroJankProxy(Executor executor, IInputMethodManager inner) {
+ mInner = inner;
+ mExecutor = executor;
+ }
+
+ private void offload(ThrowingRunnable r) {
+ offloadInner(r);
+ }
+
+ private void offload(Runnable r) {
+ offloadInner(r);
+ }
+
+ private void offloadInner(Runnable r) {
+ boolean useThrowingRunnable = r instanceof ThrowingRunnable;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> {
+ final long inner = Binder.clearCallingIdentity();
+ // Restoring calling identity, so we can still do permission checks on caller.
+ Binder.restoreCallingIdentity(identity);
+ try {
+ try {
+ if (useThrowingRunnable) {
+ ((ThrowingRunnable) r).runOrThrow();
+ } else {
+ r.run();
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Error in async call", e);
+ throw ExceptionUtils.propagate(e);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(inner);
+ }
+ });
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void addClient(IInputMethodClient client, IRemoteInputConnection inputConnection,
+ int selfReportedDisplayId) throws RemoteException {
+ offload(() -> mInner.addClient(client, inputConnection, selfReportedDisplayId));
+ }
+
+ @Override
+ public InputMethodInfo getCurrentInputMethodInfoAsUser(int userId) throws RemoteException {
+ return mInner.getCurrentInputMethodInfoAsUser(userId);
+ }
+
+ @Override
+ public List<InputMethodInfo> getInputMethodList(
+ int userId, @DirectBootAwareness int directBootAwareness) throws RemoteException {
+ return mInner.getInputMethodList(userId, directBootAwareness);
+ }
+
+ @Override
+ public List<InputMethodInfo> getEnabledInputMethodList(int userId) throws RemoteException {
+ return mInner.getEnabledInputMethodList(userId);
+ }
+
+ @Override
+ public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String imiId,
+ boolean allowsImplicitlyEnabledSubtypes, int userId)
+ throws RemoteException {
+ return mInner.getEnabledInputMethodSubtypeList(imiId, allowsImplicitlyEnabledSubtypes,
+ userId);
+ }
+
+ @Override
+ public InputMethodSubtype getLastInputMethodSubtype(int userId) throws RemoteException {
+ return mInner.getLastInputMethodSubtype(userId);
+ }
+
+ @Override
+ public boolean showSoftInput(IInputMethodClient client, IBinder windowToken,
+ @Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
+ int lastClickTooType, ResultReceiver resultReceiver,
+ @SoftInputShowHideReason int reason)
+ throws RemoteException {
+ offload(() -> mInner.showSoftInput(client, windowToken, statsToken, flags, lastClickTooType,
+ resultReceiver, reason));
+ return true;
+ }
+
+ @Override
+ public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
+ @Nullable ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
+ ResultReceiver resultReceiver, @SoftInputShowHideReason int reason)
+ throws RemoteException {
+ offload(() -> mInner.hideSoftInput(client, windowToken, statsToken, flags, resultReceiver,
+ reason));
+ return true;
+ }
+
+ @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ @Override
+ public void startInputOrWindowGainedFocusAsync(
+ @StartInputReason int startInputReason,
+ IInputMethodClient client, IBinder windowToken,
+ @StartInputFlags int startInputFlags,
+ @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode,
+ int windowFlags, @Nullable EditorInfo editorInfo,
+ IRemoteInputConnection inputConnection,
+ IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
+ int unverifiedTargetSdkVersion, @UserIdInt int userId,
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq)
+ throws RemoteException {
+ offload(() -> {
+ InputBindResult result = mInner.startInputOrWindowGainedFocus(startInputReason, client,
+ windowToken, startInputFlags, softInputMode, windowFlags,
+ editorInfo,
+ inputConnection, remoteAccessibilityInputConnection,
+ unverifiedTargetSdkVersion,
+ userId, imeDispatcher);
+ sendOnStartInputResult(client, result, startInputSeq);
+ });
+ }
+
+ @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ @Override
+ public InputBindResult startInputOrWindowGainedFocus(
+ @StartInputReason int startInputReason,
+ IInputMethodClient client, IBinder windowToken,
+ @StartInputFlags int startInputFlags,
+ @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode,
+ int windowFlags, @Nullable EditorInfo editorInfo,
+ IRemoteInputConnection inputConnection,
+ IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
+ int unverifiedTargetSdkVersion, @UserIdInt int userId,
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher)
+ throws RemoteException {
+ // Should never be called when flag is enabled i.e. when this proxy is used.
+ return null;
+ }
+
+ @Override
+ public void showInputMethodPickerFromClient(IInputMethodClient client,
+ int auxiliarySubtypeMode)
+ throws RemoteException {
+ offload(() -> mInner.showInputMethodPickerFromClient(client, auxiliarySubtypeMode));
+ }
+
+ @EnforcePermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ @Override
+ public void showInputMethodPickerFromSystem(int auxiliarySubtypeMode, int displayId)
+ throws RemoteException {
+ mInner.showInputMethodPickerFromSystem(auxiliarySubtypeMode, displayId);
+ }
+
+ @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
+ @Override
+ public boolean isInputMethodPickerShownForTest() throws RemoteException {
+ super.isInputMethodPickerShownForTest_enforcePermission();
+ return mInner.isInputMethodPickerShownForTest();
+ }
+
+ @Override
+ public InputMethodSubtype getCurrentInputMethodSubtype(int userId) throws RemoteException {
+ return mInner.getCurrentInputMethodSubtype(userId);
+ }
+
+ @Override
+ public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes,
+ @UserIdInt int userId) throws RemoteException {
+ mInner.setAdditionalInputMethodSubtypes(imiId, subtypes, userId);
+ }
+
+ @Override
+ public void setExplicitlyEnabledInputMethodSubtypes(String imeId,
+ @NonNull int[] subtypeHashCodes, @UserIdInt int userId) throws RemoteException {
+ mInner.setExplicitlyEnabledInputMethodSubtypes(imeId, subtypeHashCodes, userId);
+ }
+
+ @Override
+ public int getInputMethodWindowVisibleHeight(IInputMethodClient client)
+ throws RemoteException {
+ return mInner.getInputMethodWindowVisibleHeight(client);
+ }
+
+ @Override
+ public void reportPerceptibleAsync(IBinder windowToken, boolean perceptible)
+ throws RemoteException {
+ // Already async TODO(b/293640003): ordering issues?
+ mInner.reportPerceptibleAsync(windowToken, perceptible);
+ }
+
+ @EnforcePermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW)
+ @Override
+ public void removeImeSurface() throws RemoteException {
+ mInner.removeImeSurface();
+ }
+
+ @Override
+ public void removeImeSurfaceFromWindowAsync(IBinder windowToken) throws RemoteException {
+ mInner.removeImeSurfaceFromWindowAsync(windowToken);
+ }
+
+ @Override
+ public void startProtoDump(byte[] bytes, int i, String s) throws RemoteException {
+ mInner.startProtoDump(bytes, i, s);
+ }
+
+ @Override
+ public boolean isImeTraceEnabled() throws RemoteException {
+ return mInner.isImeTraceEnabled();
+ }
+
+ @EnforcePermission(Manifest.permission.CONTROL_UI_TRACING)
+ @Override
+ public void startImeTrace() throws RemoteException {
+ mInner.startImeTrace();
+ }
+
+ @EnforcePermission(Manifest.permission.CONTROL_UI_TRACING)
+ @Override
+ public void stopImeTrace() throws RemoteException {
+ mInner.stopImeTrace();
+ }
+
+ @Override
+ public void startStylusHandwriting(IInputMethodClient client)
+ throws RemoteException {
+ offload(() -> mInner.startStylusHandwriting(client));
+ }
+
+ @Override
+ public void startConnectionlessStylusHandwriting(IInputMethodClient client, int userId,
+ @Nullable CursorAnchorInfo cursorAnchorInfo, @Nullable String delegatePackageName,
+ @Nullable String delegatorPackageName,
+ @NonNull IConnectionlessHandwritingCallback callback) throws RemoteException {
+ offload(() -> mInner.startConnectionlessStylusHandwriting(
+ client, userId, cursorAnchorInfo, delegatePackageName, delegatorPackageName,
+ callback));
+ }
+
+ @Override
+ public boolean acceptStylusHandwritingDelegation(
+ @NonNull IInputMethodClient client,
+ @UserIdInt int userId,
+ @NonNull String delegatePackageName,
+ @NonNull String delegatorPackageName,
+ @InputMethodManager.HandwritingDelegateFlags int flags) {
+ try {
+ return CompletableFuture.supplyAsync(() -> {
+ try {
+ return mInner.acceptStylusHandwritingDelegation(
+ client, userId, delegatePackageName, delegatorPackageName, flags);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }, this::offload).get();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ } catch (ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void acceptStylusHandwritingDelegationAsync(
+ @NonNull IInputMethodClient client,
+ @UserIdInt int userId,
+ @NonNull String delegatePackageName,
+ @NonNull String delegatorPackageName,
+ @InputMethodManager.HandwritingDelegateFlags int flags, IBooleanListener callback)
+ throws RemoteException {
+ offload(() -> mInner.acceptStylusHandwritingDelegationAsync(
+ client, userId, delegatePackageName, delegatorPackageName, flags, callback));
+ }
+
+ @Override
+ public void prepareStylusHandwritingDelegation(
+ @NonNull IInputMethodClient client,
+ @UserIdInt int userId,
+ @NonNull String delegatePackageName,
+ @NonNull String delegatorPackageName) {
+ offload(() -> mInner.prepareStylusHandwritingDelegation(
+ client, userId, delegatePackageName, delegatorPackageName));
+ }
+
+ @Override
+ public boolean isStylusHandwritingAvailableAsUser(int userId, boolean connectionless)
+ throws RemoteException {
+ return mInner.isStylusHandwritingAvailableAsUser(userId, connectionless);
+ }
+
+ @EnforcePermission("android.permission.TEST_INPUT_METHOD")
+ @Override
+ public void addVirtualStylusIdForTestSession(IInputMethodClient client)
+ throws RemoteException {
+ mInner.addVirtualStylusIdForTestSession(client);
+ }
+
+ @EnforcePermission("android.permission.TEST_INPUT_METHOD")
+ @Override
+ public void setStylusWindowIdleTimeoutForTest(IInputMethodClient client, long timeout)
+ throws RemoteException {
+ mInner.setStylusWindowIdleTimeoutForTest(client, timeout);
+ }
+
+ @Override
+ public IImeTracker getImeTrackerService() throws RemoteException {
+ return mInner.getImeTrackerService();
+ }
+
+ @BinderThread
+ @Override
+ public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+ @Nullable FileDescriptor err,
+ @NonNull String[] args, @Nullable ShellCallback callback,
+ @NonNull ResultReceiver resultReceiver) throws RemoteException {
+ ((InputMethodManagerService) mInner).onShellCommand(
+ in, out, err, args, callback, resultReceiver);
+ }
+
+ private void sendOnStartInputResult(
+ IInputMethodClient client, InputBindResult res, int startInputSeq) {
+ InputMethodManagerService service = (InputMethodManagerService) mInner;
+ final ClientState cs = service.getClientState(client);
+ if (cs != null && cs.mClient != null) {
+ cs.mClient.onStartInputResult(res, startInputSeq);
+ } else {
+ // client is unbound.
+ Slog.i(TAG, "Client that requested startInputOrWindowGainedFocus is no longer"
+ + " bound. InputBindResult: " + res + " for startInputSeq: " + startInputSeq);
+ }
+ }
+}
+
diff --git a/services/core/java/com/android/server/location/GeocoderProxy.java b/services/core/java/com/android/server/location/GeocoderProxy.java
deleted file mode 100644
index ac42646499a3..000000000000
--- a/services/core/java/com/android/server/location/GeocoderProxy.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2010 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.location;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.location.GeocoderParams;
-import android.location.IGeocodeListener;
-import android.location.IGeocodeProvider;
-import android.os.IBinder;
-import android.os.RemoteException;
-
-import com.android.server.servicewatcher.CurrentUserServiceSupplier;
-import com.android.server.servicewatcher.ServiceWatcher;
-
-import java.util.Collections;
-
-/**
- * Proxy for IGeocodeProvider implementations.
- *
- * @hide
- */
-public class GeocoderProxy {
-
- private static final String SERVICE_ACTION = "com.android.location.service.GeocodeProvider";
-
- /**
- * Creates and registers this proxy. If no suitable service is available for the proxy, returns
- * null.
- */
- @Nullable
- public static GeocoderProxy createAndRegister(Context context) {
- GeocoderProxy proxy = new GeocoderProxy(context);
- if (proxy.register()) {
- return proxy;
- } else {
- return null;
- }
- }
-
- private final ServiceWatcher mServiceWatcher;
-
- private GeocoderProxy(Context context) {
- mServiceWatcher = ServiceWatcher.create(context, "GeocoderProxy",
- CurrentUserServiceSupplier.createFromConfig(context, SERVICE_ACTION,
- com.android.internal.R.bool.config_enableGeocoderOverlay,
- com.android.internal.R.string.config_geocoderProviderPackageName),
- null);
- }
-
- private boolean register() {
- boolean resolves = mServiceWatcher.checkServiceResolves();
- if (resolves) {
- mServiceWatcher.register();
- }
- return resolves;
- }
-
- /**
- * Geocodes stuff.
- */
- public void getFromLocation(double latitude, double longitude, int maxResults,
- GeocoderParams params, IGeocodeListener listener) {
- mServiceWatcher.runOnBinder(new ServiceWatcher.BinderOperation() {
- @Override
- public void run(IBinder binder) throws RemoteException {
- IGeocodeProvider provider = IGeocodeProvider.Stub.asInterface(binder);
- provider.getFromLocation(latitude, longitude, maxResults, params, listener);
- }
-
- @Override
- public void onError(Throwable t) {
- try {
- listener.onResults(t.toString(), Collections.emptyList());
- } catch (RemoteException e) {
- // ignore
- }
- }
- });
- }
-
- /**
- * Geocodes stuff.
- */
- public void getFromLocationName(String locationName,
- double lowerLeftLatitude, double lowerLeftLongitude,
- double upperRightLatitude, double upperRightLongitude, int maxResults,
- GeocoderParams params, IGeocodeListener listener) {
- mServiceWatcher.runOnBinder(new ServiceWatcher.BinderOperation() {
- @Override
- public void run(IBinder binder) throws RemoteException {
- IGeocodeProvider provider = IGeocodeProvider.Stub.asInterface(binder);
- provider.getFromLocationName(locationName, lowerLeftLatitude,
- lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
- maxResults, params, listener);
- }
-
- @Override
- public void onError(Throwable t) {
- try {
- listener.onResults(t.toString(), Collections.emptyList());
- } catch (RemoteException e) {
- // ignore
- }
- }
- });
- }
-}
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 323cdc54edae..a608049cd677 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -52,13 +52,11 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Criteria;
-import android.location.GeocoderParams;
import android.location.Geofence;
import android.location.GnssAntennaInfo;
import android.location.GnssCapabilities;
import android.location.GnssMeasurementCorrections;
import android.location.GnssMeasurementRequest;
-import android.location.IGeocodeListener;
import android.location.IGnssAntennaInfoListener;
import android.location.IGnssMeasurementsListener;
import android.location.IGnssNavigationMessageListener;
@@ -75,8 +73,11 @@ import android.location.LocationManagerInternal.LocationPackageTagsListener;
import android.location.LocationProvider;
import android.location.LocationRequest;
import android.location.LocationTime;
+import android.location.provider.ForwardGeocodeRequest;
+import android.location.provider.IGeocodeCallback;
import android.location.provider.IProviderRequestListener;
import android.location.provider.ProviderProperties;
+import android.location.provider.ReverseGeocodeRequest;
import android.location.util.identity.CallerIdentity;
import android.os.Binder;
import android.os.Build;
@@ -141,6 +142,7 @@ import com.android.server.location.provider.MockLocationProvider;
import com.android.server.location.provider.PassiveLocationProvider;
import com.android.server.location.provider.PassiveLocationProviderManager;
import com.android.server.location.provider.StationaryThrottlingLocationProvider;
+import com.android.server.location.provider.proxy.ProxyGeocodeProvider;
import com.android.server.location.provider.proxy.ProxyLocationProvider;
import com.android.server.location.settings.LocationSettings;
import com.android.server.location.settings.LocationUserSettings;
@@ -253,7 +255,7 @@ public class LocationManagerService extends ILocationManager.Stub implements
private final GeofenceManager mGeofenceManager;
private volatile @Nullable GnssManagerService mGnssManagerService = null;
- private GeocoderProxy mGeocodeProvider;
+ private ProxyGeocodeProvider mGeocodeProvider;
private final Object mDeprecatedGnssBatchingLock = new Object();
@GuardedBy("mDeprecatedGnssBatchingLock")
@@ -461,11 +463,13 @@ public class LocationManagerService extends ILocationManager.Stub implements
com.android.internal.R.bool.config_useGnssHardwareProvider);
AbstractLocationProvider gnssProvider = null;
if (!useGnssHardwareProvider) {
+ // TODO: Create a separate config_enableGnssLocationOverlay config resource
+ // if we want to selectively enable a GNSS overlay but disable a fused overlay.
gnssProvider = ProxyLocationProvider.create(
mContext,
GPS_PROVIDER,
ACTION_GNSS_PROVIDER,
- com.android.internal.R.bool.config_useGnssHardwareProvider,
+ com.android.internal.R.bool.config_enableFusedLocationOverlay,
com.android.internal.R.string.config_gnssLocationProviderPackageName);
}
if (gnssProvider == null) {
@@ -493,7 +497,7 @@ public class LocationManagerService extends ILocationManager.Stub implements
}
// bind to geocoder provider
- mGeocodeProvider = GeocoderProxy.createAndRegister(mContext);
+ mGeocodeProvider = ProxyGeocodeProvider.createAndRegister(mContext);
if (mGeocodeProvider == null) {
Log.e(TAG, "no geocoder provider found");
}
@@ -1344,7 +1348,9 @@ public class LocationManagerService extends ILocationManager.Stub implements
"setAutomotiveGnssSuspended only allowed on automotive devices");
}
- mGnssManagerService.setAutomotiveGnssSuspended(suspended);
+ if (mGnssManagerService != null) {
+ mGnssManagerService.setAutomotiveGnssSuspended(suspended);
+ }
}
@android.annotation.EnforcePermission(android.Manifest.permission.CONTROL_AUTOMOTIVE_GNSS)
@@ -1359,27 +1365,29 @@ public class LocationManagerService extends ILocationManager.Stub implements
"isAutomotiveGnssSuspended only allowed on automotive devices");
}
- return mGnssManagerService.isAutomotiveGnssSuspended();
+ if (mGnssManagerService != null) {
+ return mGnssManagerService.isAutomotiveGnssSuspended();
+ }
+ return false;
}
@Override
- public boolean geocoderIsPresent() {
+ public boolean isGeocodeAvailable() {
return mGeocodeProvider != null;
}
@Override
- public void getFromLocation(double latitude, double longitude, int maxResults,
- GeocoderParams params, IGeocodeListener listener) {
- // validate identity
- CallerIdentity identity = CallerIdentity.fromBinder(mContext, params.getClientPackage(),
- params.getClientAttributionTag());
- Preconditions.checkArgument(identity.getUid() == params.getClientUid());
+ public void reverseGeocode(ReverseGeocodeRequest request, IGeocodeCallback callback) {
+ CallerIdentity identity =
+ CallerIdentity.fromBinder(
+ mContext, request.getCallingPackage(), request.getCallingAttributionTag());
+ Preconditions.checkArgument(identity.getUid() == request.getCallingUid());
if (mGeocodeProvider != null) {
- mGeocodeProvider.getFromLocation(latitude, longitude, maxResults, params, listener);
+ mGeocodeProvider.reverseGeocode(request, callback);
} else {
try {
- listener.onResults(null, Collections.emptyList());
+ callback.onError(null);
} catch (RemoteException e) {
// ignore
}
@@ -1387,22 +1395,17 @@ public class LocationManagerService extends ILocationManager.Stub implements
}
@Override
- public void getFromLocationName(String locationName,
- double lowerLeftLatitude, double lowerLeftLongitude,
- double upperRightLatitude, double upperRightLongitude, int maxResults,
- GeocoderParams params, IGeocodeListener listener) {
- // validate identity
- CallerIdentity identity = CallerIdentity.fromBinder(mContext, params.getClientPackage(),
- params.getClientAttributionTag());
- Preconditions.checkArgument(identity.getUid() == params.getClientUid());
+ public void forwardGeocode(ForwardGeocodeRequest request, IGeocodeCallback callback) {
+ CallerIdentity identity =
+ CallerIdentity.fromBinder(
+ mContext, request.getCallingPackage(), request.getCallingAttributionTag());
+ Preconditions.checkArgument(identity.getUid() == request.getCallingUid());
if (mGeocodeProvider != null) {
- mGeocodeProvider.getFromLocationName(locationName, lowerLeftLatitude,
- lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
- maxResults, params, listener);
+ mGeocodeProvider.forwardGeocode(request, callback);
} else {
try {
- listener.onResults(null, Collections.emptyList());
+ callback.onError(null);
} catch (RemoteException e) {
// ignore
}
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index ecb4fcca5eef..40e538b02728 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -1618,6 +1618,17 @@ public class LocationProviderManager extends
*/
@SuppressLint("AndroidFrameworkRequiresPermission")
public boolean isVisibleToCaller() {
+ // Anything sharing the system's UID can view all providers
+ if (Binder.getCallingUid() == Process.SYSTEM_UID) {
+ return true;
+ }
+
+ // If an app mocked this provider, anybody can access it (the goal is
+ // to behave as if this provider didn't naturally exist).
+ if (mProvider.isMock()) {
+ return true;
+ }
+
for (String permission : mRequiredPermissions) {
if (mContext.checkCallingOrSelfPermission(permission) != PERMISSION_GRANTED) {
return false;
diff --git a/services/core/java/com/android/server/location/provider/proxy/ProxyGeocodeProvider.java b/services/core/java/com/android/server/location/provider/proxy/ProxyGeocodeProvider.java
new file mode 100644
index 000000000000..ac945f1d2921
--- /dev/null
+++ b/services/core/java/com/android/server/location/provider/proxy/ProxyGeocodeProvider.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2010 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.location.provider.proxy;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.location.provider.ForwardGeocodeRequest;
+import android.location.provider.GeocodeProviderBase;
+import android.location.provider.IGeocodeCallback;
+import android.location.provider.IGeocodeProvider;
+import android.location.provider.ReverseGeocodeRequest;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import com.android.server.servicewatcher.CurrentUserServiceSupplier;
+import com.android.server.servicewatcher.ServiceWatcher;
+
+/** Proxy for IGeocodeProvider implementations. */
+public class ProxyGeocodeProvider {
+
+ /**
+ * Creates and registers this proxy. If no suitable service is available for the proxy, returns
+ * null.
+ */
+ @Nullable
+ public static ProxyGeocodeProvider createAndRegister(Context context) {
+ ProxyGeocodeProvider proxy = new ProxyGeocodeProvider(context);
+ if (proxy.register()) {
+ return proxy;
+ } else {
+ return null;
+ }
+ }
+
+ private final ServiceWatcher mServiceWatcher;
+
+ private ProxyGeocodeProvider(Context context) {
+ mServiceWatcher =
+ ServiceWatcher.create(
+ context,
+ "GeocoderProxy",
+ CurrentUserServiceSupplier.createFromConfig(
+ context,
+ GeocodeProviderBase.ACTION_GEOCODE_PROVIDER,
+ com.android.internal.R.bool.config_enableGeocoderOverlay,
+ com.android.internal.R.string.config_geocoderProviderPackageName),
+ null);
+ }
+
+ private boolean register() {
+ boolean resolves = mServiceWatcher.checkServiceResolves();
+ if (resolves) {
+ mServiceWatcher.register();
+ }
+ return resolves;
+ }
+
+ /** Reverse geocodes. */
+ public void reverseGeocode(ReverseGeocodeRequest request, IGeocodeCallback callback) {
+ mServiceWatcher.runOnBinder(
+ new ServiceWatcher.BinderOperation() {
+ @Override
+ public void run(IBinder binder) throws RemoteException {
+ IGeocodeProvider.Stub.asInterface(binder).reverseGeocode(request, callback);
+ }
+
+ @Override
+ public void onError(Throwable t) {
+ try {
+ callback.onError(t.toString());
+ } catch (RemoteException e) {
+ // ignore
+ }
+ }
+ });
+ }
+
+ /** Forward geocodes. */
+ public void forwardGeocode(ForwardGeocodeRequest request, IGeocodeCallback callback) {
+ mServiceWatcher.runOnBinder(
+ new ServiceWatcher.BinderOperation() {
+ @Override
+ public void run(IBinder binder) throws RemoteException {
+ IGeocodeProvider.Stub.asInterface(binder).forwardGeocode(request, callback);
+ }
+
+ @Override
+ public void onError(Throwable t) {
+ try {
+ callback.onError(t.toString());
+ } catch (RemoteException e) {
+ // ignore
+ }
+ }
+ });
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 7fb3e001c4c3..9a76ebd148aa 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -18,6 +18,7 @@ package com.android.server.locksettings;
import static android.security.Flags.reportPrimaryAuthAttempts;
import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
+import static android.Manifest.permission.CONFIGURE_FACTORY_RESET_PROTECTION;
import static android.Manifest.permission.MANAGE_BIOMETRIC;
import static android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS;
import static android.Manifest.permission.SET_INITIAL_LOCK;
@@ -27,6 +28,7 @@ import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRY
import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRYPTED_MESSAGE;
import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRYPTED_TITLE;
import static android.content.Context.KEYGUARD_SERVICE;
+import static android.content.Intent.ACTION_MAIN_USER_LOCKSCREEN_KNOWLEDGE_FACTOR_CHANGED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.UserHandle.USER_ALL;
import static android.os.UserHandle.USER_SYSTEM;
@@ -1201,8 +1203,9 @@ public class LockSettingsService extends ILockSettings.Stub {
final boolean inSetupWizard = Settings.Secure.getIntForUser(cr,
Settings.Secure.USER_SETUP_COMPLETE, 0, mainUserId) == 0;
- final boolean secureFrp = Settings.Global.getInt(cr,
- Settings.Global.SECURE_FRP_MODE, 0) == 1;
+ final boolean secureFrp = android.security.Flags.frpEnforcement()
+ ? mStorage.isFactoryResetProtectionActive()
+ : (Settings.Global.getInt(cr, Settings.Global.SECURE_FRP_MODE, 0) == 1);
if (inSetupWizard && secureFrp) {
throw new SecurityException("Cannot change credential in SUW while factory reset"
@@ -2332,8 +2335,13 @@ public class LockSettingsService extends ILockSettings.Stub {
synchronized (mSpManager) {
if (isSpecialUserId(userId)) {
- return mSpManager.verifySpecialUserCredential(userId, getGateKeeperService(),
+ response = mSpManager.verifySpecialUserCredential(userId, getGateKeeperService(),
credential, progressCallback);
+ if (android.security.Flags.frpEnforcement() && response.isMatched()
+ && userId == USER_FRP) {
+ mStorage.deactivateFactoryResetProtectionWithoutSecret();
+ }
+ return response;
}
long protectorId = getCurrentLskfBasedProtectorId(userId);
@@ -3054,6 +3062,7 @@ public class LockSettingsService extends ILockSettings.Stub {
setCurrentLskfBasedProtectorId(newProtectorId, userId);
LockPatternUtils.invalidateCredentialTypeCache();
synchronizeUnifiedChallengeForProfiles(userId, profilePasswords);
+ sendMainUserCredentialChangedNotificationIfNeeded(userId);
setUserPasswordMetrics(credential, userId);
mUnifiedProfilePasswordCache.removePassword(userId);
@@ -3071,6 +3080,24 @@ public class LockSettingsService extends ILockSettings.Stub {
return newProtectorId;
}
+ private void sendMainUserCredentialChangedNotificationIfNeeded(int userId) {
+ if (!android.security.Flags.frpEnforcement()) {
+ return;
+ }
+
+ if (userId != mInjector.getUserManagerInternal().getMainUserId()) {
+ return;
+ }
+
+ sendBroadcast(new Intent(ACTION_MAIN_USER_LOCKSCREEN_KNOWLEDGE_FACTOR_CHANGED),
+ UserHandle.of(userId), CONFIGURE_FACTORY_RESET_PROTECTION);
+ }
+
+ @VisibleForTesting
+ void sendBroadcast(Intent intent, UserHandle userHandle, String permission) {
+ mContext.sendBroadcastAsUser(intent, userHandle, permission, /* options */ null);
+ }
+
private void removeBiometricsForUser(int userId) {
removeAllFingerprintForUser(userId);
removeAllFaceForUser(userId);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index 6d123ccebc7c..158d444bcff2 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -34,6 +34,7 @@ import android.os.Environment;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.service.persistentdata.PersistentDataBlockManager;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.AtomicFile;
@@ -587,6 +588,10 @@ class LockSettingsStorage {
return mPersistentDataBlockManagerInternal;
}
+ /**
+ * Writes main user credential handle to the persistent data block, to enable factory reset
+ * protection to be deactivated with the credential.
+ */
public void writePersistentDataBlock(int persistentType, int userId, int qualityForUi,
byte[] payload) {
PersistentDataBlockManagerInternal persistentDataBlock = getPersistentDataBlockManager();
@@ -610,6 +615,31 @@ class LockSettingsStorage {
}
}
+ public void deactivateFactoryResetProtectionWithoutSecret() {
+ PersistentDataBlockManagerInternal persistentDataBlock = getPersistentDataBlockManager();
+ if (persistentDataBlock != null) {
+ persistentDataBlock.deactivateFactoryResetProtectionWithoutSecret();
+ } else {
+ Slog.wtf(TAG, "Failed to get PersistentDataBlockManagerInternal");
+ }
+ }
+
+ public boolean isFactoryResetProtectionActive() {
+ PersistentDataBlockManager persistentDataBlockManager =
+ mContext.getSystemService(PersistentDataBlockManager.class);
+ if (persistentDataBlockManager != null) {
+ return persistentDataBlockManager.isFactoryResetProtectionActive();
+ } else {
+ Slog.wtf(TAG, "Failed to get PersistentDataBlockManager");
+ // This should never happen, but in the event it does, let's not block the user. This
+ // may be the wrong call, since if an attacker can find a way to prevent us from
+ // getting the PersistentDataBlockManager they can defeat FRP, but if they can block
+ // access to PersistentDataBlockManager they must have compromised the system and we've
+ // probably already lost this battle.
+ return false;
+ }
+ }
+
/**
* Provides a concrete data structure to represent the minimal information from
* a user's LSKF-based SP protector that is needed to verify the user's LSKF,
diff --git a/services/core/java/com/android/server/media/MediaSession2Record.java b/services/core/java/com/android/server/media/MediaSession2Record.java
index 34bb219a9162..a110e5637f82 100644
--- a/services/core/java/com/android/server/media/MediaSession2Record.java
+++ b/services/core/java/com/android/server/media/MediaSession2Record.java
@@ -40,6 +40,7 @@ public class MediaSession2Record implements MediaSessionRecordImpl {
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final Object mLock = new Object();
+ private final int mUniqueId;
@GuardedBy("mLock")
private final Session2Token mSessionToken;
@GuardedBy("mLock")
@@ -63,11 +64,13 @@ public class MediaSession2Record implements MediaSessionRecordImpl {
MediaSessionService service,
Looper handlerLooper,
int pid,
- int policies) {
+ int policies,
+ int uniqueId) {
// The lock is required to prevent `Controller2Callback` from using partially initialized
// `MediaSession2Record.this`.
synchronized (mLock) {
mSessionToken = sessionToken;
+ mUniqueId = uniqueId;
mService = service;
mHandlerExecutor = new HandlerExecutor(new Handler(handlerLooper));
mController = new MediaController2.Builder(service.getContext(), sessionToken)
@@ -98,6 +101,13 @@ public class MediaSession2Record implements MediaSessionRecordImpl {
}
@Override
+ public int getUniqueId() {
+ synchronized (mLock) {
+ return mUniqueId;
+ }
+ }
+
+ @Override
public String getPackageName() {
return mSessionToken.getPackageName();
}
@@ -144,7 +154,7 @@ public class MediaSession2Record implements MediaSessionRecordImpl {
@Override
public boolean checkPlaybackActiveState(boolean expected) {
synchronized (mLock) {
- return mIsConnected && mController.isPlaybackActive() == expected;
+ return (mIsConnected && mController.isPlaybackActive()) == expected;
}
}
@@ -200,6 +210,7 @@ public class MediaSession2Record implements MediaSessionRecordImpl {
@Override
public void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "uniqueId=" + mUniqueId);
pw.println(prefix + "token=" + mSessionToken);
pw.println(prefix + "controller=" + mController);
@@ -209,8 +220,7 @@ public class MediaSession2Record implements MediaSessionRecordImpl {
@Override
public String toString() {
- // TODO(jaewan): Also add getId().
- return getPackageName() + " (userId=" + getUserId() + ")";
+ return getPackageName() + "/" + mUniqueId + " (userId=" + getUserId() + ")";
}
private class Controller2Callback extends MediaController2.ControllerCallback {
@@ -224,10 +234,6 @@ public class MediaSession2Record implements MediaSessionRecordImpl {
mIsConnected = true;
service = mService;
}
-
- // TODO (b/318745416): Add support for FGS in MediaSession2. Passing a
- // null playback state means the owning process will not be allowed to
- // run in the foreground.
service.onSessionActiveStateChanged(MediaSession2Record.this,
/* playbackState= */ null);
}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 53f780e4d19e..15527041d8eb 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -173,6 +173,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
private final int mUserId;
private final String mPackageName;
private final String mTag;
+ private final int mUniqueId;
private final Bundle mSessionInfo;
private final ControllerStub mController;
private final MediaSession.Token mSessionToken;
@@ -223,15 +224,25 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
private int mPolicies;
- public MediaSessionRecord(int ownerPid, int ownerUid, int userId, String ownerPackageName,
- ISessionCallback cb, String tag, Bundle sessionInfo,
- MediaSessionService service, Looper handlerLooper, int policies)
+ public MediaSessionRecord(
+ int ownerPid,
+ int ownerUid,
+ int userId,
+ String ownerPackageName,
+ ISessionCallback cb,
+ String tag,
+ int uniqueId,
+ Bundle sessionInfo,
+ MediaSessionService service,
+ Looper handlerLooper,
+ int policies)
throws RemoteException {
mOwnerPid = ownerPid;
mOwnerUid = ownerUid;
mUserId = userId;
mPackageName = ownerPackageName;
mTag = tag;
+ mUniqueId = uniqueId;
mSessionInfo = sessionInfo;
mController = new ControllerStub();
mSessionToken = new MediaSession.Token(ownerUid, mController);
@@ -292,6 +303,16 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
}
/**
+ * Get the unique id of this session record.
+ *
+ * @return a unique id of this session record.
+ */
+ @Override
+ public int getUniqueId() {
+ return mUniqueId;
+ }
+
+ /**
* Get the info for this session.
*
* @return Info that identifies this session.
@@ -703,7 +724,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR
@Override
public String toString() {
- return mPackageName + "/" + mTag + " (userId=" + mUserId + ")";
+ return mPackageName + "/" + mTag + "/" + mUniqueId + " (userId=" + mUserId + ")";
}
@Override
diff --git a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
index 99c8ea93936e..e53a2dbe8101 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
@@ -32,6 +32,13 @@ import java.io.PrintWriter;
public interface MediaSessionRecordImpl extends AutoCloseable {
/**
+ * Get the unique id of this session record.
+ *
+ * @return a unique id of this session record.
+ */
+ int getUniqueId();
+
+ /**
* Get the info for this session.
*
* @return Info that identifies this session.
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 8cb5cef38ec6..9e98a5809650 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -106,6 +106,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* System implementation of MediaSessionManager
@@ -151,7 +152,11 @@ public class MediaSessionService extends SystemService implements Monitor {
private boolean mHasFeatureLeanback;
private ActivityManagerInternal mActivityManagerInternal;
private UsageStatsManagerInternal mUsageStatsManagerInternal;
- private final Set<Integer> mUserEngagingSessions = new HashSet<>();
+
+ /* Maps uid with all user engaging session tokens associated to it */
+ private final SparseArray<Set<MediaSession.Token>> mUserEngagingSessions = new SparseArray<>();
+
+ private final AtomicInteger mNextMediaSessionRecordId = new AtomicInteger(1);
// The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile)
// It's always not null after the MediaSessionService is started.
@@ -191,7 +196,8 @@ public class MediaSessionService extends SystemService implements Monitor {
MediaSessionService.this,
mRecordThread.getLooper(),
pid,
- /* policies= */ 0);
+ /* policies= */ 0,
+ /* uniqueId= */ mNextMediaSessionRecordId.getAndIncrement());
synchronized (mLock) {
FullUserRecord user = getFullUserRecordLocked(record.getUserId());
if (user != null) {
@@ -310,18 +316,27 @@ public class MediaSessionService extends SystemService implements Monitor {
}
user.mPriorityStack.onSessionActiveStateChanged(record);
}
- boolean allowRunningInForeground = record.isActive()
- && (playbackState == null || playbackState.isActive());
+ boolean isUserEngaged = isUserEngaged(record, playbackState);
Log.d(TAG, "onSessionActiveStateChanged: "
+ "record=" + record
+ "playbackState=" + playbackState
- + "allowRunningInForeground=" + allowRunningInForeground);
- setForegroundServiceAllowance(record, allowRunningInForeground);
+ + "allowRunningInForeground=" + isUserEngaged);
+ setForegroundServiceAllowance(record, /* allowRunningInForeground= */ isUserEngaged);
+ reportMediaInteractionEvent(record, isUserEngaged);
mHandler.postSessionsChanged(record);
}
}
+ private boolean isUserEngaged(MediaSessionRecordImpl record,
+ @Nullable PlaybackState playbackState) {
+ if (playbackState == null) {
+ // MediaSession2 case
+ return record.checkPlaybackActiveState(/* expected= */ true);
+ }
+ return playbackState.isActive() && record.isActive();
+ }
+
// Currently only media1 can become global priority session.
void setGlobalPrioritySession(MediaSessionRecord record) {
synchronized (mLock) {
@@ -414,14 +429,13 @@ public class MediaSessionService extends SystemService implements Monitor {
return;
}
user.mPriorityStack.onPlaybackStateChanged(record, shouldUpdatePriority);
- if (playbackState != null) {
- boolean allowRunningInForeground = playbackState.isActive() && record.isActive();
- Log.d(TAG, "onSessionPlaybackStateChanged: "
- + "record=" + record
- + "playbackState=" + playbackState
- + "allowRunningInForeground=" + allowRunningInForeground);
- setForegroundServiceAllowance(record, allowRunningInForeground);
- }
+ boolean isUserEngaged = isUserEngaged(record, playbackState);
+ Log.d(TAG, "onSessionPlaybackStateChanged: "
+ + "record=" + record
+ + "playbackState=" + playbackState
+ + "allowRunningInForeground=" + isUserEngaged);
+ setForegroundServiceAllowance(record, /* allowRunningInForeground= */ isUserEngaged);
+ reportMediaInteractionEvent(record, isUserEngaged);
}
}
@@ -588,6 +602,7 @@ public class MediaSessionService extends SystemService implements Monitor {
Log.d(TAG, "destroySessionLocked: record=" + session);
setForegroundServiceAllowance(session, /* allowRunningInForeground= */ false);
+ reportMediaInteractionEvent(session, /* userEngaged= */ false);
mHandler.postSessionsChanged(session);
}
@@ -606,41 +621,41 @@ public class MediaSessionService extends SystemService implements Monitor {
if (allowRunningInForeground) {
mActivityManagerInternal.startForegroundServiceDelegate(
foregroundServiceDelegationOptions, /* connection= */ null);
- reportMediaInteractionEvent(record, /* userEngaged= */ true);
} else {
mActivityManagerInternal.stopForegroundServiceDelegate(
foregroundServiceDelegationOptions);
- reportMediaInteractionEvent(record, /* userEngaged= */ false);
}
}
private void reportMediaInteractionEvent(MediaSessionRecordImpl record, boolean userEngaged) {
- if (!android.app.usage.Flags.userInteractionTypeApi()) {
- return;
- }
+ if (!android.app.usage.Flags.userInteractionTypeApi()
+ || !(record instanceof MediaSessionRecord)) {
+ return;
+ }
- String packageName = record.getPackageName();
- int sessionUid = record.getUid();
- String actionToLog = null;
- if (userEngaged) {
- if (!mUserEngagingSessions.contains(sessionUid)) {
- actionToLog = "start";
- }
- mUserEngagingSessions.add(sessionUid);
- } else {
- if (mUserEngagingSessions.contains(sessionUid)) {
- actionToLog = "stop";
- }
+ String packageName = record.getPackageName();
+ int sessionUid = record.getUid();
+ MediaSession.Token token = ((MediaSessionRecord) record).getSessionToken();
+ if (userEngaged) {
+ if (!mUserEngagingSessions.contains(sessionUid)) {
+ mUserEngagingSessions.put(sessionUid, new HashSet<>());
+ reportUserInteractionEvent(/* action= */ "start", record.getUserId(), packageName);
+ }
+ mUserEngagingSessions.get(sessionUid).add(token);
+ } else if (mUserEngagingSessions.contains(sessionUid)) {
+ mUserEngagingSessions.get(sessionUid).remove(token);
+ if (mUserEngagingSessions.get(sessionUid).isEmpty()) {
+ reportUserInteractionEvent(/* action= */ "stop", record.getUserId(), packageName);
mUserEngagingSessions.remove(sessionUid);
}
+ }
+ }
- if (actionToLog != null) {
- PersistableBundle extras = new PersistableBundle();
- extras.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY, "android.media");
- extras.putString(UsageStatsManager.EXTRA_EVENT_ACTION, actionToLog);
- mUsageStatsManagerInternal.reportUserInteractionEvent(
- packageName, record.getUserId(), extras);
- }
+ private void reportUserInteractionEvent(String action, int userId, String packageName) {
+ PersistableBundle extras = new PersistableBundle();
+ extras.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY, "android.media");
+ extras.putString(UsageStatsManager.EXTRA_EVENT_ACTION, action);
+ mUsageStatsManagerInternal.reportUserInteractionEvent(packageName, userId, extras);
}
void tempAllowlistTargetPkgIfPossible(int targetUid, String targetPackage,
@@ -783,9 +798,19 @@ public class MediaSessionService extends SystemService implements Monitor {
final MediaSessionRecord session;
try {
- session = new MediaSessionRecord(callerPid, callerUid, userId,
- callerPackageName, cb, tag, sessionInfo, this,
- mRecordThread.getLooper(), policies);
+ session =
+ new MediaSessionRecord(
+ callerPid,
+ callerUid,
+ userId,
+ callerPackageName,
+ cb,
+ tag,
+ /* uniqueId= */ mNextMediaSessionRecordId.getAndIncrement(),
+ sessionInfo,
+ this,
+ mRecordThread.getLooper(),
+ policies);
} catch (RemoteException e) {
throw new RuntimeException("Media Session owner died prematurely.", e);
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index f9ffb1cebac4..796d8d73fc51 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -26,6 +26,8 @@ import static android.Manifest.permission.NETWORK_STACK;
import static android.Manifest.permission.OBSERVE_NETWORK_POLICY;
import static android.Manifest.permission.READ_PHONE_STATE;
import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK;
import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN;
import static android.app.ActivityManager.isProcStateConsideredInteraction;
import static android.app.ActivityManager.printCapabilitiesSummary;
@@ -85,8 +87,10 @@ import static android.net.NetworkPolicyManager.ALLOWED_REASON_POWER_SAVE_EXCEPT_
import static android.net.NetworkPolicyManager.ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS;
import static android.net.NetworkPolicyManager.ALLOWED_REASON_SYSTEM;
import static android.net.NetworkPolicyManager.ALLOWED_REASON_TOP;
+import static android.net.NetworkPolicyManager.BACKGROUND_THRESHOLD_STATE;
import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
+import static android.net.NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE;
import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
import static android.net.NetworkPolicyManager.POLICY_NONE;
import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
@@ -97,6 +101,7 @@ import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
import static android.net.NetworkPolicyManager.RULE_REJECT_RESTRICTED_MODE;
import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED;
import static android.net.NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_UNMETERED;
+import static android.net.NetworkPolicyManager.TOP_THRESHOLD_STATE;
import static android.net.NetworkPolicyManager.allowedReasonsToString;
import static android.net.NetworkPolicyManager.blockedReasonsToString;
import static android.net.NetworkPolicyManager.isProcStateAllowedNetworkWhileBackground;
@@ -335,6 +340,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
static final String TAG = NetworkPolicyLogger.TAG;
private static final boolean LOGD = NetworkPolicyLogger.LOGD;
private static final boolean LOGV = NetworkPolicyLogger.LOGV;
+ // TODO: b/304347838 - Remove once the feature is in staging.
+ private static final boolean ALWAYS_RESTRICT_BACKGROUND_NETWORK = false;
/**
* No opportunistic quota could be calculated from user data plan or data settings.
@@ -469,7 +476,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
*/
private static final int MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS = 24;
- private static final int UID_MSG_STATE_CHANGED = 100;
+ @VisibleForTesting
+ static final int UID_MSG_STATE_CHANGED = 100;
private static final int UID_MSG_GONE = 101;
private static final String PROP_SUB_PLAN_OWNER = "persist.sys.sub_plan_owner";
@@ -1055,7 +1063,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
// The flag is boot-stable.
- mBackgroundNetworkRestricted = Flags.networkBlockedForTopSleepingAndAbove();
+ mBackgroundNetworkRestricted = ALWAYS_RESTRICT_BACKGROUND_NETWORK
+ && Flags.networkBlockedForTopSleepingAndAbove();
if (mBackgroundNetworkRestricted) {
// Firewall rules and UidBlockedState will get updated in
// updateRulesForGlobalChangeAL below.
@@ -1075,8 +1084,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final int cutpoint = mBackgroundNetworkRestricted ? PROCESS_STATE_UNKNOWN
: NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE;
- // TODO (b/319728914): Filter out the unnecessary changes when using no cutpoint.
-
mActivityManagerInternal.registerNetworkPolicyUidObserver(mUidObserver, changes,
cutpoint, "android");
mNetworkManager.registerObserver(mAlertObserver);
@@ -1185,6 +1192,51 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
final private IUidObserver mUidObserver = new UidObserver() {
+
+ /**
+ * Returns whether the uid state change information is relevant for the service. If the
+ * state information does not lead to any change in the network rules, it can safely be
+ * ignored.
+ */
+ @GuardedBy("mUidStateCallbackInfos")
+ private boolean isUidStateChangeRelevant(UidStateCallbackInfo previousInfo,
+ int newProcState, long newProcStateSeq, int newCapability) {
+ if (previousInfo.procStateSeq == -1) {
+ // No previous record. Always process the first state change callback.
+ return true;
+ }
+ if (newProcStateSeq <= previousInfo.procStateSeq) {
+ // Stale callback. Ignore.
+ return false;
+ }
+ final int previousProcState = previousInfo.procState;
+ if (mBackgroundNetworkRestricted && (previousProcState >= BACKGROUND_THRESHOLD_STATE)
+ != (newProcState >= BACKGROUND_THRESHOLD_STATE)) {
+ // Proc-state change crossed BACKGROUND_THRESHOLD_STATE: Network rules for the
+ // BACKGROUND chain may change.
+ return true;
+ }
+ if ((previousProcState <= TOP_THRESHOLD_STATE)
+ != (newProcState <= TOP_THRESHOLD_STATE)) {
+ // Proc-state change crossed TOP_THRESHOLD_STATE: Network rules for the
+ // LOW_POWER_STANDBY chain may change.
+ return true;
+ }
+ if ((previousProcState <= FOREGROUND_THRESHOLD_STATE)
+ != (newProcState <= FOREGROUND_THRESHOLD_STATE)) {
+ // Proc-state change crossed FOREGROUND_THRESHOLD_STATE: Network rules for many
+ // different chains may change.
+ return true;
+ }
+ final int networkCapabilities = PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK
+ | PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK;
+ if ((previousInfo.capability & networkCapabilities)
+ != (newCapability & networkCapabilities)) {
+ return true;
+ }
+ return false;
+ }
+
@Override public void onUidStateChanged(int uid, int procState, long procStateSeq,
@ProcessCapability int capability) {
synchronized (mUidStateCallbackInfos) {
@@ -1193,13 +1245,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
callbackInfo = new UidStateCallbackInfo();
mUidStateCallbackInfos.put(uid, callbackInfo);
}
- if (callbackInfo.procStateSeq == -1 || procStateSeq > callbackInfo.procStateSeq) {
+ if (isUidStateChangeRelevant(callbackInfo, procState, procStateSeq, capability)) {
callbackInfo.update(uid, procState, procStateSeq, capability);
- }
- if (!callbackInfo.isPending) {
- mUidEventHandler.obtainMessage(UID_MSG_STATE_CHANGED, callbackInfo)
- .sendToTarget();
- callbackInfo.isPending = true;
+ if (!callbackInfo.isPending) {
+ mUidEventHandler.obtainMessage(UID_MSG_STATE_CHANGED, callbackInfo)
+ .sendToTarget();
+ callbackInfo.isPending = true;
+ }
}
}
}
diff --git a/services/core/java/com/android/server/notification/Android.bp b/services/core/java/com/android/server/notification/Android.bp
index f26a25b63207..9be43581aed5 100644
--- a/services/core/java/com/android/server/notification/Android.bp
+++ b/services/core/java/com/android/server/notification/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_",
+}
+
java_aconfig_library {
name: "notification_flags_lib",
aconfig_declarations: "notification_flags",
@@ -9,4 +13,4 @@ aconfig_declarations {
srcs: [
"flags.aconfig",
],
-} \ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/notification/GroupHelper.java b/services/core/java/com/android/server/notification/GroupHelper.java
index e349fa36368d..babb6c219714 100644
--- a/services/core/java/com/android/server/notification/GroupHelper.java
+++ b/services/core/java/com/android/server/notification/GroupHelper.java
@@ -22,6 +22,8 @@ import static android.app.Notification.FLAG_GROUP_SUMMARY;
import static android.app.Notification.FLAG_LOCAL_ONLY;
import static android.app.Notification.FLAG_NO_CLEAR;
import static android.app.Notification.FLAG_ONGOING_EVENT;
+import static android.app.Notification.VISIBILITY_PRIVATE;
+import static android.app.Notification.VISIBILITY_PUBLIC;
import android.annotation.NonNull;
import android.app.Notification;
@@ -145,7 +147,8 @@ public class GroupHelper {
mUngroupedNotifications.getOrDefault(key, new ArrayMap<>());
NotificationAttributes attr = new NotificationAttributes(sbn.getNotification().flags,
- sbn.getNotification().getSmallIcon(), sbn.getNotification().color);
+ sbn.getNotification().getSmallIcon(), sbn.getNotification().color,
+ sbn.getNotification().visibility);
children.put(sbn.getKey(), attr);
mUngroupedNotifications.put(key, children);
@@ -158,25 +161,29 @@ public class GroupHelper {
if (notificationsToGroup.size() > 0) {
if (autogroupSummaryExists) {
NotificationAttributes attr = new NotificationAttributes(flags,
- sbn.getNotification().getSmallIcon(), sbn.getNotification().color);
+ sbn.getNotification().getSmallIcon(), sbn.getNotification().color,
+ VISIBILITY_PRIVATE);
if (Flags.autogroupSummaryIconUpdate()) {
- attr = updateAutobundledSummaryIcon(sbn.getPackageName(), childrenAttr, attr);
+ attr = updateAutobundledSummaryAttributes(sbn.getPackageName(), childrenAttr,
+ attr);
}
mCallback.updateAutogroupSummary(sbn.getUserId(), sbn.getPackageName(), attr);
} else {
Icon summaryIcon = sbn.getNotification().getSmallIcon();
int summaryIconColor = sbn.getNotification().color;
+ int summaryVisibility = VISIBILITY_PRIVATE;
if (Flags.autogroupSummaryIconUpdate()) {
- // Calculate the initial summary icon and icon color
- NotificationAttributes iconAttr = getAutobundledSummaryIconAndColor(
+ // Calculate the initial summary icon, icon color and visibility
+ NotificationAttributes iconAttr = getAutobundledSummaryAttributes(
sbn.getPackageName(), childrenAttr);
summaryIcon = iconAttr.icon;
summaryIconColor = iconAttr.iconColor;
+ summaryVisibility = iconAttr.visibility;
}
NotificationAttributes attr = new NotificationAttributes(flags, summaryIcon,
- summaryIconColor);
+ summaryIconColor, summaryVisibility);
mCallback.addAutoGroupSummary(sbn.getUserId(), sbn.getPackageName(), sbn.getKey(),
attr);
}
@@ -238,18 +245,19 @@ public class GroupHelper {
mCallback.removeAutoGroupSummary(userId, sbn.getPackageName());
} else {
NotificationAttributes attr = new NotificationAttributes(summaryFlags,
- sbn.getNotification().getSmallIcon(), sbn.getNotification().color);
- boolean iconUpdated = false;
+ sbn.getNotification().getSmallIcon(), sbn.getNotification().color,
+ VISIBILITY_PRIVATE);
+ boolean attributesUpdated = false;
if (Flags.autogroupSummaryIconUpdate()) {
- NotificationAttributes newAttr = updateAutobundledSummaryIcon(sbn.getPackageName(),
- childrenAttrs, attr);
+ NotificationAttributes newAttr = updateAutobundledSummaryAttributes(
+ sbn.getPackageName(), childrenAttrs, attr);
if (!newAttr.equals(attr)) {
- iconUpdated = true;
+ attributesUpdated = true;
attr = newAttr;
}
}
- if (updateSummaryFlags || iconUpdated) {
+ if (updateSummaryFlags || attributesUpdated) {
mCallback.updateAutogroupSummary(userId, sbn.getPackageName(), attr);
}
}
@@ -268,12 +276,13 @@ public class GroupHelper {
}
}
- NotificationAttributes getAutobundledSummaryIconAndColor(@NonNull String packageName,
+ NotificationAttributes getAutobundledSummaryAttributes(@NonNull String packageName,
@NonNull List<NotificationAttributes> childrenAttr) {
Icon newIcon = null;
boolean childrenHaveSameIcon = true;
int newColor = Notification.COLOR_INVALID;
boolean childrenHaveSameColor = true;
+ int newVisibility = VISIBILITY_PRIVATE;
// Both the icon drawable and the icon background color are updated according to this rule:
// - if all child icons are identical => use the common icon
@@ -296,6 +305,10 @@ public class GroupHelper {
childrenHaveSameColor = false;
}
}
+ // Check for visibility. If at least one child is public, then set to public
+ if (state.visibility == VISIBILITY_PUBLIC) {
+ newVisibility = VISIBILITY_PUBLIC;
+ }
}
if (!childrenHaveSameIcon) {
newIcon = getMonochromeAppIcon(packageName);
@@ -304,13 +317,13 @@ public class GroupHelper {
newColor = COLOR_DEFAULT;
}
- return new NotificationAttributes(0, newIcon, newColor);
+ return new NotificationAttributes(0, newIcon, newColor, newVisibility);
}
- NotificationAttributes updateAutobundledSummaryIcon(@NonNull String packageName,
+ NotificationAttributes updateAutobundledSummaryAttributes(@NonNull String packageName,
@NonNull List<NotificationAttributes> childrenAttr,
@NonNull NotificationAttributes oldAttr) {
- NotificationAttributes newAttr = getAutobundledSummaryIconAndColor(packageName,
+ NotificationAttributes newAttr = getAutobundledSummaryAttributes(packageName,
childrenAttr);
Icon newIcon = newAttr.icon;
int newColor = newAttr.iconColor;
@@ -321,7 +334,7 @@ public class GroupHelper {
newColor = oldAttr.iconColor;
}
- return new NotificationAttributes(oldAttr.flags, newIcon, newColor);
+ return new NotificationAttributes(oldAttr.flags, newIcon, newColor, newAttr.visibility);
}
/**
@@ -358,17 +371,20 @@ public class GroupHelper {
public final int flags;
public final int iconColor;
public final Icon icon;
+ public final int visibility;
- public NotificationAttributes(int flags, Icon icon, int iconColor) {
+ public NotificationAttributes(int flags, Icon icon, int iconColor, int visibility) {
this.flags = flags;
this.icon = icon;
this.iconColor = iconColor;
+ this.visibility = visibility;
}
public NotificationAttributes(@NonNull NotificationAttributes attr) {
this.flags = attr.flags;
this.icon = attr.icon;
this.iconColor = attr.iconColor;
+ this.visibility = attr.visibility;
}
@Override
@@ -379,12 +395,13 @@ public class GroupHelper {
if (!(o instanceof NotificationAttributes that)) {
return false;
}
- return flags == that.flags && iconColor == that.iconColor && icon.sameAs(that.icon);
+ return flags == that.flags && iconColor == that.iconColor && icon.sameAs(that.icon)
+ && visibility == that.visibility;
}
@Override
public int hashCode() {
- return Objects.hash(flags, iconColor, icon);
+ return Objects.hash(flags, iconColor, icon, visibility);
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index ea4e67a17e50..3a7ac0bd659d 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1038,15 +1038,17 @@ public class NotificationManagerService extends SystemService {
}
int oldFlags = summary.getSbn().getNotification().flags;
- boolean iconUpdated =
+ boolean attributesUpdated =
!summaryAttr.icon.sameAs(summary.getSbn().getNotification().getSmallIcon())
- || summaryAttr.iconColor != summary.getSbn().getNotification().color;
+ || summaryAttr.iconColor != summary.getSbn().getNotification().color
+ || summaryAttr.visibility != summary.getSbn().getNotification().visibility;
- if (oldFlags != summaryAttr.flags || iconUpdated) {
+ if (oldFlags != summaryAttr.flags || attributesUpdated) {
summary.getSbn().getNotification().flags =
summaryAttr.flags != GroupHelper.FLAG_INVALID ? summaryAttr.flags : oldFlags;
summary.getSbn().getNotification().setSmallIcon(summaryAttr.icon);
summary.getSbn().getNotification().color = summaryAttr.iconColor;
+ summary.getSbn().getNotification().visibility = summaryAttr.visibility;
mHandler.post(new EnqueueNotificationRunnable(userId, summary, isAppForeground,
mPostNotificationTrackerFactory.newTracker(null)));
}
@@ -1353,7 +1355,8 @@ public class NotificationManagerService extends SystemService {
nv.recycle();
reportUserInteraction(r);
mAssistants.notifyAssistantActionClicked(r, action, generatedByAssistant);
- // Notifications that have been interacted with don't need to be lifetime extended.
+ // Notifications that have been interacted with should no longer be lifetime
+ // extended.
if (lifetimeExtensionRefactor()) {
r.getSbn().getNotification().flags &= ~FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
}
@@ -1522,9 +1525,32 @@ public class NotificationManagerService extends SystemService {
@Override
public void onNotificationDirectReplied(String key) {
exitIdle();
+ String packageName = null;
+ final int packageImportance;
+ synchronized (mNotificationLock) {
+ NotificationRecord r = mNotificationsByKey.get(key);
+ if (r != null) {
+ packageName = r.getSbn().getPackageName();
+ }
+ }
+ if (lifetimeExtensionRefactor() && packageName != null) {
+ packageImportance = getPackageImportanceWithIdentity(packageName);
+ } else {
+ packageImportance = IMPORTANCE_NONE;
+ }
synchronized (mNotificationLock) {
NotificationRecord r = mNotificationsByKey.get(key);
if (r != null) {
+ // If the notification is already marked as lifetime extended before we record
+ // the new direct reply, there must have been a previous lifetime extension
+ // event, and the app has already cancelled the notification, or does not
+ // respond to direct replies with updates. So we need to update System UI
+ // immediately.
+ if (lifetimeExtensionRefactor()) {
+ maybeNotifySystemUiListenerLifetimeExtendedLocked(r,
+ r.getSbn().getPackageName(), packageImportance);
+ }
+
r.recordDirectReplied();
mMetricsLogger.write(r.getLogMaker()
.setCategory(MetricsEvent.NOTIFICATION_DIRECT_REPLY_ACTION)
@@ -1555,10 +1581,31 @@ public class NotificationManagerService extends SystemService {
@Override
public void onNotificationSmartReplySent(String key, int replyIndex, CharSequence reply,
int notificationLocation, boolean modifiedBeforeSending) {
-
+ String packageName = null;
+ final int packageImportance;
+ synchronized (mNotificationLock) {
+ NotificationRecord r = mNotificationsByKey.get(key);
+ if (r != null) {
+ packageName = r.getSbn().getPackageName();
+ }
+ }
+ if (lifetimeExtensionRefactor() && packageName != null) {
+ packageImportance = getPackageImportanceWithIdentity(packageName);
+ } else {
+ packageImportance = IMPORTANCE_NONE;
+ }
synchronized (mNotificationLock) {
NotificationRecord r = mNotificationsByKey.get(key);
if (r != null) {
+ // If the notification is already marked as lifetime extended before we record
+ // the new direct reply, there must have been a previous lifetime extension
+ // event, and the app has already cancelled the notification, or does not
+ // respond to direct replies with updates. So we need to update System UI
+ // immediately.
+ if (lifetimeExtensionRefactor()) {
+ maybeNotifySystemUiListenerLifetimeExtendedLocked(r,
+ r.getSbn().getPackageName(), packageImportance);
+ }
r.recordSmartReplied();
LogMaker logMaker = r.getLogMaker()
.setCategory(MetricsEvent.SMART_REPLY_ACTION)
@@ -1877,11 +1924,11 @@ public class NotificationManagerService extends SystemService {
true, record.getUserId(), REASON_TIMEOUT, null);
// If cancellation will be prevented due to lifetime extension, we send an
// update to system UI.
+ final int packageImportance = getPackageImportanceWithIdentity(
+ record.getSbn().getPackageName());
synchronized (mNotificationLock) {
maybeNotifySystemUiListenerLifetimeExtendedLocked(record,
- record.getSbn().getPackageName(),
- mActivityManager.getPackageImportance(
- record.getSbn().getPackageName()));
+ record.getSbn().getPackageName(), packageImportance);
}
} else {
cancelNotification(record.getSbn().getUid(),
@@ -2939,7 +2986,8 @@ public class NotificationManagerService extends SystemService {
public void addAutoGroupSummary(int userId, String pkg, String triggeringKey,
NotificationAttributes summaryAttr) {
NotificationRecord r = createAutoGroupSummary(userId, pkg, triggeringKey,
- summaryAttr.flags, summaryAttr.icon, summaryAttr.iconColor);
+ summaryAttr.flags, summaryAttr.icon, summaryAttr.iconColor,
+ summaryAttr.visibility);
if (r != null) {
final boolean isAppForeground =
mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND;
@@ -3849,7 +3897,7 @@ public class NotificationManagerService extends SystemService {
// If cancellation will be prevented due to lifetime extension, we send an update to
// system UI.
NotificationRecord record = null;
- final int packageImportance = mActivityManager.getPackageImportance(pkg);
+ final int packageImportance = getPackageImportanceWithIdentity(pkg);
synchronized (mNotificationLock) {
record = findNotificationLocked(pkg, tag, id, userId);
maybeNotifySystemUiListenerLifetimeExtendedLocked(record, pkg,
@@ -3874,10 +3922,9 @@ public class NotificationManagerService extends SystemService {
pkg, null, 0, FLAG_FOREGROUND_SERVICE | FLAG_USER_INITIATED_JOB
| FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY,
userId, REASON_APP_CANCEL_ALL);
+ final int packageImportance = getPackageImportanceWithIdentity(pkg);
// If cancellation will be prevented due to lifetime extension, we send updates
// to system UI.
- // In this case, we need to hold the lock to access these lists.
- final int packageImportance = mActivityManager.getPackageImportance(pkg);
synchronized (mNotificationLock) {
notifySystemUiListenerLifetimeExtendedListLocked(mNotificationList,
packageImportance);
@@ -5026,7 +5073,7 @@ public class NotificationManagerService extends SystemService {
pkg = info.component.getPackageName();
}
if (lifetimeExtensionRefactor()) {
- packageImportance = mActivityManager.getPackageImportance(pkg);
+ packageImportance = getPackageImportanceWithIdentity(pkg);
} else {
packageImportance = IMPORTANCE_NONE;
}
@@ -5345,7 +5392,7 @@ public class NotificationManagerService extends SystemService {
final int packageImportance;
try {
if (lifetimeExtensionRefactor()) {
- packageImportance = mActivityManager.getPackageImportance(pkg);
+ packageImportance = getPackageImportanceWithIdentity(pkg);
} else {
packageImportance = IMPORTANCE_NONE;
}
@@ -5718,6 +5765,14 @@ public class NotificationManagerService extends SystemService {
}
@Override
+ @Condition.State
+ public int getAutomaticZenRuleState(@NonNull String id) {
+ Objects.requireNonNull(id, "id is null");
+ enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRuleState");
+ return mZenModeHelper.getAutomaticZenRuleState(id);
+ }
+
+ @Override
public void setAutomaticZenRuleState(String id, Condition condition) {
Objects.requireNonNull(id, "id is null");
Objects.requireNonNull(condition, "Condition is null");
@@ -6725,7 +6780,7 @@ public class NotificationManagerService extends SystemService {
// Creates a 'fake' summary for a package that has exceeded the solo-notification limit.
NotificationRecord createAutoGroupSummary(int userId, String pkg, String triggeringKey,
- int flagsToSet, Icon summaryIcon, int summaryIconColor) {
+ int flagsToSet, Icon summaryIcon, int summaryIconColor, int summaryVisibilty) {
NotificationRecord summaryRecord = null;
boolean isPermissionFixed = mPermissionHelper.isPermissionFixed(pkg, userId);
synchronized (mNotificationLock) {
@@ -6760,6 +6815,7 @@ public class NotificationManagerService extends SystemService {
.setGroup(GroupHelper.AUTOGROUP_KEY)
.setFlag(flagsToSet, true)
.setColor(summaryIconColor)
+ .setVisibility(summaryVisibilty)
.build();
summaryNotification.extras.putAll(extras);
Intent appIntent = getContext().getPackageManager().getLaunchIntentForPackage(pkg);
@@ -7605,14 +7661,9 @@ public class NotificationManagerService extends SystemService {
}
}
- // Need escalated privileges to get package importance
- final long token = Binder.clearCallingIdentity();
- boolean isAppForeground;
- try {
- isAppForeground = mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ // Need escalated privileges to get package importance.
+ final int packageImportance = getPackageImportanceWithIdentity(pkg);
+ boolean isAppForeground = packageImportance == IMPORTANCE_FOREGROUND;
mHandler.post(new EnqueueNotificationRunnable(userId, r, isAppForeground, tracker));
return true;
}
@@ -7729,7 +7780,7 @@ public class NotificationManagerService extends SystemService {
notification.flags &= ~FLAG_FSI_REQUESTED_BUT_DENIED;
- // Apps should not create notifications that are lifetime extended.
+ // Apps cannot post notifications that are lifetime extended.
if (lifetimeExtensionRefactor()) {
notification.flags &= ~FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
}
@@ -7940,9 +7991,9 @@ public class NotificationManagerService extends SystemService {
NotificationRecord r = mNotificationsByKey.get(key);
packageName = r != null ? r.getSbn().getPackageName() : null;
}
+ final int packageImportance = getPackageImportanceWithIdentity(packageName);
boolean isAppForeground = packageName != null
- && mActivityManager.getPackageImportance(packageName)
- == IMPORTANCE_FOREGROUND;
+ && packageImportance == IMPORTANCE_FOREGROUND;
synchronized (mNotificationLock) {
NotificationRecord r = mNotificationsByKey.get(key);
if (r != null) {
@@ -8155,7 +8206,7 @@ public class NotificationManagerService extends SystemService {
try {
return mTelecomManager.isInManagedCall()
|| mTelecomManager.isInSelfManagedCall(pkg,
- UserHandle.getUserHandleForUid(uid), /* hasCrossUserAccess */ true);
+ /* hasCrossUserAccess */ true);
} catch (IllegalStateException ise) {
// Telecom is not ready (this is likely early boot), so there are no calls.
return false;
@@ -11863,6 +11914,18 @@ public class NotificationManagerService extends SystemService {
}
}
+ @FlaggedApi(FLAG_LIFETIME_EXTENSION_REFACTOR)
+ private int getPackageImportanceWithIdentity(String pkg) {
+ final long token = Binder.clearCallingIdentity();
+ final int packageImportance;
+ try {
+ packageImportance = mActivityManager.getPackageImportance(pkg);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return packageImportance;
+ }
+
public class NotificationListeners extends ManagedServices {
static final String TAG_ENABLED_NOTIFICATION_LISTENERS = "enabled_listeners";
static final String TAG_REQUESTED_LISTENERS = "request_listeners";
@@ -11987,8 +12050,10 @@ public class NotificationManagerService extends SystemService {
@Override
public void onServiceAdded(ManagedServiceInfo info) {
if (lifetimeExtensionRefactor()) {
- // Only System or System UI can call registerSystemService, so if the caller is not
- // system, we know it's system UI.
+ // Generally, only System or System UI should have the permissions to call
+ // registerSystemService.
+ // isCallerSystemorPhone tells us whether the caller is System. Then, if it's not
+ // the system, we know it's system UI.
info.isSystemUi = !isCallerSystemOrPhone();
}
final INotificationListener listener = (INotificationListener) info.service;
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 54de1976edb6..eed0eecb0a22 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -123,6 +123,7 @@ import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -593,20 +594,20 @@ public class ZenModeHelper {
if (mConfig == null) {
return;
}
+ ZenModeConfig newConfig = mConfig.copy();
+ ZenRule rule = newConfig.automaticRules.get(implicitRuleId(callingPkg));
if (zenMode == Global.ZEN_MODE_OFF) {
// Deactivate implicit rule if it exists and is active; otherwise ignore.
- ZenRule rule = mConfig.automaticRules.get(implicitRuleId(callingPkg));
if (rule != null) {
Condition deactivated = new Condition(rule.conditionId,
mContext.getString(R.string.zen_mode_implicit_deactivated),
Condition.STATE_FALSE);
- setAutomaticZenRuleState(rule.id, deactivated, UPDATE_ORIGIN_APP, callingUid);
+ setAutomaticZenRuleStateLocked(newConfig, Collections.singletonList(rule),
+ deactivated, UPDATE_ORIGIN_APP, callingUid);
}
} else {
// Either create a new rule with a default ZenPolicy, or update an existing rule's
// filter value. In both cases, also activate (and unsnooze) it.
- ZenModeConfig newConfig = mConfig.copy();
- ZenRule rule = newConfig.automaticRules.get(implicitRuleId(callingPkg));
if (rule == null) {
rule = newImplicitZenRule(callingPkg);
@@ -856,6 +857,20 @@ public class ZenModeHelper {
}
}
+ @Condition.State
+ int getAutomaticZenRuleState(String id) {
+ synchronized (mConfigLock) {
+ if (mConfig == null) {
+ return Condition.STATE_UNKNOWN;
+ }
+ ZenRule rule = mConfig.automaticRules.get(id);
+ if (rule == null || !canManageAutomaticZenRule(rule)) {
+ return Condition.STATE_UNKNOWN;
+ }
+ return rule.condition != null ? rule.condition.state : Condition.STATE_FALSE;
+ }
+ }
+
void setAutomaticZenRuleState(String id, Condition condition, @ConfigChangeOrigin int origin,
int callingUid) {
requirePublicOrigin("setAutomaticZenRuleState", origin);
@@ -864,9 +879,17 @@ public class ZenModeHelper {
if (mConfig == null) return;
newConfig = mConfig.copy();
- ArrayList<ZenRule> rules = new ArrayList<>();
- rules.add(newConfig.automaticRules.get(id));
- setAutomaticZenRuleStateLocked(newConfig, rules, condition, origin, callingUid);
+ ZenRule rule = newConfig.automaticRules.get(id);
+ if (Flags.modesApi()) {
+ if (rule != null && canManageAutomaticZenRule(rule)) {
+ setAutomaticZenRuleStateLocked(newConfig, Collections.singletonList(rule),
+ condition, origin, callingUid);
+ }
+ } else {
+ ArrayList<ZenRule> rules = new ArrayList<>();
+ rules.add(rule); // rule may be null and throw NPE in the next method.
+ setAutomaticZenRuleStateLocked(newConfig, rules, condition, origin, callingUid);
+ }
}
}
@@ -878,9 +901,15 @@ public class ZenModeHelper {
if (mConfig == null) return;
newConfig = mConfig.copy();
- setAutomaticZenRuleStateLocked(newConfig,
- findMatchingRules(newConfig, ruleDefinition, condition),
- condition, origin, callingUid);
+ List<ZenRule> matchingRules = findMatchingRules(newConfig, ruleDefinition, condition);
+ if (Flags.modesApi()) {
+ for (int i = matchingRules.size() - 1; i >= 0; i--) {
+ if (!canManageAutomaticZenRule(matchingRules.get(i))) {
+ matchingRules.remove(i);
+ }
+ }
+ }
+ setAutomaticZenRuleStateLocked(newConfig, matchingRules, condition, origin, callingUid);
}
}
@@ -900,8 +929,9 @@ public class ZenModeHelper {
}
}
- private List<ZenRule> findMatchingRules(ZenModeConfig config, Uri id, Condition condition) {
- List<ZenRule> matchingRules= new ArrayList<>();
+ private static List<ZenRule> findMatchingRules(ZenModeConfig config, Uri id,
+ Condition condition) {
+ List<ZenRule> matchingRules = new ArrayList<>();
if (ruleMatches(id, condition, config.manualRule)) {
matchingRules.add(config.manualRule);
} else {
@@ -914,7 +944,7 @@ public class ZenModeHelper {
return matchingRules;
}
- private boolean ruleMatches(Uri id, Condition condition, ZenRule rule) {
+ private static boolean ruleMatches(Uri id, Condition condition, ZenRule rule) {
if (id == null || rule == null || rule.conditionId == null) return false;
if (!rule.conditionId.equals(id)) return false;
if (Objects.equals(condition, rule.condition)) return false;
@@ -1866,12 +1896,14 @@ public class ZenModeHelper {
if (rule.zenMode == Global.ZEN_MODE_NO_INTERRUPTIONS) {
policy.apply(new ZenPolicy.Builder()
.disallowAllSounds()
+ .allowPriorityChannels(false)
.build());
} else if (rule.zenMode == Global.ZEN_MODE_ALARMS) {
policy.apply(new ZenPolicy.Builder()
.disallowAllSounds()
.allowAlarms(true)
.allowMedia(true)
+ .allowPriorityChannels(false)
.build());
} else if (rule.zenPolicy != null) {
policy.apply(rule.zenPolicy);
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index 722654a9102c..43361ed97bc6 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -50,15 +50,6 @@ flag {
}
flag {
- name: "sensitive_notification_app_protection"
- namespace: "systemui"
- description: "This flag controls the sensitive notification app protections while screen sharing"
- bug: "312784351"
- # Referenced in WM where WM starts before DeviceConfig
- is_fixed_read_only: true
-}
-
-flag {
name: "notification_reduce_messagequeue_usage"
namespace: "systemui"
description: "When this flag is on, NMS will no longer call removeMessage() and hasCallbacks() on Handler"
@@ -77,4 +68,14 @@ flag {
namespace: "systemui"
description: "This flag enables memory restriction of notifications holding custom views with Uri Bitmaps"
bug: "270553691"
+}
+
+flag {
+ name: "notification_hide_unused_channels"
+ namespace: "systemui"
+ description: "By default, hide non-blocked notification channels that haven't sent a notification in the last 2 weeks"
+ bug: "322536537"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
} \ No newline at end of file
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index d987622676b5..872952299055 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -1143,10 +1143,9 @@ public final class OverlayManagerService extends SystemService {
};
private static final class PackageManagerHelperImpl implements PackageManagerHelper {
- private static final class PackageStateUsers {
+ private static class PackageStateUsers {
private PackageState mPackageState;
- private Boolean mDefinesOverlayable = null;
- private final ArraySet<Integer> mInstalledUsers = new ArraySet<>();
+ private final Set<Integer> mInstalledUsers = new ArraySet<>();
private PackageStateUsers(@NonNull PackageState packageState) {
this.mPackageState = packageState;
}
@@ -1196,11 +1195,13 @@ public final class OverlayManagerService extends SystemService {
return userPackages;
}
- private PackageStateUsers getRawPackageStateForUser(@NonNull final String packageName,
+ @Override
+ @Nullable
+ public PackageState getPackageStateForUser(@NonNull final String packageName,
final int userId) {
final PackageStateUsers pkg = mCache.get(packageName);
if (pkg != null && pkg.mInstalledUsers.contains(userId)) {
- return pkg;
+ return pkg.mPackageState;
}
try {
if (!mPackageManager.isPackageAvailable(packageName, userId)) {
@@ -1214,14 +1215,8 @@ public final class OverlayManagerService extends SystemService {
return addPackageUser(packageName, userId);
}
- @Override
- public PackageState getPackageStateForUser(@NonNull final String packageName,
- final int userId) {
- final PackageStateUsers pkg = getRawPackageStateForUser(packageName, userId);
- return pkg != null ? pkg.mPackageState : null;
- }
-
- private PackageStateUsers addPackageUser(@NonNull final String packageName,
+ @NonNull
+ private PackageState addPackageUser(@NonNull final String packageName,
final int user) {
final PackageState pkg = mPackageManagerInternal.getPackageStateInternal(packageName);
if (pkg == null) {
@@ -1233,20 +1228,20 @@ public final class OverlayManagerService extends SystemService {
}
@NonNull
- private PackageStateUsers addPackageUser(@NonNull final PackageState pkg,
+ private PackageState addPackageUser(@NonNull final PackageState pkg,
final int user) {
PackageStateUsers pkgUsers = mCache.get(pkg.getPackageName());
if (pkgUsers == null) {
pkgUsers = new PackageStateUsers(pkg);
mCache.put(pkg.getPackageName(), pkgUsers);
- } else if (pkgUsers.mPackageState != pkg) {
+ } else {
pkgUsers.mPackageState = pkg;
- pkgUsers.mDefinesOverlayable = null;
}
pkgUsers.mInstalledUsers.add(user);
- return pkgUsers;
+ return pkgUsers.mPackageState;
}
+
@NonNull
private void removePackageUser(@NonNull final String packageName, final int user) {
final PackageStateUsers pkgUsers = mCache.get(packageName);
@@ -1264,15 +1259,15 @@ public final class OverlayManagerService extends SystemService {
}
}
+ @Nullable
public PackageState onPackageAdded(@NonNull final String packageName, final int userId) {
- final var pu = addPackageUser(packageName, userId);
- return pu != null ? pu.mPackageState : null;
+ return addPackageUser(packageName, userId);
}
+ @Nullable
public PackageState onPackageUpdated(@NonNull final String packageName,
final int userId) {
- final var pu = addPackageUser(packageName, userId);
- return pu != null ? pu.mPackageState : null;
+ return addPackageUser(packageName, userId);
}
public void onPackageRemoved(@NonNull final String packageName, final int userId) {
@@ -1312,30 +1307,22 @@ public final class OverlayManagerService extends SystemService {
return (pkgs.length == 0) ? null : pkgs[0];
}
+ @Nullable
@Override
public OverlayableInfo getOverlayableForTarget(@NonNull String packageName,
@NonNull String targetOverlayableName, int userId)
throws IOException {
- final var psu = getRawPackageStateForUser(packageName, userId);
- final var pkg = (psu == null || psu.mPackageState == null)
- ? null : psu.mPackageState.getAndroidPackage();
+ var packageState = getPackageStateForUser(packageName, userId);
+ var pkg = packageState == null ? null : packageState.getAndroidPackage();
if (pkg == null) {
throw new IOException("Unable to get target package");
}
- if (Boolean.FALSE.equals(psu.mDefinesOverlayable)) {
- return null;
- }
-
ApkAssets apkAssets = null;
try {
apkAssets = ApkAssets.loadFromPath(pkg.getSplits().get(0).getPath(),
ApkAssets.PROPERTY_ONLY_OVERLAYABLES);
- if (psu.mDefinesOverlayable == null) {
- psu.mDefinesOverlayable = apkAssets.definesOverlayable();
- }
- return Boolean.FALSE.equals(psu.mDefinesOverlayable)
- ? null : apkAssets.getOverlayableInfo(targetOverlayableName);
+ return apkAssets.getOverlayableInfo(targetOverlayableName);
} finally {
if (apkAssets != null) {
try {
@@ -1349,29 +1336,24 @@ public final class OverlayManagerService extends SystemService {
@Override
public boolean doesTargetDefineOverlayable(String targetPackageName, int userId)
throws IOException {
- final var psu = getRawPackageStateForUser(targetPackageName, userId);
- var pkg = (psu == null || psu.mPackageState == null)
- ? null : psu.mPackageState.getAndroidPackage();
+ var packageState = getPackageStateForUser(targetPackageName, userId);
+ var pkg = packageState == null ? null : packageState.getAndroidPackage();
if (pkg == null) {
throw new IOException("Unable to get target package");
}
- if (psu.mDefinesOverlayable == null) {
- ApkAssets apkAssets = null;
- try {
- apkAssets = ApkAssets.loadFromPath(pkg.getSplits().get(0).getPath(),
- ApkAssets.PROPERTY_ONLY_OVERLAYABLES);
- psu.mDefinesOverlayable = apkAssets.definesOverlayable();
- } finally {
- if (apkAssets != null) {
- try {
- apkAssets.close();
- } catch (Throwable ignored) {
- }
+ ApkAssets apkAssets = null;
+ try {
+ apkAssets = ApkAssets.loadFromPath(pkg.getSplits().get(0).getPath());
+ return apkAssets.definesOverlayable();
+ } finally {
+ if (apkAssets != null) {
+ try {
+ apkAssets.close();
+ } catch (Throwable ignored) {
}
}
}
- return psu.mDefinesOverlayable;
}
@Override
diff --git a/services/core/java/com/android/server/ondeviceintelligence/OWNERS b/services/core/java/com/android/server/ondeviceintelligence/OWNERS
new file mode 100644
index 000000000000..09774f78d712
--- /dev/null
+++ b/services/core/java/com/android/server/ondeviceintelligence/OWNERS
@@ -0,0 +1 @@
+file:/core/java/android/app/ondeviceintelligence/OWNERS
diff --git a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
new file mode 100644
index 000000000000..a4c4347b7cef
--- /dev/null
+++ b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
@@ -0,0 +1,419 @@
+/*
+ * Copyright (C) 2024 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.ondeviceintelligence;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.app.AppGlobals;
+import android.app.ondeviceintelligence.Content;
+import android.app.ondeviceintelligence.DownloadCallback;
+import android.app.ondeviceintelligence.Feature;
+import android.app.ondeviceintelligence.IDownloadCallback;
+import android.app.ondeviceintelligence.IFeatureCallback;
+import android.app.ondeviceintelligence.IFeatureDetailsCallback;
+import android.app.ondeviceintelligence.IListFeaturesCallback;
+import android.app.ondeviceintelligence.IOnDeviceIntelligenceManager;
+import android.app.ondeviceintelligence.IProcessingSignal;
+import android.app.ondeviceintelligence.IResponseCallback;
+import android.app.ondeviceintelligence.IStreamingResponseCallback;
+import android.app.ondeviceintelligence.ITokenCountCallback;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.os.ICancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.DeviceConfig;
+import android.service.ondeviceintelligence.IOnDeviceTrustedInferenceService;
+import android.service.ondeviceintelligence.IRemoteStorageService;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.infra.AndroidFuture;
+import com.android.internal.infra.ServiceConnector;
+import com.android.internal.os.BackgroundThread;
+import com.android.server.SystemService;
+
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * This is the system service for handling calls on the {@link OnDeviceIntelligenceManager}. This
+ * service holds connection references to the underlying remote services i.e. the isolated service
+ * {@link android.service.ondeviceintelligence.OnDeviceTrustedInferenceService} and a regular
+ * service counter part {@link android.service.ondeviceintelligence.OnDeviceIntelligenceService}.
+ *
+ * Note: Both the remote services run under the SYSTEM user, as we cannot have separate instance of
+ * the Inference service for each user, due to possible high memory footprint.
+ *
+ * @hide
+ */
+public class OnDeviceIntelligenceManagerService extends SystemService {
+
+ private static final String TAG = OnDeviceIntelligenceManagerService.class.getSimpleName();
+ private static final String KEY_SERVICE_ENABLED = "service_enabled";
+
+ /** Default value in absence of {@link DeviceConfig} override. */
+ private static final boolean DEFAULT_SERVICE_ENABLED = true;
+ private static final String NAMESPACE_ON_DEVICE_INTELLIGENCE = "ondeviceintelligence";
+
+ private final Context mContext;
+ protected final Object mLock = new Object();
+
+
+ private RemoteOnDeviceTrustedInferenceService mRemoteInferenceService;
+ private RemoteOnDeviceIntelligenceService mRemoteOnDeviceIntelligenceService;
+ volatile boolean mIsServiceEnabled;
+
+ public OnDeviceIntelligenceManagerService(Context context) {
+ super(context);
+ mContext = context;
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(
+ Context.ON_DEVICE_INTELLIGENCE_SERVICE, new OnDeviceIntelligenceManagerInternal(),
+ /* allowIsolated = */true);
+ }
+
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+ DeviceConfig.addOnPropertiesChangedListener(
+ NAMESPACE_ON_DEVICE_INTELLIGENCE,
+ BackgroundThread.getExecutor(),
+ (properties) -> onDeviceConfigChange(properties.getKeyset()));
+
+ mIsServiceEnabled = isServiceEnabled();
+ }
+ }
+
+ private void onDeviceConfigChange(@NonNull Set<String> keys) {
+ if (keys.contains(KEY_SERVICE_ENABLED)) {
+ mIsServiceEnabled = isServiceEnabled();
+ }
+ }
+
+ private boolean isServiceEnabled() {
+ return DeviceConfig.getBoolean(
+ NAMESPACE_ON_DEVICE_INTELLIGENCE,
+ KEY_SERVICE_ENABLED, DEFAULT_SERVICE_ENABLED);
+ }
+
+ private final class OnDeviceIntelligenceManagerInternal extends
+ IOnDeviceIntelligenceManager.Stub {
+ @Override
+ public void getVersion(RemoteCallback remoteCallback) throws RemoteException {
+ Slog.i(TAG, "OnDeviceIntelligenceManagerInternal getVersion");
+ Objects.requireNonNull(remoteCallback);
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+ if (!mIsServiceEnabled) {
+ Slog.w(TAG, "Service not available");
+ remoteCallback.sendResult(null);
+ return;
+ }
+ ensureRemoteIntelligenceServiceInitialized();
+ mRemoteOnDeviceIntelligenceService.post(
+ service -> service.getVersion(remoteCallback));
+ }
+
+ @Override
+ public void getFeature(int id, IFeatureCallback featureCallback) throws RemoteException {
+ Slog.i(TAG, "OnDeviceIntelligenceManagerInternal getFeatures");
+ Objects.requireNonNull(featureCallback);
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+ if (!mIsServiceEnabled) {
+ Slog.w(TAG, "Service not available");
+ featureCallback.onFailure(
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException.ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE,
+ "OnDeviceIntelligenceManagerService is unavailable",
+ new PersistableBundle());
+ return;
+ }
+ ensureRemoteIntelligenceServiceInitialized();
+ mRemoteOnDeviceIntelligenceService.post(
+ service -> service.getFeature(id, featureCallback));
+ }
+
+ @Override
+ public void listFeatures(IListFeaturesCallback listFeaturesCallback)
+ throws RemoteException {
+ Slog.i(TAG, "OnDeviceIntelligenceManagerInternal getFeatures");
+ Objects.requireNonNull(listFeaturesCallback);
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+ if (!mIsServiceEnabled) {
+ Slog.w(TAG, "Service not available");
+ listFeaturesCallback.onFailure(
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException.ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE,
+ "OnDeviceIntelligenceManagerService is unavailable",
+ new PersistableBundle());
+ return;
+ }
+ ensureRemoteIntelligenceServiceInitialized();
+ mRemoteOnDeviceIntelligenceService.post(
+ service -> service.listFeatures(listFeaturesCallback));
+ }
+
+ @Override
+ public void getFeatureDetails(Feature feature,
+ IFeatureDetailsCallback featureDetailsCallback)
+ throws RemoteException {
+ Slog.i(TAG, "OnDeviceIntelligenceManagerInternal getFeatureStatus");
+ Objects.requireNonNull(feature);
+ Objects.requireNonNull(featureDetailsCallback);
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+ if (!mIsServiceEnabled) {
+ Slog.w(TAG, "Service not available");
+ featureDetailsCallback.onFailure(
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException.ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE,
+ "OnDeviceIntelligenceManagerService is unavailable",
+ new PersistableBundle());
+ return;
+ }
+ ensureRemoteIntelligenceServiceInitialized();
+ mRemoteOnDeviceIntelligenceService.post(
+ service -> service.getFeatureDetails(feature, featureDetailsCallback));
+ }
+
+ @Override
+ public void requestFeatureDownload(Feature feature, ICancellationSignal cancellationSignal,
+ IDownloadCallback downloadCallback) throws RemoteException {
+ Slog.i(TAG, "OnDeviceIntelligenceManagerInternal requestFeatureDownload");
+ Objects.requireNonNull(feature);
+ Objects.requireNonNull(downloadCallback);
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+ if (!mIsServiceEnabled) {
+ Slog.w(TAG, "Service not available");
+ downloadCallback.onDownloadFailed(
+ DownloadCallback.DOWNLOAD_FAILURE_STATUS_UNAVAILABLE,
+ "OnDeviceIntelligenceManagerService is unavailable",
+ new PersistableBundle());
+ }
+ ensureRemoteIntelligenceServiceInitialized();
+ mRemoteOnDeviceIntelligenceService.post(
+ service -> service.requestFeatureDownload(feature, cancellationSignal,
+ downloadCallback));
+ }
+
+
+ @Override
+ public void requestTokenCount(Feature feature,
+ Content request, ICancellationSignal cancellationSignal,
+ ITokenCountCallback tokenCountcallback) throws RemoteException {
+ Slog.i(TAG, "OnDeviceIntelligenceManagerInternal prepareFeatureProcessing");
+ Objects.requireNonNull(feature);
+ Objects.requireNonNull(request);
+ Objects.requireNonNull(tokenCountcallback);
+
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+ if (!mIsServiceEnabled) {
+ Slog.w(TAG, "Service not available");
+ tokenCountcallback.onFailure(
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException.ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE,
+ "OnDeviceIntelligenceManagerService is unavailable",
+ new PersistableBundle());
+ }
+ ensureRemoteTrustedInferenceServiceInitialized();
+ mRemoteInferenceService.post(
+ service -> service.requestTokenCount(feature, request, cancellationSignal,
+ tokenCountcallback));
+ }
+
+ @Override
+ public void processRequest(Feature feature,
+ Content request,
+ int requestType,
+ ICancellationSignal cancellationSignal,
+ IProcessingSignal processingSignal,
+ IResponseCallback responseCallback)
+ throws RemoteException {
+ Slog.i(TAG, "OnDeviceIntelligenceManagerInternal processRequest");
+ Objects.requireNonNull(feature);
+ Objects.requireNonNull(responseCallback);
+ Objects.requireNonNull(request);
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+ if (!mIsServiceEnabled) {
+ Slog.w(TAG, "Service not available");
+ responseCallback.onFailure(
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException.PROCESSING_ERROR_SERVICE_UNAVAILABLE,
+ "OnDeviceIntelligenceManagerService is unavailable",
+ new PersistableBundle());
+ }
+ ensureRemoteTrustedInferenceServiceInitialized();
+ mRemoteInferenceService.post(
+ service -> service.processRequest(feature, request, requestType,
+ cancellationSignal, processingSignal,
+ responseCallback));
+ }
+
+ @Override
+ public void processRequestStreaming(Feature feature,
+ Content request,
+ int requestType,
+ ICancellationSignal cancellationSignal,
+ IProcessingSignal processingSignal,
+ IStreamingResponseCallback streamingCallback) throws RemoteException {
+ Slog.i(TAG, "OnDeviceIntelligenceManagerInternal processRequestStreaming");
+ Objects.requireNonNull(feature);
+ Objects.requireNonNull(request);
+ Objects.requireNonNull(streamingCallback);
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+ if (!mIsServiceEnabled) {
+ Slog.w(TAG, "Service not available");
+ streamingCallback.onFailure(
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException.PROCESSING_ERROR_SERVICE_UNAVAILABLE,
+ "OnDeviceIntelligenceManagerService is unavailable",
+ new PersistableBundle());
+ }
+ ensureRemoteTrustedInferenceServiceInitialized();
+ mRemoteInferenceService.post(
+ service -> service.processRequestStreaming(feature, request, requestType,
+ cancellationSignal, processingSignal,
+ streamingCallback));
+ }
+ }
+
+ private void ensureRemoteIntelligenceServiceInitialized() throws RemoteException {
+ synchronized (mLock) {
+ if (mRemoteOnDeviceIntelligenceService == null) {
+ String serviceName = mContext.getResources().getString(
+ R.string.config_defaultOnDeviceIntelligenceService);
+ validateService(serviceName, false);
+ mRemoteOnDeviceIntelligenceService = new RemoteOnDeviceIntelligenceService(mContext,
+ ComponentName.unflattenFromString(serviceName),
+ UserHandle.SYSTEM.getIdentifier());
+ }
+ }
+ }
+
+ private void ensureRemoteTrustedInferenceServiceInitialized() throws RemoteException {
+ synchronized (mLock) {
+ if (mRemoteInferenceService == null) {
+ String serviceName = mContext.getResources().getString(
+ R.string.config_defaultOnDeviceTrustedInferenceService);
+ validateService(serviceName, true);
+ mRemoteInferenceService = new RemoteOnDeviceTrustedInferenceService(mContext,
+ ComponentName.unflattenFromString(serviceName),
+ UserHandle.SYSTEM.getIdentifier());
+ mRemoteInferenceService.setServiceLifecycleCallbacks(
+ new ServiceConnector.ServiceLifecycleCallbacks<>() {
+ @Override
+ public void onConnected(
+ @NonNull IOnDeviceTrustedInferenceService service) {
+ try {
+ service.registerRemoteStorageService(
+ getIRemoteStorageService());
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to send connected event", ex);
+ }
+ }
+ });
+ }
+ }
+ }
+
+ @NonNull
+ private IRemoteStorageService.Stub getIRemoteStorageService() {
+ return new IRemoteStorageService.Stub() {
+ @Override
+ public void getReadOnlyFileDescriptor(
+ String filePath,
+ AndroidFuture<ParcelFileDescriptor> future) {
+ mRemoteOnDeviceIntelligenceService.post(
+ service -> service.getReadOnlyFileDescriptor(
+ filePath, future));
+ }
+
+ @Override
+ public void getReadOnlyFeatureFileDescriptorMap(
+ Feature feature,
+ RemoteCallback remoteCallback)
+ throws RemoteException {
+ mRemoteOnDeviceIntelligenceService.post(
+ service -> service.getReadOnlyFeatureFileDescriptorMap(
+ feature, remoteCallback));
+ }
+ };
+ }
+
+ @GuardedBy("mLock")
+ private void validateService(String serviceName, boolean checkIsolated)
+ throws RemoteException {
+ if (TextUtils.isEmpty(serviceName)) {
+ throw new RuntimeException("");
+ }
+ ComponentName serviceComponent = ComponentName.unflattenFromString(
+ serviceName);
+ ServiceInfo serviceInfo = AppGlobals.getPackageManager().getServiceInfo(
+ serviceComponent,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, 0);
+ if (serviceInfo != null) {
+ if (!checkIsolated) {
+ checkServiceRequiresPermission(serviceInfo,
+ Manifest.permission.BIND_ON_DEVICE_INTELLIGENCE_SERVICE);
+ return;
+ }
+
+ checkServiceRequiresPermission(serviceInfo,
+ Manifest.permission.BIND_ON_DEVICE_TRUSTED_SERVICE);
+ if (!isIsolatedService(serviceInfo)) {
+ throw new SecurityException(
+ "Call required an isolated service, but the configured service: "
+ + serviceName + ", is not isolated");
+ }
+ } else {
+ throw new RuntimeException(
+ "Could not find service info for serviceName: " + serviceName);
+ }
+ }
+
+ private static void checkServiceRequiresPermission(ServiceInfo serviceInfo,
+ String requiredPermission) {
+ final String permission = serviceInfo.permission;
+ if (!requiredPermission.equals(permission)) {
+ throw new SecurityException(String.format(
+ "Service %s requires %s permission. Found %s permission",
+ serviceInfo.getComponentName(),
+ requiredPermission,
+ serviceInfo.permission));
+ }
+ }
+
+ @GuardedBy("mLock")
+ private boolean isIsolatedService(@NonNull ServiceInfo serviceInfo) {
+ return (serviceInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0
+ && (serviceInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) == 0;
+ }
+}
diff --git a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java
new file mode 100644
index 000000000000..48258d7bea72
--- /dev/null
+++ b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2024 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.ondeviceintelligence;
+
+import static android.content.Context.BIND_FOREGROUND_SERVICE;
+import static android.content.Context.BIND_INCLUDE_CAPABILITIES;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.service.ondeviceintelligence.IOnDeviceIntelligenceService;
+import android.service.ondeviceintelligence.OnDeviceIntelligenceService;
+
+import com.android.internal.infra.ServiceConnector;
+
+/**
+ * Manages the connection to the remote on-device intelligence service. Also, handles unbinding
+ * logic set by the service implementation via a Secure Settings flag.
+ */
+public class RemoteOnDeviceIntelligenceService extends
+ ServiceConnector.Impl<IOnDeviceIntelligenceService> {
+ private static final String TAG =
+ RemoteOnDeviceIntelligenceService.class.getSimpleName();
+
+ RemoteOnDeviceIntelligenceService(Context context, ComponentName serviceName,
+ int userId) {
+ super(context, new Intent(
+ OnDeviceIntelligenceService.SERVICE_INTERFACE).setComponent(serviceName),
+ BIND_FOREGROUND_SERVICE | BIND_INCLUDE_CAPABILITIES, userId,
+ IOnDeviceIntelligenceService.Stub::asInterface);
+
+ // Bind right away
+ connect();
+ }
+
+ @Override
+ protected long getAutoDisconnectTimeoutMs() {
+ // Disable automatic unbinding.
+ // TODO: add logic to fetch this flag via SecureSettings.
+ return -1;
+ }
+}
diff --git a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceTrustedInferenceService.java b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceTrustedInferenceService.java
new file mode 100644
index 000000000000..cc8e78804bb6
--- /dev/null
+++ b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceTrustedInferenceService.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2024 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.ondeviceintelligence;
+
+import static android.content.Context.BIND_FOREGROUND_SERVICE;
+import static android.content.Context.BIND_INCLUDE_CAPABILITIES;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.service.ondeviceintelligence.IOnDeviceTrustedInferenceService;
+import android.service.ondeviceintelligence.OnDeviceTrustedInferenceService;
+
+import com.android.internal.infra.ServiceConnector;
+
+
+/**
+ * Manages the connection to the remote on-device trusted inference service. Also, handles unbinding
+ * logic set by the service implementation via a SecureSettings flag.
+ */
+public class RemoteOnDeviceTrustedInferenceService extends
+ ServiceConnector.Impl<IOnDeviceTrustedInferenceService> {
+ /**
+ * Creates an instance of {@link ServiceConnector}
+ *
+ * See {@code protected} methods for optional parameters you can override.
+ *
+ * @param context to be used for {@link Context#bindServiceAsUser binding} and
+ * {@link Context#unbindService unbinding}
+ * @param userId to be used for {@link Context#bindServiceAsUser binding}
+ */
+ RemoteOnDeviceTrustedInferenceService(Context context, ComponentName serviceName,
+ int userId) {
+ super(context, new Intent(
+ OnDeviceTrustedInferenceService.SERVICE_INTERFACE).setComponent(serviceName),
+ BIND_FOREGROUND_SERVICE | BIND_INCLUDE_CAPABILITIES, userId,
+ IOnDeviceTrustedInferenceService.Stub::asInterface);
+
+ // Bind right away
+ connect();
+ }
+
+
+ @Override
+ protected long getAutoDisconnectTimeoutMs() {
+ // Disable automatic unbinding.
+ // TODO: add logic to fetch this flag via SecureSettings.
+ return -1;
+ }
+}
diff --git a/services/core/java/com/android/server/pdb/PersistentDataBlockService.java b/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
index 133fc8faf14d..5ad550722c93 100644
--- a/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
@@ -143,7 +143,8 @@ public class PersistentDataBlockService extends SystemService {
// Magic number to mark block device as adhering to the format consumed by this service
private static final int PARTITION_TYPE_MARKER = 0x19901873;
/** Size of the block reserved for FRP credential, including 4 bytes for the size header. */
- private static final int FRP_CREDENTIAL_RESERVED_SIZE = 1000;
+ @VisibleForTesting
+ static final int FRP_CREDENTIAL_RESERVED_SIZE = 1000;
/** Maximum size of the FRP credential handle that can be stored. */
@VisibleForTesting
static final int MAX_FRP_CREDENTIAL_HANDLE_SIZE = FRP_CREDENTIAL_RESERVED_SIZE - 4;
@@ -158,7 +159,8 @@ public class PersistentDataBlockService extends SystemService {
/**
* Size of the block reserved for Test Harness Mode data, including 4 bytes for the size header.
*/
- private static final int TEST_MODE_RESERVED_SIZE = 10000;
+ @VisibleForTesting
+ static final int TEST_MODE_RESERVED_SIZE = 10000;
/** Maximum size of the Test Harness Mode data that can be stored. */
@VisibleForTesting
static final int MAX_TEST_MODE_DATA_SIZE = TEST_MODE_RESERVED_SIZE - 4;
@@ -393,7 +395,8 @@ public class PersistentDataBlockService extends SystemService {
return totalDataSize;
}
- private long getBlockDeviceSize() {
+ @VisibleForTesting
+ long getBlockDeviceSize() {
synchronized (mLock) {
if (mBlockDeviceSize == -1) {
if (mIsFileBacked) {
@@ -553,26 +556,33 @@ public class PersistentDataBlockService extends SystemService {
channel.write(buf);
channel.force(true);
- // 3. skip the test mode data and leave it unformatted.
+ // 3. Write the default FRP secret (all zeros).
+ if (mFrpEnforced) {
+ Slog.i(TAG, "Writing FRP secret magic");
+ channel.write(ByteBuffer.wrap(FRP_SECRET_MAGIC));
+
+ Slog.i(TAG, "Writing default FRP secret");
+ channel.write(ByteBuffer.allocate(FRP_SECRET_SIZE));
+ channel.force(true);
+
+ mFrpActive = false;
+ }
+
+ // 4. skip the test mode data and leave it unformatted.
// This is for a feature that enables testing.
channel.position(channel.position() + TEST_MODE_RESERVED_SIZE);
- // 4. wipe the FRP_CREDENTIAL explicitly
+ // 5. wipe the FRP_CREDENTIAL explicitly
buf = ByteBuffer.allocate(FRP_CREDENTIAL_RESERVED_SIZE);
channel.write(buf);
channel.force(true);
- // 5. set unlock = 0 because it's a formatPartitionLocked
+ // 6. set unlock = 0 because it's a formatPartitionLocked
buf = ByteBuffer.allocate(FRP_CREDENTIAL_RESERVED_SIZE);
buf.put((byte)0);
buf.flip();
channel.write(buf);
channel.force(true);
-
- // 6. Write the default FRP secret (all zeros).
- if (mFrpEnforced) {
- writeFrpMagicAndDefaultSecret();
- }
} catch (IOException e) {
Slog.e(TAG, "failed to format block", e);
return;
@@ -616,7 +626,7 @@ public class PersistentDataBlockService extends SystemService {
// version. If so, we deactivate FRP and set the secret to the default value.
if (isUpgradingFromPreVRelease()) {
Slog.w(TAG, "Upgrading from Android 14 or lower, defaulting FRP secret");
- writeFrpMagicAndDefaultSecret();
+ writeFrpMagicAndDefaultSecretLocked();
mFrpActive = false;
return true;
}
@@ -630,7 +640,7 @@ public class PersistentDataBlockService extends SystemService {
try {
return deactivateFrp(Files.readAllBytes(Paths.get(frpSecretFile)));
} catch (IOException e) {
- Slog.w(TAG, "Failed to read FRP secret file: " + frpSecretFile + " "
+ Slog.i(TAG, "Failed to read FRP secret file: " + frpSecretFile + " "
+ e.getClass().getSimpleName());
return false;
}
@@ -646,14 +656,17 @@ public class PersistentDataBlockService extends SystemService {
@VisibleForTesting
boolean isFrpActive() {
- waitForInitDoneSignal();
synchronized (mLock) {
+ // mFrpActive is initialized and automatic deactivation done (if possible) before the
+ // service is published, so there's no chance that callers could ask for the state
+ // before it has settled.
return mFrpActive;
}
}
/**
- * Write the provided secret to the FRP secret file in /data and to the /persist partition.
+ * Write the provided secret to the FRP secret file in /data and to the persistent data block
+ * partition.
*
* Writing is a three-step process, to ensure that we can recover from a crash at any point.
*/
@@ -713,7 +726,7 @@ public class PersistentDataBlockService extends SystemService {
synchronized (mLock) {
if (!hasFrpSecretMagic()) {
Slog.i(TAG, "No FRP secret magic, system must have been upgraded.");
- writeFrpMagicAndDefaultSecret();
+ writeFrpMagicAndDefaultSecretLocked();
}
}
@@ -735,11 +748,9 @@ public class PersistentDataBlockService extends SystemService {
}
}
- private void writeFrpMagicAndDefaultSecret() {
+ private void writeFrpMagicAndDefaultSecretLocked() {
try (FileChannel channel = getBlockOutputChannelIgnoringFrp()) {
synchronized (mLock) {
- // Write secret first in case we crash between the writes, causing the first write
- // to be synced but the second to be lost.
Slog.i(TAG, "Writing default FRP secret");
channel.position(getFrpSecretDataOffset());
channel.write(ByteBuffer.allocate(FRP_SECRET_SIZE));
@@ -755,6 +766,7 @@ public class PersistentDataBlockService extends SystemService {
} catch (IOException e) {
Slog.e(TAG, "Failed to write FRP magic and default secret", e);
}
+ computeAndWriteDigestLocked();
}
@VisibleForTesting
@@ -879,7 +891,7 @@ public class PersistentDataBlockService extends SystemService {
if (printSecret) {
try {
pw.println("FRP secret in " + frpSecretFile + ": " + HexFormat.of()
- .formatHex(Files.readAllBytes(Paths.get(mFrpSecretFile))));
+ .formatHex(Files.readAllBytes(Paths.get(frpSecretFile))));
} catch (IOException e) {
Slog.e(TAG, "Failed to read " + frpSecretFile, e);
}
@@ -1230,6 +1242,7 @@ public class PersistentDataBlockService extends SystemService {
@Override
public boolean setFactoryResetProtectionSecret(byte[] secret) {
+ enforceConfigureFrpPermission();
enforceUid(Binder.getCallingUid());
if (secret == null || secret.length != FRP_SECRET_SIZE) {
throw new IllegalArgumentException(
@@ -1242,6 +1255,7 @@ public class PersistentDataBlockService extends SystemService {
private void enforceFactoryResetProtectionInactive() {
if (mFrpEnforced && isFrpActive()) {
+ Slog.w(TAG, "Attempt to update PDB was blocked because FRP is active.");
throw new SecurityException("FRP is active");
}
}
diff --git a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
index 524bad58ce07..b6daed121057 100644
--- a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
+++ b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
@@ -30,6 +30,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.IBackgroundInstallControlService;
import android.content.pm.InstallSourceInfo;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
@@ -46,6 +47,7 @@ import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.AtomicFile;
+import android.util.Log;
import android.util.Slog;
import android.util.SparseArrayMap;
import android.util.SparseSetArray;
@@ -63,8 +65,10 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Comparator;
import java.util.List;
import java.util.ListIterator;
+import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
@@ -103,6 +107,24 @@ public class BackgroundInstallControlService extends SystemService {
private final SparseArrayMap<String, TreeSet<ForegroundTimeFrame>>
mInstallerForegroundTimeFrames = new SparseArrayMap<>();
+ @VisibleForTesting
+ protected final PackageManagerInternal.PackageListObserver mPackageObserver =
+ new PackageManagerInternal.PackageListObserver() {
+ @Override
+ public void onPackageAdded(String packageName, int uid) {
+ final int userId = UserHandle.getUserId(uid);
+ mHandler.obtainMessage(MSG_PACKAGE_ADDED, userId, 0, packageName)
+ .sendToTarget();
+ }
+
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ final int userId = UserHandle.getUserId(uid);
+ mHandler.obtainMessage(MSG_PACKAGE_REMOVED, userId, 0, packageName)
+ .sendToTarget();
+ }
+ };
+
public BackgroundInstallControlService(@NonNull Context context) {
this(new InjectorImpl(context));
}
@@ -258,6 +280,7 @@ public class BackgroundInstallControlService extends SystemService {
String installerPackageName;
String initiatingPackageName;
+
try {
final InstallSourceInfo installInfo = mPackageManager.getInstallSourceInfo(packageName);
installerPackageName = installInfo.getInstallingPackageName();
@@ -280,7 +303,8 @@ public class BackgroundInstallControlService extends SystemService {
// convert up-time to current time.
final long installTimestamp =
- System.currentTimeMillis() - (SystemClock.uptimeMillis() - appInfo.createTimestamp);
+ System.currentTimeMillis() - (SystemClock.uptimeMillis()
+ - retrieveInstallStartTimestamp(packageName, userId, appInfo));
if (installedByAdb(initiatingPackageName)
|| wasForegroundInstallation(installerPackageName, userId, installTimestamp)) {
@@ -293,6 +317,35 @@ public class BackgroundInstallControlService extends SystemService {
writeBackgroundInstalledPackagesToDisk();
}
+ private long retrieveInstallStartTimestamp(String packageName,
+ int userId, ApplicationInfo appInfo) {
+ long installStartTimestamp = appInfo.createTimestamp;
+
+ try {
+ Optional<PackageInstaller.SessionInfo> latestInstallSession =
+ getLatestInstallSession(packageName, userId);
+ if (latestInstallSession.isEmpty()) {
+ Slog.w(TAG, "Package's historical install session not found, falling back "
+ + "to appInfo.createTimestamp: " + packageName);
+ } else {
+ installStartTimestamp = latestInstallSession.get().getCreatedMillis();
+ }
+ } catch (Exception e) {
+ Slog.w(TAG, "Retrieval of install time from historical session failed, falling "
+ + "back to appInfo.createTimestamp");
+ Slog.w(TAG, Log.getStackTraceString(e));
+ }
+ return installStartTimestamp;
+ }
+
+ private Optional<PackageInstaller.SessionInfo> getLatestInstallSession(
+ String packageName, int userId) {
+ List<PackageInstaller.SessionInfo> historicalSessions =
+ mPackageManagerInternal.getHistoricalSessions(userId).getList();
+ return historicalSessions.stream().filter(s -> packageName.equals(s.getAppPackageName()))
+ .max(Comparator.comparingLong(PackageInstaller.SessionInfo::getCreatedMillis));
+ }
+
// ADB sets installerPackageName to null, this creates a loophole to bypass BIC which will be
// addressed with b/265203007
private boolean installedByAdb(String initiatingPackageName) {
@@ -496,22 +549,7 @@ public class BackgroundInstallControlService extends SystemService {
publishBinderService(Context.BACKGROUND_INSTALL_CONTROL_SERVICE, mBinderService);
}
- mPackageManagerInternal.getPackageList(
- new PackageManagerInternal.PackageListObserver() {
- @Override
- public void onPackageAdded(String packageName, int uid) {
- final int userId = UserHandle.getUserId(uid);
- mHandler.obtainMessage(MSG_PACKAGE_ADDED, userId, 0, packageName)
- .sendToTarget();
- }
-
- @Override
- public void onPackageRemoved(String packageName, int uid) {
- final int userId = UserHandle.getUserId(uid);
- mHandler.obtainMessage(MSG_PACKAGE_REMOVED, userId, 0, packageName)
- .sendToTarget();
- }
- });
+ mPackageManagerInternal.getPackageList(mPackageObserver);
}
// The foreground time frame (ForegroundTimeFrame) represents the period
diff --git a/services/core/java/com/android/server/pm/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java
index e984e9c255da..23d48e871d62 100644
--- a/services/core/java/com/android/server/pm/BroadcastHelper.java
+++ b/services/core/java/com/android/server/pm/BroadcastHelper.java
@@ -45,6 +45,8 @@ import android.content.Intent;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
+import android.content.pm.UserProperties;
+import android.multiuser.Flags;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -361,14 +363,13 @@ public final class BroadcastHelper {
final UserInfo parent = ums.getProfileParent(userId);
final int launcherUserId = (parent != null) ? parent.id : userId;
final ComponentName launcherComponent = snapshot.getDefaultHomeActivity(launcherUserId);
- if (launcherComponent != null) {
+ if (launcherComponent != null && canLauncherAccessProfile(launcherComponent, userId)) {
Intent launcherIntent = new Intent(PackageInstaller.ACTION_SESSION_COMMITTED)
.putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo)
.putExtra(Intent.EXTRA_USER, UserHandle.of(userId))
.setPackage(launcherComponent.getPackageName());
mContext.sendBroadcastAsUser(launcherIntent, UserHandle.of(launcherUserId));
}
- // TODO(b/122900055) Change/Remove this and replace with new permission role.
if (appPredictionServicePackage != null) {
Intent predictorIntent = new Intent(PackageInstaller.ACTION_SESSION_COMMITTED)
.putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo)
@@ -378,6 +379,36 @@ public final class BroadcastHelper {
}
}
+ /**
+ * A Profile is accessible to launcher in question if:
+ * - It's not hidden for API visibility.
+ * - Hidden, but launcher application has either
+ * {@link Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL} or
+ * {@link Manifest.permission.ACCESS_HIDDEN_PROFILES}
+ * granted.
+ */
+ boolean canLauncherAccessProfile(ComponentName launcherComponent, int userId) {
+ if (android.os.Flags.allowPrivateProfile()
+ && Flags.enablePermissionToAccessHiddenProfiles()) {
+ if (mUmInternal.getUserProperties(userId).getProfileApiVisibility()
+ != UserProperties.PROFILE_API_VISIBILITY_HIDDEN) {
+ return true;
+ }
+ if (mContext.getPackageManager().checkPermission(
+ Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL,
+ launcherComponent.getPackageName())
+ == PackageManager.PERMISSION_GRANTED) {
+ return true;
+ }
+ // TODO(b/122900055) Change/Remove this and replace with new permission role.
+ return mContext.getPackageManager().checkPermission(
+ Manifest.permission.ACCESS_HIDDEN_PROFILES,
+ launcherComponent.getPackageName())
+ == PackageManager.PERMISSION_GRANTED;
+ }
+ return true;
+ }
+
void sendPreferredActivityChangedBroadcast(int userId) {
mHandler.post(() -> {
final IActivityManager am = ActivityManager.getService();
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
index 5b3f7a58e653..017cf55541fc 100644
--- a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
@@ -20,6 +20,9 @@ import static android.content.pm.PackageManager.ONLY_IF_NO_MATCH_FOUND;
import static android.content.pm.PackageManager.SKIP_CURRENT_PROFILE;
import static android.speech.RecognizerIntent.ACTION_RECOGNIZE_SPEECH;
+import static com.android.server.pm.CrossProfileIntentFilter.FLAG_ALLOW_CHAINED_RESOLUTION;
+import static com.android.server.pm.CrossProfileIntentFilter.FLAG_IS_PACKAGE_FOR_FILTER;
+
import android.content.Intent;
import android.hardware.usb.UsbManager;
import android.provider.AlarmClock;
@@ -613,6 +616,27 @@ public class DefaultCrossProfileIntentFiltersUtils {
.addDataScheme("mmsto")
.build();
+ private static final DefaultCrossProfileIntentFilter CLONE_TO_PARENT_ACTION_PICK_IMAGES =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */ FLAG_IS_PACKAGE_FOR_FILTER | FLAG_ALLOW_CHAINED_RESOLUTION,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(MediaStore.ACTION_PICK_IMAGES)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .build();
+
+ private static final DefaultCrossProfileIntentFilter
+ CLONE_TO_PARENT_ACTION_PICK_IMAGES_WITH_DATA_TYPES =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */ FLAG_IS_PACKAGE_FOR_FILTER | FLAG_ALLOW_CHAINED_RESOLUTION,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(MediaStore.ACTION_PICK_IMAGES)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addDataType("image/*")
+ .addDataType("video/*")
+ .build();
+
public static List<DefaultCrossProfileIntentFilter> getDefaultCloneProfileFilters() {
return Arrays.asList(
PARENT_TO_CLONE_SEND_ACTION,
@@ -626,7 +650,80 @@ public class DefaultCrossProfileIntentFiltersUtils {
CLONE_TO_PARENT_PICK_INSERT_ACTION,
CLONE_TO_PARENT_DIAL_DATA,
CLONE_TO_PARENT_SMS_MMS,
- CLONE_TO_PARENT_PHOTOPICKER_SELECTION
+ CLONE_TO_PARENT_PHOTOPICKER_SELECTION,
+ CLONE_TO_PARENT_ACTION_PICK_IMAGES,
+ CLONE_TO_PARENT_ACTION_PICK_IMAGES_WITH_DATA_TYPES
+ );
+ }
+
+ /** Dial intent with mime type can be handled by either private profile or its parent user. */
+ private static final DefaultCrossProfileIntentFilter DIAL_MIME_PRIVATE_PROFILE =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ ONLY_IF_NO_MATCH_FOUND,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_DIAL)
+ .addAction(Intent.ACTION_VIEW)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .addDataType("vnd.android.cursor.item/phone")
+ .addDataType("vnd.android.cursor.item/phone_v2")
+ .addDataType("vnd.android.cursor.item/person")
+ .addDataType("vnd.android.cursor.dir/calls")
+ .addDataType("vnd.android.cursor.item/calls")
+ .build();
+
+ /** Dial intent with data scheme can be handled by either private profile or its parent user. */
+ private static final DefaultCrossProfileIntentFilter DIAL_DATA_PRIVATE_PROFILE =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ ONLY_IF_NO_MATCH_FOUND,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_DIAL)
+ .addAction(Intent.ACTION_VIEW)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .addDataScheme("tel")
+ .addDataScheme("sip")
+ .addDataScheme("voicemail")
+ .build();
+
+ /**
+ * Dial intent with no data scheme or type can be handled by either private profile or its
+ * parent user.
+ */
+ private static final DefaultCrossProfileIntentFilter DIAL_RAW_PRIVATE_PROFILE =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ ONLY_IF_NO_MATCH_FOUND,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_DIAL)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .build();
+
+ /** SMS and MMS can be handled by the private profile or by the parent user. */
+ private static final DefaultCrossProfileIntentFilter SMS_MMS_PRIVATE_PROFILE =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ ONLY_IF_NO_MATCH_FOUND,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_VIEW)
+ .addAction(Intent.ACTION_SENDTO)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .addDataScheme("sms")
+ .addDataScheme("smsto")
+ .addDataScheme("mms")
+ .addDataScheme("mmsto")
+ .build();
+
+ public static List<DefaultCrossProfileIntentFilter> getDefaultPrivateProfileFilters() {
+ return Arrays.asList(
+ DIAL_MIME_PRIVATE_PROFILE,
+ DIAL_DATA_PRIVATE_PROFILE,
+ DIAL_RAW_PRIVATE_PROFILE,
+ SMS_MMS_PRIVATE_PROFILE
);
}
}
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 295528e14ca7..d8d8dd2e57a9 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -350,18 +350,50 @@ public class LauncherAppsService extends SystemService {
public void registerPackageInstallerCallback(String callingPackage,
IPackageInstallerCallback callback) {
verifyCallingPackage(callingPackage);
- UserHandle callingIdUserHandle = new UserHandle(getCallingUserId());
- getPackageInstallerService().registerCallback(callback, eventUserId ->
- isEnabledProfileOf(callingIdUserHandle,
- new UserHandle(eventUserId), "shouldReceiveEvent"));
+ BroadcastCookie callerInfo =
+ new BroadcastCookie(
+ new UserHandle(getCallingUserId()),
+ callingPackage,
+ getCallingPid(),
+ getCallingUid());
+ getPackageInstallerService()
+ .registerCallback(
+ callback,
+ eventUserId ->
+ isEnabledProfileOf(
+ callerInfo,
+ new UserHandle(eventUserId),
+ "shouldReceiveEvent"));
+ }
+
+ @Override
+ public List<UserHandle> getUserProfiles() {
+ int[] userIds;
+ if (!canAccessHiddenProfile(getCallingUid(), getCallingPid())) {
+ userIds = mUm.getProfileIdsExcludingHidden(getCallingUserId(), /* enabled= */ true);
+ } else {
+ userIds = mUm.getEnabledProfileIds(getCallingUserId());
+ }
+ final List<UserHandle> result = new ArrayList<>(userIds.length);
+ for (int userId : userIds) {
+ result.add(UserHandle.of(userId));
+ }
+ return result;
}
@Override
public ParceledListSlice<SessionInfo> getAllSessions(String callingPackage) {
verifyCallingPackage(callingPackage);
List<SessionInfo> sessionInfos = new ArrayList<>();
- int[] userIds = mUm.getEnabledProfileIds(getCallingUserId());
final int callingUid = Binder.getCallingUid();
+
+ int[] userIds;
+ if (!canAccessHiddenProfile(callingUid, Binder.getCallingPid())) {
+ userIds = mUm.getProfileIdsExcludingHidden(getCallingUserId(), /* enabled= */ true);
+ } else {
+ userIds = mUm.getEnabledProfileIds(getCallingUserId());
+ }
+
final long token = Binder.clearCallingIdentity();
try {
for (int userId : userIds) {
@@ -389,7 +421,7 @@ public class LauncherAppsService extends SystemService {
mPackageInstallerService = ((PackageInstallerService) ((IPackageManager)
ServiceManager.getService("package")).getPackageInstaller());
} catch (RemoteException e) {
- Slog.wtf(TAG, "Error gettig IPackageInstaller", e);
+ Slog.wtf(TAG, "Error getting IPackageInstaller", e);
}
}
return mPackageInstallerService;
@@ -470,57 +502,86 @@ public class LauncherAppsService extends SystemService {
+ targetUserId + " from " + callingUserId + " not allowed");
return false;
}
-
- if (areHiddenApisChecksEnabled()
- && mUm.getUserProperties(UserHandle.of(targetUserId))
- .getProfileApiVisibility()
- == UserProperties.PROFILE_API_VISIBILITY_HIDDEN
- && !canAccessHiddenProfileInjected(callingUid, callingPid)) {
- return false;
- }
} finally {
injectRestoreCallingIdentity(ident);
}
+ if (isHiddenProfile(UserHandle.of(targetUserId))
+ && !canAccessHiddenProfile(callingUid, callingPid)) {
+ return false;
+ }
+
return mUserManagerInternal.isProfileAccessible(callingUserId, targetUserId,
message, true);
}
- boolean areHiddenApisChecksEnabled() {
- return android.os.Flags.allowPrivateProfile()
- && Flags.enableLauncherAppsHiddenProfileChecks()
- && Flags.enablePermissionToAccessHiddenProfiles();
+ private boolean isHiddenProfile(UserHandle targetUser) {
+ if (!Flags.enableLauncherAppsHiddenProfileChecks()) {
+ return false;
+ }
+
+ long identity = injectClearCallingIdentity();
+ try {
+ UserProperties properties = mUm.getUserProperties(targetUser);
+ if (properties == null) {
+ return false;
+ }
+
+ return properties.getProfileApiVisibility()
+ == UserProperties.PROFILE_API_VISIBILITY_HIDDEN;
+ } catch (IllegalArgumentException e) {
+ return false;
+ } finally {
+ injectRestoreCallingIdentity(identity);
+ }
}
private void verifyCallingPackage(String callingPackage) {
verifyCallingPackage(callingPackage, injectBinderCallingUid());
}
- boolean canAccessHiddenProfileInjected(int callingUid, int callingPid) {
- AndroidPackage callingPackage = mPackageManagerInternal.getPackage(callingUid);
- if (callingPackage == null) {
- return false;
+ private boolean canAccessHiddenProfile(int callingUid, int callingPid) {
+ if (!areHiddenApisChecksEnabled()) {
+ return true;
}
- if (!mRoleManager
- .getRoleHoldersAsUser(
- RoleManager.ROLE_HOME, UserHandle.getUserHandleForUid(callingUid))
- .contains(callingPackage.getPackageName())) {
- return false;
- }
+ long ident = injectClearCallingIdentity();
+ try {
+ AndroidPackage callingPackage = mPackageManagerInternal.getPackage(callingUid);
+ if (callingPackage == null) {
+ return false;
+ }
- if (mContext.checkPermission(
- Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL, callingPid, callingUid)
- == PackageManager.PERMISSION_GRANTED) {
- return true;
+ if (!mRoleManager
+ .getRoleHoldersAsUser(
+ RoleManager.ROLE_HOME, UserHandle.getUserHandleForUid(callingUid))
+ .contains(callingPackage.getPackageName())) {
+ return false;
+ }
+ if (mContext.checkPermission(
+ Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL,
+ callingPid,
+ callingUid)
+ == PackageManager.PERMISSION_GRANTED) {
+ return true;
+ }
+
+ // TODO(b/321988638): add option to disable with a flag
+ return mContext.checkPermission(
+ android.Manifest.permission.ACCESS_HIDDEN_PROFILES,
+ callingPid,
+ callingUid)
+ == PackageManager.PERMISSION_GRANTED;
+ } finally {
+ injectRestoreCallingIdentity(ident);
}
+ }
- // TODO(b/321988638): add option to disable with a flag
- return mContext.checkPermission(
- android.Manifest.permission.ACCESS_HIDDEN_PROFILES,
- callingPid,
- callingUid)
- == PackageManager.PERMISSION_GRANTED;
+ private boolean areHiddenApisChecksEnabled() {
+ return android.os.Flags.allowPrivateProfile()
+ && Flags.enableHidingProfiles()
+ && Flags.enableLauncherAppsHiddenProfileChecks()
+ && Flags.enablePermissionToAccessHiddenProfiles();
}
@VisibleForTesting // We override it in unit tests
@@ -798,6 +859,10 @@ public class LauncherAppsService extends SystemService {
public ParceledListSlice getShortcutConfigActivities(
String callingPackage, String packageName, UserHandle user)
throws RemoteException {
+ // Not supported for user-profiles with items restricted on home screen.
+ if (!mShortcutServiceInternal.areShortcutsSupportedOnHomeScreen(user.getIdentifier())) {
+ return null;
+ }
return queryActivitiesForUser(callingPackage,
new Intent(Intent.ACTION_CREATE_SHORTCUT).setPackage(packageName), user);
}
@@ -1256,6 +1321,14 @@ public class LauncherAppsService extends SystemService {
@Override
public void pinShortcuts(String callingPackage, String packageName, List<String> ids,
UserHandle targetUser) {
+ if (!mShortcutServiceInternal
+ .areShortcutsSupportedOnHomeScreen(targetUser.getIdentifier())) {
+ // Requires strict ACCESS_SHORTCUTS permission for user-profiles with items
+ // restricted on home screen.
+ ensureStrictAccessShortcutsPermission(callingPackage);
+ } else {
+ ensureShortcutPermission(callingPackage);
+ }
ensureShortcutPermission(callingPackage);
if (!canAccessProfile(targetUser.getIdentifier(), "Cannot pin shortcuts")) {
return;
@@ -1625,10 +1698,11 @@ public class LauncherAppsService extends SystemService {
}
@Override
+ @NonNull
public List<String> getPreInstalledSystemPackages(UserHandle user) {
if (!canAccessProfile(user.getIdentifier(),
"Can't access preinstalled packages for another user")) {
- return null;
+ return new ArrayList<>();
}
final long identity = Binder.clearCallingIdentity();
try {
@@ -2055,12 +2129,18 @@ public class LauncherAppsService extends SystemService {
});
}
- /** Checks if user is a profile of or same as listeningUser.
- * and the user is enabled. */
- private boolean isEnabledProfileOf(UserHandle listeningUser, UserHandle user,
- String debugMsg) {
- return mUserManagerInternal.isProfileAccessible(listeningUser.getIdentifier(),
- user.getIdentifier(), debugMsg, false);
+ /**
+ * Checks if user is a profile of or same as listeningUser and the target user is enabled
+ * and accessible for caller.
+ */
+ private boolean isEnabledProfileOf(
+ BroadcastCookie cookie, UserHandle user, String debugMsg) {
+ if (isHiddenProfile(user)
+ && !canAccessHiddenProfile(cookie.callingUid, cookie.callingPid)) {
+ return false;
+ }
+ return mUserManagerInternal.isProfileAccessible(
+ cookie.user.getIdentifier(), user.getIdentifier(), debugMsg, false);
}
/**
@@ -2292,7 +2372,7 @@ public class LauncherAppsService extends SystemService {
mListeners.getBroadcastItem(i);
final BroadcastCookie cookie =
(BroadcastCookie) mListeners.getBroadcastCookie(i);
- if (!isEnabledProfileOf(cookie.user, user, "onPackageRemoved")) {
+ if (!isEnabledProfileOf(cookie, user, "onPackageRemoved")) {
continue;
}
if (!isCallingAppIdAllowed(appIdAllowList, UserHandle.getAppId(
@@ -2331,7 +2411,7 @@ public class LauncherAppsService extends SystemService {
for (int i = 0; i < n; i++) {
IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
- if (!isEnabledProfileOf(cookie.user, user, "onPackageAdded")) {
+ if (!isEnabledProfileOf(cookie, user, "onPackageAdded")) {
continue;
}
if (!isPackageVisibleToListener(packageName, cookie, user)) {
@@ -2365,7 +2445,7 @@ public class LauncherAppsService extends SystemService {
for (int i = 0; i < n; i++) {
IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
- if (!isEnabledProfileOf(cookie.user, user, "onPackageModified")) {
+ if (!isEnabledProfileOf(cookie, user, "onPackageModified")) {
continue;
}
if (!isPackageVisibleToListener(packageName, cookie, user)) {
@@ -2390,7 +2470,7 @@ public class LauncherAppsService extends SystemService {
for (int i = 0; i < n; i++) {
IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
- if (!isEnabledProfileOf(cookie.user, user, "onPackagesAvailable")) {
+ if (!isEnabledProfileOf(cookie, user, "onPackagesAvailable")) {
continue;
}
final String[] filteredPackages =
@@ -2420,7 +2500,7 @@ public class LauncherAppsService extends SystemService {
for (int i = 0; i < n; i++) {
IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
- if (!isEnabledProfileOf(cookie.user, user, "onPackagesUnavailable")) {
+ if (!isEnabledProfileOf(cookie, user, "onPackagesUnavailable")) {
continue;
}
final String[] filteredPackages =
@@ -2464,7 +2544,7 @@ public class LauncherAppsService extends SystemService {
for (int i = 0; i < n; i++) {
IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
- if (!isEnabledProfileOf(cookie.user, user, "onPackagesSuspended")) {
+ if (!isEnabledProfileOf(cookie, user, "onPackagesSuspended")) {
continue;
}
final String[] filteredPackagesWithoutExtras =
@@ -2501,7 +2581,7 @@ public class LauncherAppsService extends SystemService {
for (int i = 0; i < n; i++) {
IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
- if (!isEnabledProfileOf(cookie.user, user, "onPackagesUnsuspended")) {
+ if (!isEnabledProfileOf(cookie, user, "onPackagesUnsuspended")) {
continue;
}
final String[] filteredPackages =
@@ -2538,7 +2618,7 @@ public class LauncherAppsService extends SystemService {
for (int i = 0; i < n; i++) {
IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
- if (!isEnabledProfileOf(cookie.user, user, "onShortcutChanged")) {
+ if (!isEnabledProfileOf(cookie, user, "onShortcutChanged")) {
continue;
}
if (!isPackageVisibleToListener(packageName, cookie, user)) {
@@ -2612,7 +2692,7 @@ public class LauncherAppsService extends SystemService {
for (int i = 0; i < n; i++) {
IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
- if (!isEnabledProfileOf(cookie.user, mUser, "onLoadingProgressChanged")) {
+ if (!isEnabledProfileOf(cookie, mUser, "onLoadingProgressChanged")) {
continue;
}
if (!isPackageVisibleToListener(mPackageName, cookie, mUser)) {
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
index 85d2df320fc9..9db4d33106ce 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
@@ -27,11 +27,14 @@ import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.pm.Flags;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment;
import android.os.FileUtils;
+import android.os.SystemProperties;
import android.os.Trace;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -50,9 +53,16 @@ import libcore.io.IoUtils;
import java.io.File;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
final class PackageAbiHelperImpl implements PackageAbiHelper {
+ @Nullable
+ private static String[] sNativelySupported32BitAbis = null;
+ @Nullable
+ private static String[] sNativelySupported64BitAbis = null;
+
private static String calculateBundledApkRoot(final String codePathString) {
final File codePath = new File(codePathString);
final File codeRoot;
@@ -122,13 +132,20 @@ final class PackageAbiHelperImpl implements PackageAbiHelper {
}
}
- private static void maybeThrowExceptionForMultiArchCopy(String message, int copyRet) throws
- PackageManagerException {
+ private static void maybeThrowExceptionForMultiArchCopy(String message, int copyRet,
+ boolean forceMatch) throws PackageManagerException {
if (copyRet < 0) {
if (copyRet != PackageManager.NO_NATIVE_LIBRARIES
&& copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
throw new PackageManagerException(copyRet, message);
}
+
+ if (forceMatch && copyRet == PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
+ throw new PackageManagerException(
+ PackageManager.INSTALL_FAILED_MULTI_ARCH_NOT_MATCH_ALL_NATIVE_ABIS,
+ "The multiArch app's native libs don't support all the natively"
+ + " supported ABIs of the device.");
+ }
}
}
@@ -296,7 +313,40 @@ final class PackageAbiHelperImpl implements PackageAbiHelper {
return new Abis(primaryCpuAbi, secondaryCpuAbi);
}
+ @NonNull
+ private static String[] getNativelySupportedAbis(@NonNull String[] supportedAbis) {
+ List<String> nativelySupportedAbis = new ArrayList<>();
+ for (int i = 0; i < supportedAbis.length; i++) {
+ final String currentAbi = supportedAbis[i];
+ // In presence of a native bridge this means the Abi is emulated.
+ final String currentIsa = VMRuntime.getInstructionSet(currentAbi);
+ if (TextUtils.isEmpty(SystemProperties.get("ro.dalvik.vm.isa." + currentIsa))) {
+ nativelySupportedAbis.add(currentAbi);
+ }
+ }
+ return nativelySupportedAbis.toArray(new String[0]);
+ }
+
+ private static String[] getNativelySupported32BitAbis() {
+ if (sNativelySupported32BitAbis != null) {
+ return sNativelySupported32BitAbis;
+ }
+
+ sNativelySupported32BitAbis = getNativelySupportedAbis(Build.SUPPORTED_32_BIT_ABIS);
+ return sNativelySupported32BitAbis;
+ }
+
+ private static String[] getNativelySupported64BitAbis() {
+ if (sNativelySupported64BitAbis != null) {
+ return sNativelySupported64BitAbis;
+ }
+
+ sNativelySupported64BitAbis = getNativelySupportedAbis(Build.SUPPORTED_64_BIT_ABIS);
+ return sNativelySupported64BitAbis;
+ }
+
@Override
+ @SuppressWarnings("AndroidFrameworkCompatChange") // the check is before the apk is installed
public Pair<Abis, NativeLibraryPaths> derivePackageAbi(AndroidPackage pkg, boolean isSystemApp,
boolean isUpdatedSystemApp, String cpuAbiOverride, File appLib32InstallDir)
throws PackageManagerException {
@@ -334,18 +384,33 @@ final class PackageAbiHelperImpl implements PackageAbiHelper {
primaryCpuAbi = null;
secondaryCpuAbi = null;
if (pkg.isMultiArch()) {
+ // Force the match for these cases
+ // 1. pkg.getTargetSdkVersion >= Build.VERSION_CODES.VANILLA_ICE_CREAM
+ // 2. cpuAbiOverride is null. If it is non-null, it is set via shell for testing
+ final boolean forceMatch = Flags.forceMultiArchNativeLibsMatch()
+ && pkg.getTargetSdkVersion() >= Build.VERSION_CODES.VANILLA_ICE_CREAM
+ && cpuAbiOverride == null;
+
+ String[] supported32BitAbis = forceMatch ? getNativelySupported32BitAbis()
+ : Build.SUPPORTED_32_BIT_ABIS;
+ String[] supported64BitAbis = forceMatch ? getNativelySupported64BitAbis()
+ : Build.SUPPORTED_64_BIT_ABIS;
+
+ final boolean systemSupports32BitAbi = supported32BitAbis.length > 0;
+ final boolean systemSupports64BitAbi = supported64BitAbis.length > 0;
+
int abi32 = PackageManager.NO_NATIVE_LIBRARIES;
int abi64 = PackageManager.NO_NATIVE_LIBRARIES;
- if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
+ if (systemSupports32BitAbi) {
if (extractLibs) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
- nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS,
+ nativeLibraryRoot, supported32BitAbis,
useIsaSpecificSubdirs, onIncremental);
} else {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
abi32 = NativeLibraryHelper.findSupportedAbi(
- handle, Build.SUPPORTED_32_BIT_ABIS);
+ handle, supported32BitAbis);
}
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -357,24 +422,26 @@ final class PackageAbiHelperImpl implements PackageAbiHelper {
}
maybeThrowExceptionForMultiArchCopy(
- "Error unpackaging 32 bit native libs for multiarch app.", abi32);
+ "Error unpackaging 32 bit native libs for multiarch app.", abi32,
+ forceMatch && systemSupports32BitAbi);
- if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
+ if (systemSupports64BitAbi) {
if (extractLibs) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
- nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS,
+ nativeLibraryRoot, supported64BitAbis,
useIsaSpecificSubdirs, onIncremental);
} else {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
abi64 = NativeLibraryHelper.findSupportedAbi(
- handle, Build.SUPPORTED_64_BIT_ABIS);
+ handle, supported64BitAbis);
}
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
maybeThrowExceptionForMultiArchCopy(
- "Error unpackaging 64 bit native libs for multiarch app.", abi64);
+ "Error unpackaging 64 bit native libs for multiarch app.", abi64,
+ forceMatch && systemSupports64BitAbi);
if (abi64 >= 0) {
// Shared library native libs should be in the APK zip aligned
@@ -382,11 +449,11 @@ final class PackageAbiHelperImpl implements PackageAbiHelper {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
"Shared library native lib extraction not supported");
}
- primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[abi64];
+ primaryCpuAbi = supported64BitAbis[abi64];
}
if (abi32 >= 0) {
- final String abi = Build.SUPPORTED_32_BIT_ABIS[abi32];
+ final String abi = supported32BitAbis[abi32];
if (abi64 >= 0) {
if (pkg.is32BitAbiPreferred()) {
secondaryCpuAbi = primaryCpuAbi;
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index dc97e5fa92af..cdd52a47e433 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -62,6 +62,7 @@ import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
import android.content.pm.VersionedPackage;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -85,6 +86,7 @@ import android.os.RemoteException;
import android.os.SELinux;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.os.UserManager;
import android.text.TextUtils;
import android.util.ExceptionUtils;
import android.util.Pair;
@@ -122,6 +124,7 @@ import java.util.concurrent.CompletableFuture;
public class PackageArchiver {
private static final String TAG = "PackageArchiverService";
+ private static final boolean DEBUG = true;
public static final String EXTRA_UNARCHIVE_INTENT_SENDER =
"android.content.pm.extra.UNARCHIVE_INTENT_SENDER";
@@ -163,6 +166,9 @@ public class PackageArchiver {
@Nullable
private AppOpsManager mAppOpsManager;
+ @Nullable
+ private UserManager mUserManager;
+
/* IntentSender store that maps key: {userId, appPackageName} to respective existing attached
unarchival intent sender. */
private final Map<Pair<Integer, String>, IntentSender> mLauncherIntentSenders;
@@ -203,6 +209,9 @@ public class PackageArchiver {
Objects.requireNonNull(intentSender);
Objects.requireNonNull(userHandle);
+ Slog.i(TAG,
+ TextUtils.formatSimple("Requested archival of package %s for user %s.", packageName,
+ userHandle.getIdentifier()));
Computer snapshot = mPm.snapshotComputer();
int binderUserId = userHandle.getIdentifier();
int binderUid = Binder.getCallingUid();
@@ -227,7 +236,7 @@ public class PackageArchiver {
archiveStateStored[i] = createAndStoreArchiveState(packageName, users[i]);
}
} catch (PackageManager.NameNotFoundException e) {
- Slog.d(TAG, TextUtils.formatSimple("Failed to archive %s with message %s",
+ Slog.e(TAG, TextUtils.formatSimple("Failed to archive %s with message %s",
packageName, e.getMessage()));
throw new ParcelableException(e);
}
@@ -247,7 +256,7 @@ public class PackageArchiver {
binderPid)
).exceptionally(
e -> {
- Slog.d(TAG, TextUtils.formatSimple("Failed to archive %s with message %s",
+ Slog.e(TAG, TextUtils.formatSimple("Failed to archive %s with message %s",
packageName, e.getMessage()));
sendFailureStatus(intentSender, packageName, e.getMessage());
return null;
@@ -272,12 +281,8 @@ public class PackageArchiver {
Slog.e(TAG, "callerPackageName cannot be null for unarchival!");
return START_CLASS_NOT_FOUND;
}
- if (!isCallingPackageValid(callerPackageName, callingUid, userId)) {
- // Return early as the calling UID does not match caller package's UID.
- return START_CLASS_NOT_FOUND;
- }
- String currentLauncherPackageName = getCurrentLauncherPackageName(userId);
+ String currentLauncherPackageName = getCurrentLauncherPackageName(getParentUserId(userId));
if ((currentLauncherPackageName == null || !callerPackageName.equals(
currentLauncherPackageName)) && callingUid != Process.SHELL_UID) {
// TODO(b/311619990): Remove dependency on SHELL_UID for testing
@@ -312,6 +317,13 @@ public class PackageArchiver {
return START_ABORTED;
}
+ // Profiles share their UI and default apps, so we have to get the profile parent before
+ // fetching the default launcher.
+ private int getParentUserId(int userId) {
+ UserInfo profileParent = getUserManager().getProfileParent(userId);
+ return profileParent == null ? userId : profileParent.id;
+ }
+
/**
* Returns true if the componentName targeted by the intent corresponds to that of an archived
* app.
@@ -350,17 +362,18 @@ public class PackageArchiver {
ps.setArchiveState(/* archiveState= */ null, userId);
}
}
- mPm.mBackgroundHandler.post(
- () -> {
- File iconsDir = getIconsDir(packageName, userId);
- if (!iconsDir.exists()) {
- return;
- }
- // TODO(b/319238030) Move this into installd.
- if (!FileUtils.deleteContentsAndDir(iconsDir)) {
- Slog.e(TAG, "Failed to clean up archive files for " + packageName);
- }
- });
+ File iconsDir = getIconsDir(packageName, userId);
+ if (!iconsDir.exists()) {
+ return;
+ }
+ // TODO(b/319238030) Move this into installd.
+ if (!FileUtils.deleteContentsAndDir(iconsDir)) {
+ Slog.e(TAG, "Failed to clean up archive files for " + packageName);
+ } else {
+ if (DEBUG) {
+ Slog.e(TAG, "Deleted icons at " + iconsDir.getAbsolutePath());
+ }
+ }
}
@Nullable
@@ -521,6 +534,9 @@ public class PackageArchiver {
}
out.flush();
}
+ if (DEBUG && iconFile.exists()) {
+ Slog.i(TAG, "Stored icon at " + iconFile.getAbsolutePath());
+ }
return iconFile.toPath();
}
@@ -1120,6 +1136,13 @@ public class PackageArchiver {
return mAppOpsManager;
}
+ private UserManager getUserManager() {
+ if (mUserManager == null) {
+ mUserManager = mContext.getSystemService(UserManager.class);
+ }
+ return mUserManager;
+ }
+
private void storeArchiveState(String packageName, ArchiveState archiveState, int userId)
throws PackageManager.NameNotFoundException {
synchronized (mPm.mLock) {
@@ -1191,6 +1214,9 @@ public class PackageArchiver {
if (!iconsDir.isDirectory()) {
throw new IOException("Unable to create directory " + iconsDir);
}
+ if (DEBUG) {
+ Slog.i(TAG, "Created icons directory at " + iconsDir.getAbsolutePath());
+ }
}
SELinux.restorecon(iconsDir);
return iconsDir;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerHistoricalSession.java b/services/core/java/com/android/server/pm/PackageInstallerHistoricalSession.java
index 4b98e3408cd3..ea37d8e5d3c9 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerHistoricalSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerHistoricalSession.java
@@ -78,6 +78,7 @@ public final class PackageInstallerHistoricalSession {
private final int mSessionErrorCode;
private final String mSessionErrorMessage;
private final String mPreVerifiedDomains;
+ private final String mPackageName;
PackageInstallerHistoricalSession(int sessionId, int userId, int originalInstallerUid,
String originalInstallerPackageName, InstallSource installSource, int installerUid,
@@ -88,7 +89,8 @@ public final class PackageInstallerHistoricalSession {
String finalMessage, SessionParams params, int parentSessionId,
int[] childSessionIds, boolean sessionApplied, boolean sessionFailed,
boolean sessionReady, int sessionErrorCode, String sessionErrorMessage,
- PreapprovalDetails preapprovalDetails, DomainSet preVerifiedDomains) {
+ PreapprovalDetails preapprovalDetails, DomainSet preVerifiedDomains,
+ String packageNameFromApk) {
this.sessionId = sessionId;
this.userId = userId;
this.mOriginalInstallerUid = originalInstallerUid;
@@ -135,6 +137,9 @@ public final class PackageInstallerHistoricalSession {
} else {
this.mPreVerifiedDomains = null;
}
+
+ this.mPackageName = preapprovalDetails != null ? preapprovalDetails.getPackageName()
+ : packageNameFromApk != null ? packageNameFromApk : params.appPackageName;
}
void dump(IndentingPrintWriter pw) {
@@ -178,6 +183,7 @@ public final class PackageInstallerHistoricalSession {
pw.printPair("mSessionErrorMessage", mSessionErrorMessage);
pw.printPair("mPreapprovalDetails", mPreapprovalDetails);
pw.printPair("mPreVerifiedDomains", mPreVerifiedDomains);
+ pw.printPair("mAppPackageName", mPackageName);
pw.println();
pw.decreaseIndent();
@@ -206,6 +212,7 @@ public final class PackageInstallerHistoricalSession {
info.createdMillis = mCreatedMillis;
info.updatedMillis = mUpdatedMillis;
info.installerUid = mInstallerUid;
+ info.appPackageName = mPackageName;
return info;
}
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 6e4f19925b59..29320aeefde9 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -1702,6 +1702,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
Objects.requireNonNull(installerPackageName);
Objects.requireNonNull(userHandle);
+ Slog.i(TAG,
+ TextUtils.formatSimple("Requested archived install of package %s for user %s.",
+ archivedPackageParcel.packageName,
+ userHandle.getIdentifier()));
final int callingUid = Binder.getCallingUid();
final int userId = userHandle.getIdentifier();
final Computer snapshot = mPm.snapshotComputer();
@@ -1737,6 +1741,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
session.addFile(LOCATION_DATA_APP, "base", 0 /*lengthBytes*/,
metadata.toByteArray(), null /*signature*/);
session.commit(statusReceiver, false /*forTransfer*/);
+ Slog.i(TAG, TextUtils.formatSimple("Installed archived app %s.",
+ archivedPackageParcel.packageName));
} catch (IOException e) {
throw ExceptionUtils.wrap(e);
} finally {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index c860b5ae79f6..5792d864a8ab 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1242,7 +1242,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
mStageDirInUse, mDestroyed, mFds.size(), mBridges.size(), mFinalStatus,
mFinalMessage, params, mParentSessionId, getChildSessionIdsLocked(),
mSessionApplied, mSessionFailed, mSessionReady, mSessionErrorCode,
- mSessionErrorMessage, mPreapprovalDetails, mPreVerifiedDomains);
+ mSessionErrorMessage, mPreapprovalDetails, mPreVerifiedDomains, mPackageName);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 4f9ed03dc8e7..0a3dfc08686a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -46,7 +46,7 @@ import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledAfter;
+import android.compat.annotation.Disabled;
import android.compat.annotation.Overridable;
import android.content.Context;
import android.content.Intent;
@@ -200,7 +200,7 @@ public class PackageManagerServiceUtils {
*/
@Overridable
@ChangeId
- @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @Disabled
private static final long ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS = 161252188;
/**
@@ -1246,6 +1246,9 @@ public class PackageManagerServiceUtils {
ActivityManagerUtils.logUnsafeIntentEvent(
UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__EXPLICIT_INTENT_FILTER_UNMATCH,
filterCallingUid, intent, resolvedType, enforce);
+ if (android.security.Flags.enforceIntentFilterMatch()) {
+ intent.addExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
+ }
if (enforce) {
Slog.w(TAG, "Intent does not match component's intent filter: " + intent);
Slog.w(TAG, "Access blocked: " + comp.getComponentName());
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 29242aa9f5e1..fe65010b7281 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -5358,9 +5358,16 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
pw.println(sdf.format(date));
if (pus.getArchiveState() != null) {
+ final ArchiveState archiveState = pus.getArchiveState();
pw.print(" archiveTime=");
- date.setTime(pus.getArchiveState().getArchiveTimeMillis());
+ date.setTime(archiveState.getArchiveTimeMillis());
pw.println(sdf.format(date));
+ pw.print(" unarchiveInstallerTitle=");
+ pw.println(archiveState.getInstallerTitle());
+ for (ArchiveState.ArchiveActivityInfo activity : archiveState.getActivityInfos()) {
+ pw.print(" archiveActivityInfo=");
+ pw.println(activity.toString());
+ }
}
pw.print(" uninstallReason=");
@@ -5475,10 +5482,6 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
}
}
}
- ArchiveState archiveState = userState.getArchiveState();
- if (archiveState != null) {
- pw.print(archiveState.toString());
- }
}
}
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index d644235b8714..449e9ab8c5ec 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -240,7 +240,7 @@ class ShortcutPackage extends ShortcutPackageItem {
@Override
protected boolean canRestoreAnyVersion() {
- return false;
+ return true;
}
@Override
diff --git a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
index 1e1f17894605..47a140a97c96 100644
--- a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
+++ b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
@@ -416,6 +416,10 @@ class ShortcutRequestPinProcessor {
@VisibleForTesting
Pair<ComponentName, Integer> getRequestPinConfirmationActivity(
int callingUserId, int requestType) {
+ // Pinning is not supported for user-profiles with items restricted on home screen.
+ if (!mService.areShortcutsSupportedOnHomeScreen(callingUserId)) {
+ return null;
+ }
// Find the default launcher.
final int launcherUserId = mService.getParentOrSelfUserId(callingUserId);
final String defaultLauncher = mService.getDefaultLauncher(launcherUserId);
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index c23d2abf0853..a600eeabf62b 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -70,6 +70,7 @@ import android.graphics.Canvas;
import android.graphics.RectF;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.Icon;
+import android.multiuser.Flags;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -2830,6 +2831,26 @@ public class ShortcutService extends IShortcutService.Stub {
}
}
+ @VisibleForTesting
+ boolean areShortcutsSupportedOnHomeScreen(@UserIdInt int userId) {
+ if (!android.os.Flags.allowPrivateProfile() || !Flags.disablePrivateSpaceItemsOnHome()) {
+ return true;
+ }
+ final long start = getStatStartTime();
+ final long token = injectClearCallingIdentity();
+ boolean isSupported;
+ try {
+ synchronized (mLock) {
+ isSupported = !mUserManagerInternal.getUserProperties(userId)
+ .areItemsRestrictedOnHomeScreen();
+ }
+ } finally {
+ injectRestoreCallingIdentity(token);
+ logDurationStat(Stats.GET_DEFAULT_LAUNCHER, start);
+ }
+ return isSupported;
+ }
+
@Nullable
String getDefaultLauncher(@UserIdInt int userId) {
final long start = getStatStartTime();
@@ -3660,6 +3681,10 @@ public class ShortcutService extends IShortcutService.Stub {
callingPid, callingUid);
}
+ public boolean areShortcutsSupportedOnHomeScreen(@UserIdInt int userId) {
+ return ShortcutService.this.areShortcutsSupportedOnHomeScreen(userId);
+ }
+
@Override
public void setShortcutHostPackage(@NonNull String type, @Nullable String packageName,
int userId) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index f222fe9add0f..7349755402b1 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -18,6 +18,7 @@ package com.android.server.pm;
import static android.content.Intent.ACTION_SCREEN_OFF;
import static android.content.Intent.ACTION_SCREEN_ON;
+import static android.content.Intent.EXTRA_USER_ID;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.os.UserManager.DEV_CREATE_OVERRIDE_PROPERTY;
@@ -25,6 +26,8 @@ import static android.os.UserManager.DISALLOW_USER_SWITCH;
import static android.os.UserManager.SYSTEM_USER_MODE_EMULATION_PROPERTY;
import static android.os.UserManager.USER_OPERATION_ERROR_UNKNOWN;
+import static com.android.internal.app.SetScreenLockDialogActivity.EXTRA_ORIGIN_USER_ID;
+import static com.android.internal.app.SetScreenLockDialogActivity.LAUNCH_REASON_DISABLE_QUIET_MODE;
import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_ABORTED;
import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_UNSPECIFIED;
@@ -137,6 +140,7 @@ import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IAppOpsService;
+import com.android.internal.app.SetScreenLockDialogActivity;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.RoSystemProperties;
@@ -973,7 +977,7 @@ public class UserManagerService extends IUserManager.Stub {
mUsers = users != null ? users : new SparseArray<>();
mHandler = new MainHandler();
mInternalExecutor = new ThreadPoolExecutor(/* corePoolSize */ 0, /* maximumPoolSize */ 1,
- /* keepAliveTime */ 1, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
+ /* keepAliveTime */ 24, TimeUnit.HOURS, new LinkedBlockingQueue<>());
mUserVisibilityMediator = new UserVisibilityMediator(mHandler);
mUserDataPreparer = userDataPreparer;
mUserTypes = UserTypeFactory.getUserTypes();
@@ -1676,6 +1680,10 @@ public class UserManagerService extends IUserManager.Stub {
synchronized (mUsersLock) {
userInfo = getUserInfo(userId);
}
+ if (userInfo == null) {
+ throw new IllegalArgumentException("Invalid user. Can't find user details "
+ + "for userId " + userId);
+ }
if (!userInfo.isManagedProfile()) {
throw new IllegalArgumentException("Invalid flags: " + flags
+ ". Can't skip credential check for the user");
@@ -1692,6 +1700,19 @@ public class UserManagerService extends IUserManager.Stub {
if (onlyIfCredentialNotRequired) {
return false;
}
+
+ if (android.multiuser.Flags.showSetScreenLockDialog()) {
+ // Show the prompt to set a new screen lock if the device does not have one
+ final KeyguardManager km = mContext.getSystemService(KeyguardManager.class);
+ if (km != null && !km.isDeviceSecure()) {
+ Intent setScreenLockPromptIntent =
+ SetScreenLockDialogActivity
+ .createBaseIntent(LAUNCH_REASON_DISABLE_QUIET_MODE);
+ setScreenLockPromptIntent.putExtra(EXTRA_ORIGIN_USER_ID, userId);
+ mContext.startActivity(setScreenLockPromptIntent);
+ return false;
+ }
+ }
showConfirmCredentialToDisableQuietMode(userId, target, callingPackage);
return false;
}
@@ -1915,7 +1936,7 @@ public class UserManagerService extends IUserManager.Stub {
if (target != null) {
callBackIntent.putExtra(Intent.EXTRA_INTENT, target);
}
- callBackIntent.putExtra(Intent.EXTRA_USER_ID, userId);
+ callBackIntent.putExtra(EXTRA_USER_ID, userId);
callBackIntent.setPackage(mContext.getPackageName());
callBackIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, callingPackage);
callBackIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index a9d2858fd36e..c2f74a8895cb 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -232,7 +232,8 @@ public class UserRestrictionsUtils {
UserManager.DISALLOW_UNMUTE_MICROPHONE,
UserManager.DISALLOW_UNMUTE_DEVICE,
UserManager.DISALLOW_CAMERA,
- UserManager.DISALLOW_ASSIST_CONTENT
+ UserManager.DISALLOW_ASSIST_CONTENT,
+ UserManager.DISALLOW_CONFIG_DEFAULT_APPS
);
/**
@@ -288,7 +289,8 @@ public class UserRestrictionsUtils {
UserManager.DISALLOW_SMS,
UserManager.DISALLOW_USB_FILE_TRANSFER,
UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA,
- UserManager.DISALLOW_UNMUTE_MICROPHONE
+ UserManager.DISALLOW_UNMUTE_MICROPHONE,
+ UserManager.DISALLOW_CONFIG_DEFAULT_APPS
);
/**
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 067a012ed373..114daaac3c18 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -306,6 +306,7 @@ public final class UserTypeFactory {
.setDarkThemeBadgeColors(
R.color.white)
.setDefaultRestrictions(getDefaultProfileRestrictions())
+ .setDefaultCrossProfileIntentFilters(getDefaultPrivateCrossProfileIntentFilter())
.setDefaultUserProperties(new UserProperties.Builder()
.setStartWithParent(true)
.setCredentialShareableWithParent(true)
@@ -446,6 +447,11 @@ public final class UserTypeFactory {
return DefaultCrossProfileIntentFiltersUtils.getDefaultCloneProfileFilters();
}
+ private static List<DefaultCrossProfileIntentFilter> getDefaultPrivateCrossProfileIntentFilter()
+ {
+ return DefaultCrossProfileIntentFiltersUtils.getDefaultPrivateProfileFilters();
+ }
+
/** Gets a default bundle, keyed by Settings.Secure String names, for non-managed profiles. */
private static Bundle getDefaultNonManagedProfileSecureSettings() {
final Bundle settings = new Bundle();
diff --git a/services/core/java/com/android/server/pm/VerifyingSession.java b/services/core/java/com/android/server/pm/VerifyingSession.java
index dd2b409c7100..1a9e012a7c53 100644
--- a/services/core/java/com/android/server/pm/VerifyingSession.java
+++ b/services/core/java/com/android/server/pm/VerifyingSession.java
@@ -47,6 +47,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.DataLoaderType;
+import android.content.pm.Flags;
import android.content.pm.IPackageInstallObserver2;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageInstaller;
@@ -541,7 +542,12 @@ final class VerifyingSession {
}
final int verificationCodeAtTimeout;
- if (getDefaultVerificationResponse() == PackageManager.VERIFICATION_ALLOW) {
+ // Allows package verification to continue in the event the app being updated is verifying
+ // itself and fails to respond
+ if (Flags.emergencyInstallPermission() && requiredVerifierPackages.contains(
+ pkgLite.packageName)) {
+ verificationCodeAtTimeout = PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT;
+ } else if (getDefaultVerificationResponse() == PackageManager.VERIFICATION_ALLOW) {
verificationCodeAtTimeout = PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT;
} else {
verificationCodeAtTimeout = PackageManager.VERIFICATION_REJECT;
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index f44fcf002a62..21e2bf2e76e5 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -42,6 +42,7 @@ import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.AppOpsManager.AttributionFlags;
import android.app.IActivityManager;
+import android.companion.virtual.VirtualDeviceManager;
import android.content.AttributionSource;
import android.content.AttributionSourceState;
import android.content.Context;
@@ -78,6 +79,7 @@ import com.android.internal.util.Preconditions;
import com.android.internal.util.function.QuadFunction;
import com.android.internal.util.function.TriFunction;
import com.android.server.LocalServices;
+import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.pm.UserManagerService;
import com.android.server.pm.permission.PermissionManagerServiceInternal.HotwordDetectionServiceProvider;
import com.android.server.pm.pkg.AndroidPackage;
@@ -135,6 +137,9 @@ public class PermissionManagerService extends IPermissionManager.Stub {
@Nullable
private HotwordDetectionServiceProvider mHotwordDetectionServiceProvider;
+ @Nullable
+ private VirtualDeviceManagerInternal mVirtualDeviceManagerInternal;
+
PermissionManagerService(@NonNull Context context,
@NonNull ArrayMap<String, FeatureInfo> availableFeatures) {
// The package info cache is the cache for package and permission information.
@@ -146,6 +151,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
mContext = context;
mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class);
mAppOpsManager = context.getSystemService(AppOpsManager.class);
+ mVirtualDeviceManagerInternal =
+ LocalServices.getService(VirtualDeviceManagerInternal.class);
mAttributionSourceRegistry = new AttributionSourceRegistry(context);
@@ -246,16 +253,30 @@ public class PermissionManagerService extends IPermissionManager.Stub {
return PackageManager.PERMISSION_DENIED;
}
+ String persistentDeviceId = getPersistentDeviceId(deviceId);
+
final CheckPermissionDelegate checkPermissionDelegate;
synchronized (mLock) {
checkPermissionDelegate = mCheckPermissionDelegate;
}
-
- if (checkPermissionDelegate == null) {
- return mPermissionManagerServiceImpl.checkUidPermission(uid, permissionName, deviceId);
+ if (checkPermissionDelegate == null) {
+ return mPermissionManagerServiceImpl.checkUidPermission(uid, permissionName,
+ persistentDeviceId);
}
return checkPermissionDelegate.checkUidPermission(uid, permissionName,
- deviceId, mPermissionManagerServiceImpl::checkUidPermission);
+ persistentDeviceId, mPermissionManagerServiceImpl::checkUidPermission);
+ }
+
+ private String getPersistentDeviceId(int deviceId) {
+ if (deviceId == Context.DEVICE_ID_DEFAULT) {
+ return VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
+ }
+
+ if (mVirtualDeviceManagerInternal == null) {
+ mVirtualDeviceManagerInternal =
+ LocalServices.getService(VirtualDeviceManagerInternal.class);
+ }
+ return mVirtualDeviceManagerInternal.getPersistentIdForDevice(deviceId);
}
@Override
@@ -608,15 +629,17 @@ public class PermissionManagerService extends IPermissionManager.Stub {
@Override
public boolean shouldShowRequestPermissionRationale(String packageName, String permissionName,
int deviceId, int userId) {
+ String persistentDeviceId = getPersistentDeviceId(deviceId);
return mPermissionManagerServiceImpl.shouldShowRequestPermissionRationale(packageName,
- permissionName, deviceId, userId);
+ permissionName, persistentDeviceId, userId);
}
@Override
public boolean isPermissionRevokedByPolicy(String packageName, String permissionName,
int deviceId, int userId) {
+ String persistentDeviceId = getPersistentDeviceId(deviceId);
return mPermissionManagerServiceImpl.isPermissionRevokedByPolicy(packageName,
- permissionName, deviceId, userId);
+ permissionName, persistentDeviceId, userId);
}
@Override
@@ -914,13 +937,13 @@ public class PermissionManagerService extends IPermissionManager.Stub {
*
* @param uid the UID to be checked
* @param permissionName the name of the permission to be checked
- * @param deviceId The device ID
+ * @param persistentDeviceId The persistent device ID
* @param superImpl the original implementation that can be delegated to
* @return {@link android.content.pm.PackageManager#PERMISSION_GRANTED} if the package has
* the permission, or {@link android.content.pm.PackageManager#PERMISSION_DENIED} otherwise
*/
- int checkUidPermission(int uid, @NonNull String permissionName, int deviceId,
- TriFunction<Integer, String, Integer, Integer> superImpl);
+ int checkUidPermission(int uid, @NonNull String permissionName, String persistentDeviceId,
+ TriFunction<Integer, String, String, Integer> superImpl);
/**
* @return list of delegated permissions
@@ -965,17 +988,18 @@ public class PermissionManagerService extends IPermissionManager.Stub {
}
@Override
- public int checkUidPermission(int uid, @NonNull String permissionName, int deviceId,
- @NonNull TriFunction<Integer, String, Integer, Integer> superImpl) {
+ public int checkUidPermission(int uid, @NonNull String permissionName,
+ String persistentDeviceId,
+ @NonNull TriFunction<Integer, String, String, Integer> superImpl) {
if (uid == mDelegatedUid && isDelegatedPermission(permissionName)) {
final long identity = Binder.clearCallingIdentity();
try {
- return superImpl.apply(Process.SHELL_UID, permissionName, deviceId);
+ return superImpl.apply(Process.SHELL_UID, permissionName, persistentDeviceId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- return superImpl.apply(uid, permissionName, deviceId);
+ return superImpl.apply(uid, permissionName, persistentDeviceId);
}
@Override
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index c5b637d8b48b..70913c351eeb 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -682,7 +682,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
}
@Override
- public int getPermissionFlags(String packageName, String permName, String persistentDeviceId,
+ public int getPermissionFlags(String packageName, String permName, String deviceId,
int userId) {
final int callingUid = Binder.getCallingUid();
return getPermissionFlagsInternal(packageName, permName, callingUid, userId);
@@ -726,8 +726,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
@Override
public void updatePermissionFlags(String packageName, String permName, int flagMask,
- int flagValues, boolean checkAdjustPolicyFlagPermission, String persistentDeviceId,
- int userId) {
+ int flagValues, boolean checkAdjustPolicyFlagPermission, String deviceId, int userId) {
final int callingUid = Binder.getCallingUid();
boolean overridePolicy = false;
@@ -917,8 +916,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
}
@Override
- public int checkPermission(String pkgName, String permName, String persistentDeviceId,
- int userId) {
+ public int checkPermission(String pkgName, String permName, String deviceId, int userId) {
if (!mUserManagerInt.exists(userId)) {
return PackageManager.PERMISSION_DENIED;
}
@@ -985,11 +983,11 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
}
private int checkUidPermission(int uid, String permName) {
- return checkUidPermission(uid, permName, Context.DEVICE_ID_DEFAULT);
+ return checkUidPermission(uid, permName, VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT);
}
@Override
- public int checkUidPermission(int uid, String permName, int deviceId) {
+ public int checkUidPermission(int uid, String permName, String deviceId) {
final int userId = UserHandle.getUserId(uid);
if (!mUserManagerInt.exists(userId)) {
return PackageManager.PERMISSION_DENIED;
@@ -1001,7 +999,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
@Override
public Map<String, PermissionManager.PermissionState> getAllPermissionStates(
- @NonNull String packageName, @NonNull String persistentDeviceId, int userId) {
+ @NonNull String packageName, @NonNull String deviceId, int userId) {
throw new UnsupportedOperationException(
"This method is supported in newer implementation only");
}
@@ -1315,8 +1313,8 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
}
@Override
- public void grantRuntimePermission(String packageName, String permName,
- String persistentDeviceId, int userId) {
+ public void grantRuntimePermission(String packageName, String permName, String deviceId,
+ int userId) {
final int callingUid = Binder.getCallingUid();
final boolean overridePolicy =
checkUidPermission(callingUid, ADJUST_RUNTIME_PERMISSIONS_POLICY)
@@ -1489,12 +1487,13 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
}
@Override
- public void revokeRuntimePermission(String packageName, String permName,
- String persistentDeviceId, int userId, String reason) {
+ public void revokeRuntimePermission(String packageName, String permName, String deviceId,
+ int userId, String reason) {
final int callingUid = Binder.getCallingUid();
final boolean overridePolicy =
checkUidPermission(callingUid, ADJUST_RUNTIME_PERMISSIONS_POLICY,
- Context.DEVICE_ID_DEFAULT) == PackageManager.PERMISSION_GRANTED;
+ VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT)
+ == PackageManager.PERMISSION_GRANTED;
revokeRuntimePermissionInternal(packageName, permName, overridePolicy, callingUid, userId,
reason, mDefaultPermissionCallback);
@@ -1880,7 +1879,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
@Override
public boolean shouldShowRequestPermissionRationale(String packageName, String permName,
- int deviceId, @UserIdInt int userId) {
+ String deviceId, @UserIdInt int userId) {
final int callingUid = Binder.getCallingUid();
if (UserHandle.getCallingUserId() != userId) {
mContext.enforceCallingPermission(
@@ -1943,7 +1942,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
}
@Override
- public boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId,
+ public boolean isPermissionRevokedByPolicy(String packageName, String permName, String deviceId,
int userId) {
if (UserHandle.getCallingUserId() != userId) {
mContext.enforceCallingPermission(
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
index 7c1042535973..47032ea2d6af 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
@@ -141,11 +141,11 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte
*
* @param packageName the package name for which to get the flags
* @param permName the permission for which to get the flags
- * @param persistentDeviceId The device for which to get the flags
+ * @param deviceId The device for which to get the flags
* @param userId the user for which to get permission flags
* @return the permission flags
*/
- int getPermissionFlags(String packageName, String permName, String persistentDeviceId,
+ int getPermissionFlags(String packageName, String permName, String deviceId,
@UserIdInt int userId);
/**
@@ -156,11 +156,11 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte
* @param permName The permission for which to update the flags
* @param flagMask The flags which to replace
* @param flagValues The flags with which to replace
- * @param persistentDeviceId The device for which to update the permission flags
+ * @param deviceId The device for which to update the permission flags
* @param userId The user for which to update the permission flags
*/
void updatePermissionFlags(String packageName, String permName, int flagMask, int flagValues,
- boolean checkAdjustPolicyFlagPermission, String persistentDeviceId,
+ boolean checkAdjustPolicyFlagPermission, String deviceId,
@UserIdInt int userId);
/**
@@ -295,12 +295,12 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte
*
* @param packageName the package to which to grant the permission
* @param permName the permission name to grant
- * @param persistentDeviceId the device for which to grant the permission
+ * @param deviceId the device for which to grant the permission
* @param userId the user for which to grant the permission
*
* @see #revokeRuntimePermission(String, String, String, int, String)
*/
- void grantRuntimePermission(String packageName, String permName, String persistentDeviceId,
+ void grantRuntimePermission(String packageName, String permName, String deviceId,
@UserIdInt int userId);
/**
@@ -316,13 +316,13 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte
*
* @param packageName the package from which to revoke the permission
* @param permName the permission name to revoke
- * @param persistentDeviceId the device for which to revoke the permission
+ * @param deviceId the device for which to revoke the permission
* @param userId the user for which to revoke the permission
* @param reason the reason for the revoke, or {@code null} for unspecified
*
* @see #grantRuntimePermission(String, String, String, int)
*/
- void revokeRuntimePermission(String packageName, String permName, String persistentDeviceId,
+ void revokeRuntimePermission(String packageName, String permName, String deviceId,
@UserIdInt int userId, String reason);
/**
@@ -347,7 +347,7 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte
* @return whether you can show permission rationale UI
*/
boolean shouldShowRequestPermissionRationale(String packageName, String permName,
- int deviceId, @UserIdInt int userId);
+ String deviceId, @UserIdInt int userId);
/**
* Checks whether a particular permission has been revoked for a package by policy. Typically,
@@ -361,8 +361,8 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte
* @param userId the device for which you are checking the permission
* @return whether the permission is restricted by policy
*/
- boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId,
- @UserIdInt int userId);
+ boolean isPermissionRevokedByPolicy(String packageName, String permName,
+ String deviceId, @UserIdInt int userId);
/**
* Get set of permissions that have been split into more granular or dependent permissions.
@@ -389,11 +389,11 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte
*
* @param pkgName package name
* @param permName permission name
- * @param persistentDeviceId persistent device ID
+ * @param deviceId persistent device ID
* @param userId user ID
* @return permission result {@link PackageManager.PermissionResult}
*/
- int checkPermission(String pkgName, String permName, String persistentDeviceId,
+ int checkPermission(String pkgName, String permName, String deviceId,
@UserIdInt int userId);
/**
@@ -401,23 +401,23 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte
*
* @param uid UID
* @param permName permission name
- * @param deviceId device ID
+ * @param deviceId persistent device ID
* @return permission result {@link PackageManager.PermissionResult}
*/
- int checkUidPermission(int uid, String permName, int deviceId);
+ int checkUidPermission(int uid, String permName, String deviceId);
/**
* Gets the permission states for requested package, persistent device and user.
*
* @param packageName name of the package you are checking against
- * @param persistentDeviceId id of the persistent device you are checking against
+ * @param deviceId id of the persistent device you are checking against
* @param userId id of the user for which to get permission flags
* @return mapping of all permission states keyed by their permission names
*
* @hide
*/
Map<String, PermissionState> getAllPermissionStates(@NonNull String packageName,
- @NonNull String persistentDeviceId, @UserIdInt int userId);
+ @NonNull String deviceId, @UserIdInt int userId);
/**
* Get all the package names requesting app op permissions.
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java
index 91a778d49d61..c18f856594ed 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java
@@ -121,24 +121,22 @@ public class PermissionManagerServiceLoggingDecorator implements PermissionManag
}
@Override
- public int getPermissionFlags(String packageName, String permName, String persistentDeviceId,
+ public int getPermissionFlags(String packageName, String permName, String deviceId,
int userId) {
Log.i(LOG_TAG, "getPermissionFlags(packageName = " + packageName + ", permName = "
- + permName + ", persistentDeviceId = " + persistentDeviceId + ", userId = " + userId
- + ")");
- return mService.getPermissionFlags(packageName, permName, persistentDeviceId, userId);
+ + permName + ", deviceId = " + deviceId + ", userId = " + userId + ")");
+ return mService.getPermissionFlags(packageName, permName, deviceId, userId);
}
@Override
public void updatePermissionFlags(String packageName, String permName, int flagMask,
- int flagValues, boolean checkAdjustPolicyFlagPermission, String persistentDeviceId,
- int userId) {
+ int flagValues, boolean checkAdjustPolicyFlagPermission, String deviceId, int userId) {
Log.i(LOG_TAG, "updatePermissionFlags(packageName = " + packageName + ", permName = "
+ permName + ", flagMask = " + flagMask + ", flagValues = " + flagValues
+ ", checkAdjustPolicyFlagPermission = " + checkAdjustPolicyFlagPermission
- + ", persistentDeviceId = " + persistentDeviceId + ", userId = " + userId + ")");
+ + ", deviceId = " + deviceId + ", userId = " + userId + ")");
mService.updatePermissionFlags(packageName, permName, flagMask, flagValues,
- checkAdjustPolicyFlagPermission, persistentDeviceId, userId);
+ checkAdjustPolicyFlagPermission, deviceId, userId);
}
@Override
@@ -186,21 +184,20 @@ public class PermissionManagerServiceLoggingDecorator implements PermissionManag
}
@Override
- public void grantRuntimePermission(String packageName, String permName,
- String persistentDeviceId, int userId) {
+ public void grantRuntimePermission(String packageName, String permName, String deviceId,
+ int userId) {
Log.i(LOG_TAG, "grantRuntimePermission(packageName = " + packageName + ", permName = "
- + permName + ", persistentDeviceId = " + persistentDeviceId + ", userId = " + userId
- + ")");
- mService.grantRuntimePermission(packageName, permName, persistentDeviceId, userId);
+ + permName + ", deviceId = " + deviceId + ", userId = " + userId + ")");
+ mService.grantRuntimePermission(packageName, permName, deviceId, userId);
}
@Override
- public void revokeRuntimePermission(String packageName, String permName,
- String persistentDeviceId, int userId, String reason) {
+ public void revokeRuntimePermission(String packageName, String permName, String deviceId,
+ int userId, String reason) {
Log.i(LOG_TAG, "revokeRuntimePermission(packageName = " + packageName + ", permName = "
- + permName + ", persistentDeviceId = " + persistentDeviceId + ", userId = " + userId
- + ", reason = " + reason + ")");
- mService.revokeRuntimePermission(packageName, permName, persistentDeviceId, userId, reason);
+ + permName + ", deviceId = " + deviceId + ", userId = " + userId + ", reason = "
+ + reason + ")");
+ mService.revokeRuntimePermission(packageName, permName, deviceId, userId, reason);
}
@Override
@@ -212,16 +209,16 @@ public class PermissionManagerServiceLoggingDecorator implements PermissionManag
@Override
public boolean shouldShowRequestPermissionRationale(String packageName, String permName,
- int deviceId, int userId) {
+ String deviceId, int userId) {
Log.i(LOG_TAG, "shouldShowRequestPermissionRationale(packageName = " + packageName
- + ", permName = " + permName + ", deviceId = " + deviceId
- + ", userId = " + userId + ")");
+ + ", permName = " + permName + ", deviceId = " + deviceId + ", userId = "
+ + userId + ")");
return mService.shouldShowRequestPermissionRationale(packageName, permName, deviceId,
userId);
}
@Override
- public boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId,
+ public boolean isPermissionRevokedByPolicy(String packageName, String permName, String deviceId,
int userId) {
Log.i(LOG_TAG, "isPermissionRevokedByPolicy(packageName = " + packageName + ", permName = "
+ permName + ", deviceId = " + deviceId + ", userId = " + userId + ")");
@@ -235,26 +232,27 @@ public class PermissionManagerServiceLoggingDecorator implements PermissionManag
}
@Override
- public int checkPermission(String pkgName, String permName, String persistentDeviceId,
+ public int checkPermission(String pkgName, String permName, String deviceId,
int userId) {
Log.i(LOG_TAG, "checkPermission(pkgName = " + pkgName + ", permName = " + permName
- + ", persistentDeviceId = " + persistentDeviceId + ", userId = " + userId + ")");
- return mService.checkPermission(pkgName, permName, persistentDeviceId, userId);
+ + ", deviceId = " + deviceId + ", userId = " + userId + ")");
+ return mService.checkPermission(pkgName, permName, deviceId, userId);
}
@Override
- public int checkUidPermission(int uid, String permName, int deviceId) {
- Log.i(LOG_TAG, "checkUidPermission(uid = " + uid + ", permName = "
- + permName + ", deviceId = " + deviceId + ")");
+ public int checkUidPermission(int uid, String permName, String deviceId) {
+ Log.i(LOG_TAG, "checkUidPermission(uid = " + uid + ", permName = " + permName
+ + ", deviceId = " + deviceId + ")");
return mService.checkUidPermission(uid, permName, deviceId);
}
@Override
public Map<String, PermissionState> getAllPermissionStates(@NonNull String packageName,
- @NonNull String persistentDeviceId, int userId) {
- Log.i(LOG_TAG, "getAllPermissionStates(packageName = " + packageName
- + ", persistentDeviceId = " + persistentDeviceId + ", userId = " + userId + ")");
- return mService.getAllPermissionStates(packageName, persistentDeviceId, userId);
+ @NonNull String deviceId, int userId) {
+ Log.i(LOG_TAG,
+ "getAllPermissionStates(packageName = " + packageName + ", deviceId = " + deviceId
+ + ", userId = " + userId + ")");
+ return mService.getAllPermissionStates(packageName, deviceId, userId);
}
@Override
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java
index 0a4ff0797c97..40139baf0e98 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java
@@ -154,12 +154,10 @@ public class PermissionManagerServiceTestingShim implements PermissionManagerSer
}
@Override
- public int getPermissionFlags(String packageName, String permName, String persistentDeviceId,
+ public int getPermissionFlags(String packageName, String permName, String deviceId,
@UserIdInt int userId) {
- int oldVal = mOldImplementation.getPermissionFlags(packageName, permName,
- persistentDeviceId, userId);
- int newVal = mNewImplementation.getPermissionFlags(packageName, permName,
- persistentDeviceId, userId);
+ int oldVal = mOldImplementation.getPermissionFlags(packageName, permName, deviceId, userId);
+ int newVal = mNewImplementation.getPermissionFlags(packageName, permName, deviceId, userId);
if (!Objects.equals(oldVal, newVal)) {
signalImplDifference("getPermissionFlags");
@@ -169,12 +167,12 @@ public class PermissionManagerServiceTestingShim implements PermissionManagerSer
@Override
public void updatePermissionFlags(String packageName, String permName, int flagMask,
- int flagValues, boolean checkAdjustPolicyFlagPermission, String persistentDeviceId,
+ int flagValues, boolean checkAdjustPolicyFlagPermission, String deviceId,
@UserIdInt int userId) {
mOldImplementation.updatePermissionFlags(packageName, permName, flagMask, flagValues,
- checkAdjustPolicyFlagPermission, persistentDeviceId, userId);
+ checkAdjustPolicyFlagPermission, deviceId, userId);
mNewImplementation.updatePermissionFlags(packageName, permName, flagMask, flagValues,
- checkAdjustPolicyFlagPermission, persistentDeviceId, userId);
+ checkAdjustPolicyFlagPermission, deviceId, userId);
}
@Override
@@ -239,21 +237,17 @@ public class PermissionManagerServiceTestingShim implements PermissionManagerSer
}
@Override
- public void grantRuntimePermission(String packageName, String permName,
- String persistentDeviceId, @UserIdInt int userId) {
- mOldImplementation.grantRuntimePermission(packageName, permName, persistentDeviceId,
- userId);
- mNewImplementation.grantRuntimePermission(packageName, permName, persistentDeviceId,
- userId);
+ public void grantRuntimePermission(String packageName, String permName, String deviceId,
+ @UserIdInt int userId) {
+ mOldImplementation.grantRuntimePermission(packageName, permName, deviceId, userId);
+ mNewImplementation.grantRuntimePermission(packageName, permName, deviceId, userId);
}
@Override
- public void revokeRuntimePermission(String packageName, String permName,
- String persistentDeviceId, @UserIdInt int userId, String reason) {
- mOldImplementation.revokeRuntimePermission(packageName, permName, persistentDeviceId,
- userId, reason);
- mNewImplementation.revokeRuntimePermission(packageName, permName, persistentDeviceId,
- userId, reason);
+ public void revokeRuntimePermission(String packageName, String permName, String deviceId,
+ @UserIdInt int userId, String reason) {
+ mOldImplementation.revokeRuntimePermission(packageName, permName, deviceId, userId, reason);
+ mNewImplementation.revokeRuntimePermission(packageName, permName, deviceId, userId, reason);
}
@Override
@@ -265,7 +259,7 @@ public class PermissionManagerServiceTestingShim implements PermissionManagerSer
@Override
public boolean shouldShowRequestPermissionRationale(String packageName, String permName,
- int deviceId, @UserIdInt int userId) {
+ String deviceId, @UserIdInt int userId) {
boolean oldVal = mOldImplementation.shouldShowRequestPermissionRationale(packageName,
permName, deviceId, userId);
boolean newVal = mNewImplementation.shouldShowRequestPermissionRationale(packageName,
@@ -278,7 +272,7 @@ public class PermissionManagerServiceTestingShim implements PermissionManagerSer
}
@Override
- public boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId,
+ public boolean isPermissionRevokedByPolicy(String packageName, String permName, String deviceId,
@UserIdInt int userId) {
boolean oldVal = mOldImplementation.isPermissionRevokedByPolicy(packageName, permName,
deviceId, userId);
@@ -303,12 +297,9 @@ public class PermissionManagerServiceTestingShim implements PermissionManagerSer
}
@Override
- public int checkPermission(String pkgName, String permName, String persistentDeviceId,
- int userId) {
- int oldVal = mOldImplementation.checkPermission(pkgName, permName, persistentDeviceId,
- userId);
- int newVal = mNewImplementation.checkPermission(pkgName, permName, persistentDeviceId,
- userId);
+ public int checkPermission(String pkgName, String permName, String deviceId, int userId) {
+ int oldVal = mOldImplementation.checkPermission(pkgName, permName, deviceId, userId);
+ int newVal = mNewImplementation.checkPermission(pkgName, permName, deviceId, userId);
if (!Objects.equals(oldVal, newVal)) {
signalImplDifference("checkPermission");
@@ -317,7 +308,7 @@ public class PermissionManagerServiceTestingShim implements PermissionManagerSer
}
@Override
- public int checkUidPermission(int uid, String permName, int deviceId) {
+ public int checkUidPermission(int uid, String permName, String deviceId) {
int oldVal = mOldImplementation.checkUidPermission(uid, permName, deviceId);
int newVal = mNewImplementation.checkUidPermission(uid, permName, deviceId);
@@ -329,8 +320,8 @@ public class PermissionManagerServiceTestingShim implements PermissionManagerSer
@Override
public Map<String, PermissionState> getAllPermissionStates(@NonNull String packageName,
- @NonNull String persistentDeviceId, int userId) {
- return mNewImplementation.getAllPermissionStates(packageName, persistentDeviceId, userId);
+ @NonNull String deviceId, int userId) {
+ return mNewImplementation.getAllPermissionStates(packageName, deviceId, userId);
}
@Override
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java
index bc29e6773bca..981d3d92b15a 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java
@@ -159,11 +159,11 @@ public class PermissionManagerServiceTracingDecorator implements PermissionManag
}
@Override
- public int getPermissionFlags(String packageName, String permName, String persistentDeviceId,
+ public int getPermissionFlags(String packageName, String permName, String deviceId,
int userId) {
Trace.traceBegin(TRACE_TAG, "TaggedTracingPermissionManagerServiceImpl#getPermissionFlags");
try {
- return mService.getPermissionFlags(packageName, permName, persistentDeviceId, userId);
+ return mService.getPermissionFlags(packageName, permName, deviceId, userId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
@@ -171,13 +171,12 @@ public class PermissionManagerServiceTracingDecorator implements PermissionManag
@Override
public void updatePermissionFlags(String packageName, String permName, int flagMask,
- int flagValues, boolean checkAdjustPolicyFlagPermission, String persistentDeviceId,
- int userId) {
+ int flagValues, boolean checkAdjustPolicyFlagPermission, String deviceId, int userId) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingPermissionManagerServiceImpl#updatePermissionFlags");
try {
mService.updatePermissionFlags(packageName, permName, flagMask, flagValues,
- checkAdjustPolicyFlagPermission, persistentDeviceId, userId);
+ checkAdjustPolicyFlagPermission, deviceId, userId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
@@ -256,25 +255,24 @@ public class PermissionManagerServiceTracingDecorator implements PermissionManag
}
@Override
- public void grantRuntimePermission(String packageName, String permName,
- String persistentDeviceId, int userId) {
+ public void grantRuntimePermission(String packageName, String permName, String deviceId,
+ int userId) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingPermissionManagerServiceImpl#grantRuntimePermission");
try {
- mService.grantRuntimePermission(packageName, permName, persistentDeviceId, userId);
+ mService.grantRuntimePermission(packageName, permName, deviceId, userId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
}
@Override
- public void revokeRuntimePermission(String packageName, String permName,
- String persistentDeviceId, int userId, String reason) {
+ public void revokeRuntimePermission(String packageName, String permName, String deviceId,
+ int userId, String reason) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingPermissionManagerServiceImpl#revokeRuntimePermission");
try {
- mService.revokeRuntimePermission(packageName, permName, persistentDeviceId, userId,
- reason);
+ mService.revokeRuntimePermission(packageName, permName, deviceId, userId, reason);
} finally {
Trace.traceEnd(TRACE_TAG);
}
@@ -293,19 +291,19 @@ public class PermissionManagerServiceTracingDecorator implements PermissionManag
@Override
public boolean shouldShowRequestPermissionRationale(String packageName, String permName,
- int deviceId, int userId) {
+ String deviceId, int userId) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingPermissionManagerServiceImpl#shouldShowRequestPermissionRationale");
try {
- return mService.shouldShowRequestPermissionRationale(
- packageName, permName, deviceId, userId);
+ return mService.shouldShowRequestPermissionRationale(packageName, permName, deviceId,
+ userId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
}
@Override
- public boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId,
+ public boolean isPermissionRevokedByPolicy(String packageName, String permName, String deviceId,
int userId) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingPermissionManagerServiceImpl#isPermissionRevokedByPolicy");
@@ -328,18 +326,17 @@ public class PermissionManagerServiceTracingDecorator implements PermissionManag
}
@Override
- public int checkPermission(String pkgName, String permName, String persistentDeviceId,
- int userId) {
+ public int checkPermission(String pkgName, String permName, String deviceId, int userId) {
Trace.traceBegin(TRACE_TAG, "TaggedTracingPermissionManagerServiceImpl#checkPermission");
try {
- return mService.checkPermission(pkgName, permName, persistentDeviceId, userId);
+ return mService.checkPermission(pkgName, permName, deviceId, userId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
}
@Override
- public int checkUidPermission(int uid, String permName, int deviceId) {
+ public int checkUidPermission(int uid, String permName, String deviceId) {
Trace.traceBegin(TRACE_TAG, "TaggedTracingPermissionManagerServiceImpl#checkUidPermission");
try {
return mService.checkUidPermission(uid, permName, deviceId);
@@ -350,11 +347,11 @@ public class PermissionManagerServiceTracingDecorator implements PermissionManag
@Override
public Map<String, PermissionState> getAllPermissionStates(@NonNull String packageName,
- @NonNull String persistentDeviceId, int userId) {
+ @NonNull String deviceId, int userId) {
Trace.traceBegin(TRACE_TAG, "TaggedTracingPermissionManagerServiceImpl"
+ "#getAllPermissionStates");
try {
- return mService.getAllPermissionStates(packageName, persistentDeviceId, userId);
+ return mService.getAllPermissionStates(packageName, deviceId, userId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
index 76952b30c1e9..1b220a0b1485 100644
--- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
@@ -17,7 +17,7 @@
package com.android.server.policy;
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
-import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
+import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE_IDENTIFIER;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -94,7 +94,7 @@ public final class DeviceStateProviderImpl implements DeviceStateProvider,
private static final BooleanSupplier FALSE_BOOLEAN_SUPPLIER = () -> false;
@VisibleForTesting
- static final DeviceState DEFAULT_DEVICE_STATE = new DeviceState(MINIMUM_DEVICE_STATE,
+ static final DeviceState DEFAULT_DEVICE_STATE = new DeviceState(MINIMUM_DEVICE_STATE_IDENTIFIER,
"DEFAULT", 0 /* flags */);
private static final String VENDOR_CONFIG_FILE_PATH = "etc/devicestate/";
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index bc260184e487..8781bf19565e 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -236,6 +236,7 @@ import com.android.server.policy.keyguard.KeyguardServiceDelegate.DrawnListener;
import com.android.server.policy.keyguard.KeyguardStateMonitor.StateCallback;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.vibrator.HapticFeedbackVibrationProvider;
+import com.android.server.vibrator.VibratorFrameworkStatsLogger;
import com.android.server.vr.VrManagerInternal;
import com.android.server.wallpaper.WallpaperManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -3509,6 +3510,16 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
break;
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) {
+ StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
+ if (statusbar != null) {
+ statusbar.enterDesktop(event.getDisplayId());
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.DESKTOP_MODE);
+ return true;
+ }
+ }
+ break;
case KeyEvent.KEYCODE_DPAD_LEFT:
if (firstDown && event.isMetaPressed()) {
if (event.isCtrlPressed()) {
@@ -6421,6 +6432,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
VibrationAttributes attrs =
mHapticFeedbackVibrationProvider.getVibrationAttributesForHapticFeedback(
effectId, /* bypassVibrationIntensitySetting= */ always);
+ VibratorFrameworkStatsLogger.logPerformHapticsFeedbackIfKeyboard(uid, effectId);
mVibrator.vibrate(uid, packageName, effect, reason, attrs);
return true;
}
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
index b22e37bd579b..c8cb92b915da 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
@@ -167,6 +167,9 @@ public class BatterySaverStateMachine {
/** Config flag to track if battery saver's sticky behaviour is disabled. */
private final boolean mBatterySaverStickyBehaviourDisabled;
+ /** Config flag to track if "Battery Saver turned off" notification is enabled. */
+ private final boolean mBatterySaverTurnedOffNotificationEnabled;
+
/**
* Whether or not to end sticky battery saver upon reaching a level specified by
* {@link #mSettingBatterySaverStickyAutoDisableThreshold}.
@@ -250,6 +253,8 @@ public class BatterySaverStateMachine {
mBatterySaverStickyBehaviourDisabled = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_batterySaverStickyBehaviourDisabled);
+ mBatterySaverTurnedOffNotificationEnabled = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_batterySaverTurnedOffNotificationEnabled);
mDynamicPowerSavingsDefaultDisableThreshold = mContext.getResources().getInteger(
com.android.internal.R.integer.config_dynamicPowerSavingsDefaultDisableThreshold);
}
@@ -858,6 +863,9 @@ public class BatterySaverStateMachine {
@VisibleForTesting
void triggerStickyDisabledNotification() {
+ if (!mBatterySaverTurnedOffNotificationEnabled) {
+ return;
+ }
// The current lock is the PowerManager lock, which sits very low in the service lock
// hierarchy. We shouldn't call out to NotificationManager with the PowerManager lock.
runOnBgThread(() -> {
@@ -997,6 +1005,8 @@ public class BatterySaverStateMachine {
ipw.println(mSettingBatterySaverTriggerThreshold);
ipw.print("mBatterySaverStickyBehaviourDisabled=");
ipw.println(mBatterySaverStickyBehaviourDisabled);
+ ipw.print("mBatterySaverTurnedOffNotificationEnabled=");
+ ipw.println(mBatterySaverTurnedOffNotificationEnabled);
ipw.print("mDynamicPowerSavingsDefaultDisableThreshold=");
ipw.println(mDynamicPowerSavingsDefaultDisableThreshold);
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index af4da812d79e..8e3c6ac799b4 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -1591,32 +1591,76 @@ public class BatteryStatsImpl extends BatteryStats {
@Override
public WakeLockStats getWakeLockStats() {
final long realtimeMs = mClock.elapsedRealtime();
- final long realtimeUs = realtimeMs * 1000;
List<WakeLockStats.WakeLock> uidWakeLockStats = new ArrayList<>();
+ List<WakeLockStats.WakeLock> uidAggregatedWakeLockStats = new ArrayList<>();
for (int i = mUidStats.size() - 1; i >= 0; i--) {
final Uid uid = mUidStats.valueAt(i);
+
+ // Converts unaggregated wakelocks.
final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats =
uid.mWakelockStats.getMap();
for (int j = wakelockStats.size() - 1; j >= 0; j--) {
final String name = wakelockStats.keyAt(j);
final Uid.Wakelock wakelock = (Uid.Wakelock) wakelockStats.valueAt(j);
- final DualTimer timer = wakelock.mTimerPartial;
- if (timer != null) {
- final long totalTimeLockHeldMs =
- timer.getTotalTimeLocked(realtimeUs, STATS_SINCE_CHARGED) / 1000;
- if (totalTimeLockHeldMs != 0) {
- uidWakeLockStats.add(
- new WakeLockStats.WakeLock(uid.getUid(), name,
- timer.getCountLocked(STATS_SINCE_CHARGED),
- totalTimeLockHeldMs,
- timer.isRunningLocked()
- ? timer.getCurrentDurationMsLocked(realtimeMs)
- : 0));
- }
+ final WakeLockStats.WakeLock wakeLockItem =
+ createWakeLock(uid, name, /* isAggregated= */ false, wakelock.mTimerPartial,
+ realtimeMs);
+ if (wakeLockItem != null) {
+ uidWakeLockStats.add(wakeLockItem);
}
}
+
+ // Converts aggregated wakelocks.
+ final WakeLockStats.WakeLock aggregatedWakeLockItem =
+ createWakeLock(
+ uid,
+ WakeLockStats.WakeLock.NAME_AGGREGATED,
+ /* isAggregated= */ true,
+ uid.mAggregatedPartialWakelockTimer,
+ realtimeMs);
+ if (aggregatedWakeLockItem != null) {
+ uidAggregatedWakeLockStats.add(aggregatedWakeLockItem);
+ }
+ }
+ return new WakeLockStats(uidWakeLockStats, uidAggregatedWakeLockStats);
+ }
+
+ // Returns a valid {@code WakeLockStats.WakeLock} or null.
+ private WakeLockStats.WakeLock createWakeLock(
+ Uid uid, String name, boolean isAggregated, DualTimer timer, final long realtimeMs) {
+ if (timer == null) {
+ return null;
}
- return new WakeLockStats(uidWakeLockStats);
+ // Uses the primary timer for total wakelock data and used the sub timer for background
+ // wakelock data.
+ final WakeLockStats.WakeLockData totalWakeLockData = createWakeLockData(timer, realtimeMs);
+ final WakeLockStats.WakeLockData backgroundWakeLockData =
+ createWakeLockData(timer.getSubTimer(), realtimeMs);
+
+ return WakeLockStats.WakeLock.isDataValid(totalWakeLockData, backgroundWakeLockData)
+ ? new WakeLockStats.WakeLock(
+ uid.getUid(),
+ name,
+ isAggregated,
+ totalWakeLockData,
+ backgroundWakeLockData) : null;
+ }
+
+ @NonNull
+ private WakeLockStats.WakeLockData createWakeLockData(
+ DurationTimer timer, final long realtimeMs) {
+ if (timer == null) {
+ return WakeLockStats.WakeLockData.EMPTY;
+ }
+ final long totalTimeLockHeldMs =
+ timer.getTotalTimeLocked(realtimeMs * 1000, STATS_SINCE_CHARGED) / 1000;
+ if (totalTimeLockHeldMs == 0) {
+ return WakeLockStats.WakeLockData.EMPTY;
+ }
+ return new WakeLockStats.WakeLockData(
+ timer.getCountLocked(STATS_SINCE_CHARGED),
+ totalTimeLockHeldMs,
+ timer.isRunningLocked() ? timer.getCurrentDurationMsLocked(realtimeMs) : 0);
}
@Override
@@ -1817,9 +1861,8 @@ public class BatteryStatsImpl extends BatteryStats {
if (historyDirectory == null) {
mCheckinFile = null;
mStatsFile = null;
- mHistory = new BatteryStatsHistory(mConstants.MAX_HISTORY_FILES,
- mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock, mMonotonicClock,
- traceDelegate, eventLogger);
+ mHistory = new BatteryStatsHistory(mConstants.MAX_HISTORY_BUFFER,
+ mStepDetailsCalculator, mClock, mMonotonicClock, traceDelegate, eventLogger);
} else {
mCheckinFile = new AtomicFile(new File(historyDirectory, "batterystats-checkin.bin"));
mStatsFile = new AtomicFile(new File(historyDirectory, "batterystats.bin"));
@@ -10962,8 +11005,8 @@ public class BatteryStatsImpl extends BatteryStats {
mStatsFile = null;
mCheckinFile = null;
mDailyFile = null;
- mHistory = new BatteryStatsHistory(mConstants.MAX_HISTORY_FILES,
- mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock, mMonotonicClock);
+ mHistory = new BatteryStatsHistory(mConstants.MAX_HISTORY_BUFFER,
+ mStepDetailsCalculator, mClock, mMonotonicClock);
} else {
mStatsFile = new AtomicFile(new File(systemDir, "batterystats.bin"));
mCheckinFile = new AtomicFile(new File(systemDir, "batterystats-checkin.bin"));
@@ -13854,7 +13897,9 @@ public class BatteryStatsImpl extends BatteryStats {
mNumAllUidCpuTimeReads += 2;
}
- updateSystemServerThreadStats();
+ if (!Flags.disableSystemServicePowerAttr()) {
+ updateSystemServerThreadStats();
+ }
if (powerAccumulator != null) {
updateCpuEnergyConsumerStatsLocked(cpuClusterChargeUC, powerAccumulator);
@@ -15428,17 +15473,18 @@ public class BatteryStatsImpl extends BatteryStats {
mPerDisplayBatteryStats[i].screenStateAtLastEnergyMeasurement = screenState;
}
- final boolean compatibleConfig;
if (supportedStandardBuckets != null) {
final EnergyConsumerStats.Config config = new EnergyConsumerStats.Config(
supportedStandardBuckets, customBucketNames,
SUPPORTED_PER_PROCESS_STATE_STANDARD_ENERGY_BUCKETS,
getBatteryConsumerProcessStateNames());
- if (mEnergyConsumerStatsConfig == null) {
- compatibleConfig = true;
- } else {
- compatibleConfig = mEnergyConsumerStatsConfig.isCompatible(config);
+ if (mEnergyConsumerStatsConfig != null
+ && !mEnergyConsumerStatsConfig.isCompatible(config)) {
+ // Supported power buckets changed since last boot.
+ // Existing data is no longer reliable.
+ resetAllStatsLocked(SystemClock.uptimeMillis(), SystemClock.elapsedRealtime(),
+ RESET_REASON_ENERGY_CONSUMER_BUCKETS_CHANGE);
}
mEnergyConsumerStatsConfig = config;
@@ -15454,18 +15500,14 @@ public class BatteryStatsImpl extends BatteryStats {
mWifiPowerCalculator = new WifiPowerCalculator(mPowerProfile);
}
} else {
- compatibleConfig = (mEnergyConsumerStatsConfig == null);
- // EnergyConsumer no longer supported, wipe out the existing data.
+ if (mEnergyConsumerStatsConfig != null) {
+ // EnergyConsumer no longer supported, wipe out the existing data.
+ resetAllStatsLocked(SystemClock.uptimeMillis(), SystemClock.elapsedRealtime(),
+ RESET_REASON_ENERGY_CONSUMER_BUCKETS_CHANGE);
+ }
mEnergyConsumerStatsConfig = null;
mGlobalEnergyConsumerStats = null;
}
-
- if (!compatibleConfig) {
- // Supported power buckets changed since last boot.
- // Existing data is no longer reliable.
- resetAllStatsLocked(SystemClock.uptimeMillis(), SystemClock.elapsedRealtime(),
- RESET_REASON_ENERGY_CONSUMER_BUCKETS_CHANGE);
- }
}
@GuardedBy("this")
diff --git a/services/core/java/com/android/server/power/stats/MobileRadioPowerCalculator.java b/services/core/java/com/android/server/power/stats/MobileRadioPowerCalculator.java
index 1050e8a371e8..9ea143e5c201 100644
--- a/services/core/java/com/android/server/power/stats/MobileRadioPowerCalculator.java
+++ b/services/core/java/com/android/server/power/stats/MobileRadioPowerCalculator.java
@@ -32,7 +32,6 @@ import com.android.internal.power.ModemPowerProfile;
import java.util.ArrayList;
-@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class MobileRadioPowerCalculator extends PowerCalculator {
private static final String TAG = "MobRadioPowerCalculator";
private static final boolean DEBUG = PowerCalculator.DEBUG;
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 359678b15213..2a9325544833 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -1212,13 +1212,20 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba
rollback.makeAvailable();
mPackageHealthObserver.notifyRollbackAvailable(rollback.info);
- // TODO(zezeozue): Provide API to explicitly start observing instead
- // of doing this for all rollbacks. If we do this for all rollbacks,
- // should document in PackageInstaller.SessionParams#setEnableRollback
- // After enabling and committing any rollback, observe packages and
- // prepare to rollback if packages crashes too frequently.
- mPackageHealthObserver.startObservingHealth(rollback.getPackageNames(),
- mRollbackLifetimeDurationInMillis);
+ if (Flags.recoverabilityDetection()) {
+ if (rollback.info.getRollbackImpactLevel() == PackageManager.ROLLBACK_USER_IMPACT_LOW) {
+ // TODO(zezeozue): Provide API to explicitly start observing instead
+ // of doing this for all rollbacks. If we do this for all rollbacks,
+ // should document in PackageInstaller.SessionParams#setEnableRollback
+ // After enabling and committing any rollback, observe packages and
+ // prepare to rollback if packages crashes too frequently.
+ mPackageHealthObserver.startObservingHealth(rollback.getPackageNames(),
+ mRollbackLifetimeDurationInMillis);
+ }
+ } else {
+ mPackageHealthObserver.startObservingHealth(rollback.getPackageNames(),
+ mRollbackLifetimeDurationInMillis);
+ }
runExpiration();
}
diff --git a/services/core/java/com/android/server/search/SearchManagerService.java b/services/core/java/com/android/server/search/SearchManagerService.java
index 7091c47b8e83..ecfc040ae29c 100644
--- a/services/core/java/com/android/server/search/SearchManagerService.java
+++ b/services/core/java/com/android/server/search/SearchManagerService.java
@@ -17,6 +17,7 @@
package com.android.server.search;
import android.annotation.NonNull;
+import android.annotation.UserIdInt;
import android.app.ISearchManager;
import android.app.SearchManager;
import android.app.SearchableInfo;
@@ -24,6 +25,7 @@ import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.database.ContentObserver;
import android.os.Binder;
@@ -32,6 +34,7 @@ import android.os.Handler;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
@@ -47,6 +50,7 @@ import com.android.server.statusbar.StatusBarManagerInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -71,11 +75,6 @@ public class SearchManagerService extends ISearchManager.Stub {
}
@Override
- public void onUserUnlocking(@NonNull TargetUser user) {
- mService.mHandler.post(() -> mService.onUnlockUser(user.getUserIdentifier()));
- }
-
- @Override
public void onUserStopped(@NonNull TargetUser user) {
mService.onCleanupUser(user.getUserIdentifier());
}
@@ -102,10 +101,6 @@ public class SearchManagerService extends ISearchManager.Stub {
}
private Searchables getSearchables(int userId) {
- return getSearchables(userId, false);
- }
-
- private Searchables getSearchables(int userId, boolean forceUpdate) {
final long token = Binder.clearCallingIdentity();
try {
final UserManager um = mContext.getSystemService(UserManager.class);
@@ -122,21 +117,11 @@ public class SearchManagerService extends ISearchManager.Stub {
Searchables searchables = mSearchables.get(userId);
if (searchables == null) {
searchables = new Searchables(mContext, userId);
- searchables.updateSearchableList();
- mSearchables.append(userId, searchables);
- } else if (forceUpdate) {
- searchables.updateSearchableList();
+ mSearchables.put(userId, searchables);
}
- return searchables;
- }
- }
- private void onUnlockUser(int userId) {
- try {
- getSearchables(userId, true);
- } catch (IllegalStateException ignored) {
- // We're just trying to warm a cache, so we don't mind if the user
- // was stopped or destroyed before we got here.
+ searchables.updateSearchableListIfNeeded();
+ return searchables;
}
}
@@ -150,28 +135,110 @@ public class SearchManagerService extends ISearchManager.Stub {
* Refreshes the "searchables" list when packages are added/removed.
*/
class MyPackageMonitor extends PackageMonitor {
+ /**
+ * Packages that are appeared, disappeared, or modified for whatever reason.
+ */
+ private final ArrayList<String> mChangedPackages = new ArrayList<>();
+
+ /**
+ * {@code true} if one or more packages that contain {@link SearchableInfo} appeared.
+ */
+ private boolean mSearchablePackageAppeared = false;
+
+ @Override
+ public void onBeginPackageChanges() {
+ clearPackageChangeState();
+ }
+
+ @Override
+ public void onPackageAppeared(String packageName, int reason) {
+ if (!mSearchablePackageAppeared) {
+ // Check if the new appeared package contains SearchableInfo.
+ mSearchablePackageAppeared =
+ hasSearchableForPackage(packageName, getChangingUserId());
+ }
+ mChangedPackages.add(packageName);
+ }
@Override
- public void onSomePackagesChanged() {
- updateSearchables();
+ public void onPackageDisappeared(String packageName, int reason) {
+ mChangedPackages.add(packageName);
}
@Override
- public void onPackageModified(String pkg) {
- updateSearchables();
+ public void onPackageModified(String packageName) {
+ mChangedPackages.add(packageName);
}
- private void updateSearchables() {
+ @Override
+ public void onFinishPackageChanges() {
+ onFinishPackageChangesInternal();
+ clearPackageChangeState();
+ }
+
+ private void clearPackageChangeState() {
+ mChangedPackages.clear();
+ mSearchablePackageAppeared = false;
+ }
+
+ private boolean hasSearchableForPackage(String packageName, int userId) {
+ final List<ResolveInfo> searchList = querySearchableActivities(mContext,
+ new Intent(Intent.ACTION_SEARCH).setPackage(packageName), userId);
+ if (!searchList.isEmpty()) {
+ return true;
+ }
+
+ final List<ResolveInfo> webSearchList = querySearchableActivities(mContext,
+ new Intent(Intent.ACTION_WEB_SEARCH).setPackage(packageName), userId);
+ if (!webSearchList.isEmpty()) {
+ return true;
+ }
+
+ final List<ResolveInfo> globalSearchList = querySearchableActivities(mContext,
+ new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH).setPackage(packageName),
+ userId);
+ return !globalSearchList.isEmpty();
+ }
+
+ private boolean shouldRebuildSearchableList(@UserIdInt int changingUserId) {
+ // This method is guaranteed to be called only on getRegisteredHandler()
+ if (mSearchablePackageAppeared) {
+ return true;
+ }
+
+ ArraySet<String> knownSearchablePackageNames = new ArraySet<>();
+ synchronized (mSearchables) {
+ Searchables searchables = mSearchables.get(changingUserId);
+ if (searchables != null) {
+ knownSearchablePackageNames = searchables.getKnownSearchablePackageNames();
+ }
+ }
+
+ final int numOfPackages = mChangedPackages.size();
+ for (int i = 0; i < numOfPackages; i++) {
+ final String packageName = mChangedPackages.get(i);
+ if (knownSearchablePackageNames.contains(packageName)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private void onFinishPackageChangesInternal() {
final int changingUserId = getChangingUserId();
+ if (!shouldRebuildSearchableList(changingUserId)) {
+ return;
+ }
+
synchronized (mSearchables) {
- // Update list of searchable activities
- for (int i = 0; i < mSearchables.size(); i++) {
- if (changingUserId == mSearchables.keyAt(i)) {
- mSearchables.valueAt(i).updateSearchableList();
- break;
- }
+ // Invalidate the searchable list.
+ Searchables searchables = mSearchables.get(changingUserId);
+ if (searchables != null) {
+ searchables.invalidateSearchableList();
}
}
+
// Inform all listeners that the list of searchables has been updated.
Intent intent = new Intent(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
@@ -180,6 +247,17 @@ public class SearchManagerService extends ISearchManager.Stub {
}
}
+ @NonNull
+ static List<ResolveInfo> querySearchableActivities(Context context, Intent searchIntent,
+ @UserIdInt int userId) {
+ final List<ResolveInfo> activities = context.getPackageManager()
+ .queryIntentActivitiesAsUser(searchIntent, PackageManager.GET_META_DATA
+ | PackageManager.MATCH_INSTANT
+ | PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId);
+ return activities;
+ }
+
+
class GlobalSearchProviderObserver extends ContentObserver {
private final ContentResolver mResolver;
@@ -196,7 +274,7 @@ public class SearchManagerService extends ISearchManager.Stub {
public void onChange(boolean selfChange) {
synchronized (mSearchables) {
for (int i = 0; i < mSearchables.size(); i++) {
- mSearchables.valueAt(i).updateSearchableList();
+ mSearchables.valueAt(i).invalidateSearchableList();
}
}
Intent intent = new Intent(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
diff --git a/services/core/java/com/android/server/search/Searchables.java b/services/core/java/com/android/server/search/Searchables.java
index 6e1e979b1bac..dc6733941357 100644
--- a/services/core/java/com/android/server/search/Searchables.java
+++ b/services/core/java/com/android/server/search/Searchables.java
@@ -35,8 +35,10 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
import com.android.server.LocalServices;
import java.io.FileDescriptor;
@@ -62,7 +64,6 @@ public class Searchables {
private static final String MD_SEARCHABLE_SYSTEM_SEARCH = "*";
private Context mContext;
-
private HashMap<ComponentName, SearchableInfo> mSearchablesMap = null;
private ArrayList<SearchableInfo> mSearchablesList = null;
private ArrayList<SearchableInfo> mSearchablesInGlobalSearchList = null;
@@ -81,6 +82,12 @@ public class Searchables {
final private IPackageManager mPm;
// User for which this Searchables caches information
private int mUserId;
+ @GuardedBy("this")
+ private boolean mRebuildSearchables = true;
+
+ // Package names that are known to contain {@link SearchableInfo}
+ @GuardedBy("this")
+ private ArraySet<String> mKnownSearchablePackageNames = new ArraySet<>();
/**
*
@@ -147,6 +154,9 @@ public class Searchables {
Log.e(LOG_TAG, "Error getting activity info " + re);
return null;
}
+ if (ai == null) {
+ return null;
+ }
String refActivityName = null;
// First look for activity-specific reference
@@ -221,7 +231,14 @@ public class Searchables {
*
* TODO: sort the list somehow? UI choice.
*/
- public void updateSearchableList() {
+ public void updateSearchableListIfNeeded() {
+ synchronized (this) {
+ if (!mRebuildSearchables) {
+ // The searchable list is valid, no need to rebuild.
+ return;
+ }
+ }
+
// These will become the new values at the end of the method
HashMap<ComponentName, SearchableInfo> newSearchablesMap
= new HashMap<ComponentName, SearchableInfo>();
@@ -229,6 +246,7 @@ public class Searchables {
= new ArrayList<SearchableInfo>();
ArrayList<SearchableInfo> newSearchablesInGlobalSearchList
= new ArrayList<SearchableInfo>();
+ ArraySet<String> newKnownSearchablePackageNames = new ArraySet<>();
// Use intent resolver to generate list of ACTION_SEARCH & ACTION_WEB_SEARCH receivers.
List<ResolveInfo> searchList;
@@ -261,6 +279,7 @@ public class Searchables {
mUserId);
if (searchable != null) {
newSearchablesList.add(searchable);
+ newKnownSearchablePackageNames.add(ai.packageName);
newSearchablesMap.put(searchable.getSearchActivity(), searchable);
if (searchable.shouldIncludeInGlobalSearch()) {
newSearchablesInGlobalSearchList.add(searchable);
@@ -283,16 +302,41 @@ public class Searchables {
synchronized (this) {
mSearchablesMap = newSearchablesMap;
mSearchablesList = newSearchablesList;
+ mKnownSearchablePackageNames = newKnownSearchablePackageNames;
mSearchablesInGlobalSearchList = newSearchablesInGlobalSearchList;
mGlobalSearchActivities = newGlobalSearchActivities;
mCurrentGlobalSearchActivity = newGlobalSearchActivity;
mWebSearchActivity = newWebSearchActivity;
+ for (ResolveInfo globalSearchActivity: mGlobalSearchActivities) {
+ mKnownSearchablePackageNames.add(
+ globalSearchActivity.getComponentInfo().packageName);
+ }
+ if (mCurrentGlobalSearchActivity != null) {
+ mKnownSearchablePackageNames.add(
+ mCurrentGlobalSearchActivity.getPackageName());
+ }
+ if (mWebSearchActivity != null) {
+ mKnownSearchablePackageNames.add(mWebSearchActivity.getPackageName());
+ }
+
+ mRebuildSearchables = false;
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
+ synchronized ArraySet<String> getKnownSearchablePackageNames() {
+ return mKnownSearchablePackageNames;
+ }
+
+ synchronized void invalidateSearchableList() {
+ mRebuildSearchables = true;
+
+ // Don't rebuild the searchable list, it will be rebuilt
+ // when the next updateSearchableList gets called.
+ }
+
/**
* Returns a sorted list of installed search providers as per
* the following heuristics:
@@ -529,6 +573,8 @@ public class Searchables {
pw.print(" "); pw.println(info.getSuggestAuthority());
}
}
+
+ pw.println("mRebuildSearchables = " + mRebuildSearchables);
}
}
}
diff --git a/services/core/java/com/android/server/selinux/OWNERS b/services/core/java/com/android/server/selinux/OWNERS
index 6ca4da2fd740..4cf2066018b5 100644
--- a/services/core/java/com/android/server/selinux/OWNERS
+++ b/services/core/java/com/android/server/selinux/OWNERS
@@ -1,3 +1,4 @@
# Bug component: 1117393
sandrom@google.com
+melisacz@google.com
diff --git a/services/core/java/com/android/server/selinux/SelinuxAuditLogsCollector.java b/services/core/java/com/android/server/selinux/SelinuxAuditLogsCollector.java
index 0219645bee38..03822aaf76b2 100644
--- a/services/core/java/com/android/server/selinux/SelinuxAuditLogsCollector.java
+++ b/services/core/java/com/android/server/selinux/SelinuxAuditLogsCollector.java
@@ -88,7 +88,7 @@ class SelinuxAuditLogsCollector {
if (eventTime.isAfter(latestTimestamp)) {
latestTimestamp = eventTime;
}
- if (eventTime.isBefore(mLastWrite)) {
+ if (eventTime.compareTo(mLastWrite) <= 0) {
continue;
}
Object eventData = event.getData();
diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
index 59766ec7a175..f8c678aa3aa3 100644
--- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
@@ -45,6 +45,11 @@ import static android.hardware.SensorPrivacyManager.Sources.OTHER;
import static android.hardware.SensorPrivacyManager.Sources.QS_TILE;
import static android.hardware.SensorPrivacyManager.Sources.SETTINGS;
import static android.hardware.SensorPrivacyManager.Sources.SHELL;
+import static android.hardware.SensorPrivacyManager.StateTypes.AUTOMOTIVE_DRIVER_ASSISTANCE_APPS;
+import static android.hardware.SensorPrivacyManager.StateTypes.AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS;
+import static android.hardware.SensorPrivacyManager.StateTypes.AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS;
+import static android.hardware.SensorPrivacyManager.StateTypes.DISABLED;
+import static android.hardware.SensorPrivacyManager.StateTypes.ENABLED;
import static android.hardware.SensorPrivacyManager.TOGGLE_TYPE_HARDWARE;
import static android.hardware.SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE;
import static android.os.UserHandle.USER_NULL;
@@ -52,6 +57,9 @@ import static android.service.SensorPrivacyIndividualEnabledSensorProto.UNKNOWN;
import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION;
import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__ACTION_UNKNOWN;
+import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_APPS;
+import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS;
+import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS;
import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__TOGGLE_OFF;
import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__TOGGLE_ON;
import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__SENSOR__CAMERA;
@@ -63,8 +71,11 @@ import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_
import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__SOURCE__SOURCE_UNKNOWN;
import static com.android.internal.util.FrameworkStatsLog.write;
+import android.Manifest;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
@@ -87,6 +98,7 @@ import android.content.pm.PackageManagerInternal;
import android.content.res.Configuration;
import android.database.ContentObserver;
import android.graphics.drawable.Icon;
+import android.hardware.CameraPrivacyAllowlistEntry;
import android.hardware.ISensorPrivacyListener;
import android.hardware.ISensorPrivacyManager;
import android.hardware.SensorPrivacyManager;
@@ -123,6 +135,7 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.camera.flags.Flags;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
@@ -131,6 +144,7 @@ import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.FgThread;
import com.android.server.LocalServices;
+import com.android.server.SystemConfig;
import com.android.server.SystemService;
import com.android.server.pm.UserManagerInternal;
@@ -139,6 +153,7 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
@@ -154,7 +169,24 @@ public final class SensorPrivacyService extends SystemService {
SensorPrivacyService.class.getName() + ".action.disable_sensor_privacy";
public static final int REMINDER_DIALOG_DELAY_MILLIS = 500;
-
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
+ private static final int ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS =
+ PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS;
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
+ private static final int ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS =
+ PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS;
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
+ private static final int ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_APPS =
+ PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_APPS;
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
+ private static final int ACTION__TOGGLE_ON =
+ PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__TOGGLE_ON;
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
+ private static final int ACTION__TOGGLE_OFF =
+ PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__TOGGLE_OFF;
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
+ private static final int ACTION__ACTION_UNKNOWN =
+ PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__ACTION_UNKNOWN;
private final Context mContext;
private final SensorPrivacyServiceImpl mSensorPrivacyServiceImpl;
private final UserManagerInternal mUserManagerInternal;
@@ -176,6 +208,9 @@ public final class SensorPrivacyService extends SystemService {
private CallStateHelper mCallStateHelper;
private KeyguardManager mKeyguardManager;
+ List<CameraPrivacyAllowlistEntry> mCameraPrivacyAllowlist =
+ new ArrayList<CameraPrivacyAllowlistEntry>();
+
private int mCurrentUser = USER_NULL;
public SensorPrivacyService(Context context) {
@@ -192,6 +227,15 @@ public final class SensorPrivacyService extends SystemService {
mPackageManagerInternal = getLocalService(PackageManagerInternal.class);
mNotificationManager = mContext.getSystemService(NotificationManager.class);
mSensorPrivacyServiceImpl = new SensorPrivacyServiceImpl();
+ ArrayMap<String, Boolean> cameraPrivacyAllowlist =
+ SystemConfig.getInstance().getCameraPrivacyAllowlist();
+
+ for (Map.Entry<String, Boolean> entry : cameraPrivacyAllowlist.entrySet()) {
+ CameraPrivacyAllowlistEntry ent = new CameraPrivacyAllowlistEntry();
+ ent.packageName = entry.getKey();
+ ent.isMandatory = entry.getValue();
+ mCameraPrivacyAllowlist.add(ent);
+ }
}
@Override
@@ -324,8 +368,15 @@ public final class SensorPrivacyService extends SystemService {
mHandler, mHandler::handleSensorPrivacyChanged);
mSensorPrivacyStateController.setSensorPrivacyListener(
mHandler,
- (toggleType, userId, sensor, state) -> mHandler.handleSensorPrivacyChanged(
- userId, toggleType, sensor, state.isEnabled()));
+ (toggleType, userId, sensor, state) -> {
+ mHandler.handleSensorPrivacyChanged(
+ userId, toggleType, sensor, state.isEnabled());
+ if (Flags.cameraPrivacyAllowlist()) {
+ mHandler.handleSensorPrivacyChanged(
+ userId, toggleType, sensor, state.getState());
+ }
+ });
+
}
// If sensor privacy is enabled for a sensor, but the device doesn't support sensor privacy
@@ -400,9 +451,15 @@ public final class SensorPrivacyService extends SystemService {
* @param packageName The package name of the app using the sensor
* @param sensor The sensor that is attempting to be used
*/
+ @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
private void onSensorUseStarted(int uid, String packageName, int sensor) {
UserHandle user = UserHandle.of(mCurrentUser);
- if (!isCombinedToggleSensorPrivacyEnabled(sensor)) {
+
+ if (Flags.cameraPrivacyAllowlist() && (sensor == CAMERA) && isAutomotive(mContext)) {
+ if (!isCameraPrivacyEnabled(packageName)) {
+ return;
+ }
+ } else if (!isCombinedToggleSensorPrivacyEnabled(sensor)) {
return;
}
@@ -727,6 +784,12 @@ public final class SensorPrivacyService extends SystemService {
== Configuration.UI_MODE_TYPE_TELEVISION;
}
+ private boolean isAutomotive(Context context) {
+ int uiMode = context.getResources().getConfiguration().uiMode;
+ return (uiMode & Configuration.UI_MODE_TYPE_MASK)
+ == Configuration.UI_MODE_TYPE_CAR;
+ }
+
/**
* Sets the sensor privacy to the provided state and notifies all listeners of the new
* state.
@@ -766,6 +829,225 @@ public final class SensorPrivacyService extends SystemService {
setToggleSensorPrivacyUnchecked(TOGGLE_TYPE_SOFTWARE, userId, source, sensor, enable);
}
+
+ @Override
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
+ @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
+ public void setToggleSensorPrivacyState(int userId, int source, int sensor, int state) {
+ if (DEBUG) {
+ Log.d(TAG, "callingUid=" + Binder.getCallingUid()
+ + " callingPid=" + Binder.getCallingPid()
+ + " setToggleSensorPrivacyState("
+ + "userId=" + userId
+ + " source=" + source
+ + " sensor=" + sensor
+ + " state=" + state
+ + ")");
+ }
+ enforceManageSensorPrivacyPermission();
+ if (userId == UserHandle.USER_CURRENT) {
+ userId = mCurrentUser;
+ }
+
+ if (!canChangeToggleSensorPrivacy(userId, sensor)) {
+ return;
+ }
+ if (!supportsSensorToggle(TOGGLE_TYPE_SOFTWARE, sensor)) {
+ // Do not enable sensor privacy if the device doesn't support it.
+ return;
+ }
+
+ setToggleSensorPrivacyStateUnchecked(TOGGLE_TYPE_SOFTWARE, userId, source, sensor,
+ state);
+ }
+
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
+ private void setToggleSensorPrivacyStateUnchecked(int toggleType, int userId, int source,
+ int sensor, int state) {
+ if (DEBUG) {
+ Log.d(TAG, "callingUid=" + Binder.getCallingUid()
+ + " callingPid=" + Binder.getCallingPid()
+ + " setToggleSensorPrivacyStateUnchecked("
+ + "userId=" + userId
+ + " source=" + source
+ + " sensor=" + sensor
+ + " state=" + state
+ + ")");
+ }
+ long[] lastChange = new long[1];
+ mSensorPrivacyStateController.atomic(() -> {
+ SensorState sensorState = mSensorPrivacyStateController
+ .getState(toggleType, userId, sensor);
+ lastChange[0] = sensorState.getLastChange();
+ mSensorPrivacyStateController.setState(
+ toggleType, userId, sensor, state, mHandler,
+ changeSuccessful -> {
+ if (changeSuccessful) {
+ if (userId == mUserManagerInternal.getProfileParentId(userId)) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ SensorPrivacyServiceImpl::logSensorPrivacyStateToggle,
+ this,
+ source, sensor, state, lastChange[0], false));
+ }
+ }
+ });
+ });
+ }
+
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
+ private void logSensorPrivacyStateToggle(int source, int sensor, int state,
+ long lastChange, boolean onShutDown) {
+ long logMins = Math.max(0, (getCurrentTimeMillis() - lastChange) / (1000 * 60));
+
+ int logAction = ACTION__ACTION_UNKNOWN;
+ if (!onShutDown) {
+ switch(state) {
+ case ENABLED :
+ logAction = ACTION__TOGGLE_OFF;
+ break;
+ case DISABLED :
+ logAction = ACTION__TOGGLE_ON;
+ break;
+ case AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS :
+ logAction = ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS;
+ break;
+ case AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS :
+ logAction = ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS;
+ break;
+ case AUTOMOTIVE_DRIVER_ASSISTANCE_APPS :
+ logAction = ACTION__AUTOMOTIVE_DRIVER_ASSISTANCE_APPS;
+ break;
+ default :
+ logAction = ACTION__ACTION_UNKNOWN;
+ break;
+ }
+ }
+
+ int logSensor = PRIVACY_SENSOR_TOGGLE_INTERACTION__SENSOR__SENSOR_UNKNOWN;
+ switch(sensor) {
+ case CAMERA:
+ logSensor = PRIVACY_SENSOR_TOGGLE_INTERACTION__SENSOR__CAMERA;
+ break;
+ case MICROPHONE:
+ logSensor = PRIVACY_SENSOR_TOGGLE_INTERACTION__SENSOR__MICROPHONE;
+ break;
+ default:
+ logSensor = PRIVACY_SENSOR_TOGGLE_INTERACTION__SENSOR__SENSOR_UNKNOWN;
+ break;
+ }
+
+ int logSource = PRIVACY_SENSOR_TOGGLE_INTERACTION__SOURCE__SOURCE_UNKNOWN;
+ switch(source) {
+ case QS_TILE :
+ logSource = PRIVACY_SENSOR_TOGGLE_INTERACTION__SOURCE__QS_TILE;
+ break;
+ case DIALOG :
+ logSource = PRIVACY_SENSOR_TOGGLE_INTERACTION__SOURCE__DIALOG;
+ break;
+ case SETTINGS:
+ logSource = PRIVACY_SENSOR_TOGGLE_INTERACTION__SOURCE__SETTINGS;
+ break;
+ default:
+ logSource = PRIVACY_SENSOR_TOGGLE_INTERACTION__SOURCE__SOURCE_UNKNOWN;
+ break;
+ }
+
+ if (DEBUG || DEBUG_LOGGING) {
+ Log.d(TAG, "Logging sensor toggle interaction:" + " logSensor=" + logSensor
+ + " logAction=" + logAction + " logSource=" + logSource + " logMins="
+ + logMins);
+ }
+ write(PRIVACY_SENSOR_TOGGLE_INTERACTION, logSensor, logAction, logSource, logMins);
+
+ }
+
+ @Override
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
+ @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
+ public void setToggleSensorPrivacyStateForProfileGroup(int userId, int source, int sensor,
+ int state) {
+ enforceManageSensorPrivacyPermission();
+ if (userId == UserHandle.USER_CURRENT) {
+ userId = mCurrentUser;
+ }
+ int parentId = mUserManagerInternal.getProfileParentId(userId);
+ forAllUsers(userId2 -> {
+ if (parentId == mUserManagerInternal.getProfileParentId(userId2)) {
+ setToggleSensorPrivacyState(userId2, source, sensor, state);
+ }
+ });
+ }
+
+ @Override
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
+ @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ public List<CameraPrivacyAllowlistEntry> getCameraPrivacyAllowlist() {
+ enforceObserveSensorPrivacyPermission();
+ return mCameraPrivacyAllowlist;
+ }
+
+ @Override
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
+ @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ public boolean isCameraPrivacyEnabled(String packageName) {
+ if (DEBUG) {
+ Log.d(TAG, "callingUid=" + Binder.getCallingUid()
+ + " callingPid=" + Binder.getCallingPid()
+ + " isCameraPrivacyEnabled("
+ + "packageName=" + packageName
+ + ")");
+ }
+ enforceObserveSensorPrivacyPermission();
+
+ int state = mSensorPrivacyStateController.getState(TOGGLE_TYPE_SOFTWARE, mCurrentUser,
+ CAMERA).getState();
+ if (state == ENABLED) {
+ return true;
+ } else if (state == DISABLED) {
+ return false;
+ } else if (state == AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS) {
+ for (CameraPrivacyAllowlistEntry entry : mCameraPrivacyAllowlist) {
+ if ((packageName.equals(entry.packageName)) && !entry.isMandatory) {
+ return false;
+ }
+ }
+ return true;
+ } else if (state == AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS) {
+ for (CameraPrivacyAllowlistEntry entry : mCameraPrivacyAllowlist) {
+ if ((packageName.equals(entry.packageName)) && entry.isMandatory) {
+ return false;
+ }
+ }
+ return true;
+ } else if (state == AUTOMOTIVE_DRIVER_ASSISTANCE_APPS) {
+ for (CameraPrivacyAllowlistEntry entry : mCameraPrivacyAllowlist) {
+ if (packageName.equals(entry.packageName)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
+ @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
+ public int getToggleSensorPrivacyState(int toggleType, int sensor) {
+ if (DEBUG) {
+ Log.d(TAG, "callingUid=" + Binder.getCallingUid()
+ + " callingPid=" + Binder.getCallingPid()
+ + " getToggleSensorPrivacyState("
+ + "toggleType=" + toggleType
+ + " sensor=" + sensor
+ + ")");
+ }
+ enforceObserveSensorPrivacyPermission();
+
+ return mSensorPrivacyStateController.getState(toggleType, mCurrentUser, sensor)
+ .getState();
+ }
+
private void setToggleSensorPrivacyUnchecked(int toggleType, int userId, int source,
int sensor, boolean enable) {
if (DEBUG) {
@@ -899,16 +1181,23 @@ public final class SensorPrivacyService extends SystemService {
* Enforces the caller contains the necessary permission to change the state of sensor
* privacy.
*/
+ @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
private void enforceManageSensorPrivacyPermission() {
- enforcePermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY,
- "Changing sensor privacy requires the following permission: "
- + MANAGE_SENSOR_PRIVACY);
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_SENSOR_PRIVACY) == PERMISSION_GRANTED) {
+ return;
+ }
+
+ String message = "Changing sensor privacy requires the following permission: "
+ + MANAGE_SENSOR_PRIVACY;
+ throw new SecurityException(message);
}
/**
* Enforces the caller contains the necessary permission to observe changes to the sate of
* sensor privacy.
*/
+ @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
private void enforceObserveSensorPrivacyPermission() {
String systemUIPackage = mContext.getString(R.string.config_systemUi);
int systemUIAppId = UserHandle.getAppId(mPackageManagerInternal
@@ -917,15 +1206,13 @@ public final class SensorPrivacyService extends SystemService {
// b/221782106, possible race condition with role grant might bootloop device.
return;
}
- enforcePermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY,
- "Observing sensor privacy changes requires the following permission: "
- + android.Manifest.permission.OBSERVE_SENSOR_PRIVACY);
- }
-
- private void enforcePermission(String permission, String message) {
- if (mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) {
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.OBSERVE_SENSOR_PRIVACY) == PERMISSION_GRANTED) {
return;
}
+
+ String message = "Observing sensor privacy changes requires the following permission: "
+ + android.Manifest.permission.OBSERVE_SENSOR_PRIVACY;
throw new SecurityException(message);
}
@@ -1293,11 +1580,13 @@ public final class SensorPrivacyService extends SystemService {
}
@Override
+ @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback,
ResultReceiver resultReceiver) {
(new ShellCommand() {
@Override
+ @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
public int onCommand(String cmd) {
if (cmd == null) {
return handleDefaultCommands(cmd);
@@ -1327,6 +1616,45 @@ public final class SensorPrivacyService extends SystemService {
setToggleSensorPrivacy(userId, SHELL, sensor, false);
}
break;
+ case "automotive_driver_assistance_apps" : {
+ if (Flags.cameraPrivacyAllowlist()) {
+ int sensor = sensorStrToId(getNextArgRequired());
+ if ((!isAutomotive(mContext)) || (sensor != CAMERA)) {
+ pw.println("Command not valid for this sensor");
+ return -1;
+ }
+
+ setToggleSensorPrivacyState(userId, SHELL, sensor,
+ AUTOMOTIVE_DRIVER_ASSISTANCE_APPS);
+ }
+ }
+ break;
+ case "automotive_driver_assistance_helpful_apps" : {
+ if (Flags.cameraPrivacyAllowlist()) {
+ int sensor = sensorStrToId(getNextArgRequired());
+ if ((!isAutomotive(mContext)) || (sensor != CAMERA)) {
+ pw.println("Command not valid for this sensor");
+ return -1;
+ }
+
+ setToggleSensorPrivacyState(userId, SHELL, sensor,
+ AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS);
+ }
+ }
+ break;
+ case "automotive_driver_assistance_required_apps" : {
+ if (Flags.cameraPrivacyAllowlist()) {
+ int sensor = sensorStrToId(getNextArgRequired());
+ if ((!isAutomotive(mContext)) || (sensor != CAMERA)) {
+ pw.println("Command not valid for this sensor");
+ return -1;
+ }
+
+ setToggleSensorPrivacyState(userId, SHELL, sensor,
+ AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS);
+ }
+ }
+ break;
default:
return handleDefaultCommands(cmd);
}
@@ -1349,6 +1677,24 @@ public final class SensorPrivacyService extends SystemService {
pw.println(" disable USER_ID SENSOR");
pw.println(" Disable privacy for a certain sensor.");
pw.println("");
+ if (Flags.cameraPrivacyAllowlist()) {
+ if (isAutomotive(mContext)) {
+ pw.println(" automotive_driver_assistance_apps USER_ID SENSOR");
+ pw.println(" Disable privacy for automotive apps which help you"
+ + " drive and apps which are required by OEM");
+ pw.println("");
+ pw.println(" automotive_driver_assistance_helpful_apps "
+ + "USER_ID SENSOR");
+ pw.println(" Disable privacy for automotive apps which "
+ + "help you drive.");
+ pw.println("");
+ pw.println(" automotive_driver_assistance_required_apps "
+ + "USER_ID SENSOR");
+ pw.println(" Disable privacy for automotive apps which are "
+ + "required by OEM.");
+ pw.println("");
+ }
+ }
}
}).exec(this, in, out, err, args, callback, resultReceiver);
}
@@ -1457,6 +1803,38 @@ public final class SensorPrivacyService extends SystemService {
mSensorPrivacyServiceImpl.showSensorStateChangedActivity(sensor, toggleType);
}
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
+ public void handleSensorPrivacyChanged(int userId, int toggleType, int sensor,
+ int state) {
+ if (userId == mCurrentUser) {
+ mSensorPrivacyServiceImpl.setGlobalRestriction(sensor,
+ mSensorPrivacyServiceImpl.isCombinedToggleSensorPrivacyEnabled(sensor));
+ }
+
+ if (userId != mCurrentUser) {
+ return;
+ }
+ synchronized (mListenerLock) {
+ try {
+ final int count = mToggleSensorListeners.beginBroadcast();
+ for (int i = 0; i < count; i++) {
+ ISensorPrivacyListener listener = mToggleSensorListeners.getBroadcastItem(
+ i);
+ try {
+ listener.onSensorPrivacyStateChanged(toggleType, sensor, state);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Caught an exception notifying listener " + listener + ": ",
+ e);
+ }
+ }
+ } finally {
+ mToggleSensorListeners.finishBroadcast();
+ }
+ }
+
+ mSensorPrivacyServiceImpl.showSensorStateChangedActivity(sensor, toggleType);
+ }
+
public void removeSuppressPackageReminderToken(Pair<Integer, UserHandle> key,
IBinder token) {
sendMessage(PooledLambda.obtainMessage(
diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateController.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateController.java
index 96949589447c..14b13e0d3692 100644
--- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateController.java
+++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateController.java
@@ -16,9 +16,11 @@
package com.android.server.sensorprivacy;
+import android.annotation.FlaggedApi;
import android.os.Handler;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.camera.flags.Flags;
import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.internal.util.function.pooled.PooledLambda;
@@ -51,6 +53,14 @@ abstract class SensorPrivacyStateController {
}
}
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
+ void setState(int toggleType, int userId, int sensor, int state, Handler callbackHandler,
+ SetStateResultCallback callback) {
+ synchronized (mLock) {
+ setStateLocked(toggleType, userId, sensor, state, callbackHandler, callback);
+ }
+ }
+
void setSensorPrivacyListener(Handler handler,
SensorPrivacyListener listener) {
synchronized (mLock) {
@@ -128,6 +138,11 @@ abstract class SensorPrivacyStateController {
Handler callbackHandler, SetStateResultCallback callback);
@GuardedBy("mLock")
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
+ abstract void setStateLocked(int toggleType, int userId, int sensor, int state,
+ Handler callbackHandler, SetStateResultCallback callback);
+
+ @GuardedBy("mLock")
abstract void setSensorPrivacyListenerLocked(Handler handler,
SensorPrivacyListener listener);
diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateControllerImpl.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateControllerImpl.java
index 3dcb4cf996c4..4b491ce30923 100644
--- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateControllerImpl.java
+++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateControllerImpl.java
@@ -16,8 +16,12 @@
package com.android.server.sensorprivacy;
+import static android.hardware.SensorPrivacyManager.StateTypes.DISABLED;
+
+import android.annotation.FlaggedApi;
import android.os.Handler;
+import com.android.internal.camera.flags.Flags;
import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.internal.util.function.pooled.PooledLambda;
@@ -85,6 +89,33 @@ class SensorPrivacyStateControllerImpl extends SensorPrivacyStateController {
sendSetStateCallback(callbackHandler, callback, false);
}
+ @Override
+ @FlaggedApi(Flags.FLAG_CAMERA_PRIVACY_ALLOWLIST)
+ void setStateLocked(int toggleType, int userId, int sensor, int state,
+ Handler callbackHandler, SetStateResultCallback callback) {
+ // Changing the SensorState's mEnabled updates the timestamp of its last change.
+ // A nonexistent state -> unmuted should not set the timestamp.
+ SensorState lastState = mPersistedState.getState(toggleType, userId, sensor);
+ if (lastState == null) {
+ if (state == DISABLED) {
+ sendSetStateCallback(callbackHandler, callback, false);
+ return;
+ } else {
+ SensorState sensorState = new SensorState(state);
+ mPersistedState.setState(toggleType, userId, sensor, sensorState);
+ notifyStateChangeLocked(toggleType, userId, sensor, sensorState);
+ sendSetStateCallback(callbackHandler, callback, true);
+ return;
+ }
+ }
+ if (lastState.setState(state)) {
+ notifyStateChangeLocked(toggleType, userId, sensor, lastState);
+ sendSetStateCallback(callbackHandler, callback, true);
+ return;
+ }
+ sendSetStateCallback(callbackHandler, callback, false);
+ }
+
private void notifyStateChangeLocked(int toggleType, int userId, int sensor,
SensorState sensorState) {
if (mListenerHandler != null && mListener != null) {
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index a4c6959f8ad5..3c6baa873eca 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -255,4 +255,9 @@ public interface StatusBarManagerInternal {
* @param tile the ComponentName of the {@link android.service.quicksettings.TileService}
*/
void removeQsTile(ComponentName tile);
+
+ /**
+ * Called when requested to enter desktop from an app.
+ */
+ void enterDesktop(int displayId);
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index fd316eaf9b96..14c38bde6621 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -830,6 +830,15 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
}
@Override
+ public void enterDesktop(int displayId) {
+ IStatusBar bar = mBar;
+ if (bar != null) {
+ try {
+ bar.enterDesktop(displayId);
+ } catch (RemoteException ex) { }
+ }
+ }
+ @Override
public void showMediaOutputSwitcher(String packageName) {
IStatusBar bar = mBar;
if (bar != null) {
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index d2f6701e313e..4af8c616b2bc 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -65,6 +65,7 @@ import android.content.pm.ParceledListSlice;
import android.content.pm.PathPermission;
import android.content.pm.ProviderInfo;
import android.net.Uri;
+import android.os.BadParcelableException;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -630,16 +631,24 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub implements
if (intent == null) {
return null;
}
- Uri data = intent.getData();
- ClipData clip = intent.getClipData();
- if (data == null && clip == null) {
- return null;
- }
+
// Default userId for uris in the intent (if they don't specify it themselves)
int contentUserHint = intent.getContentUserHint();
if (contentUserHint == UserHandle.USER_CURRENT) {
contentUserHint = UserHandle.getUserId(callingUid);
}
+
+ if (android.security.Flags.contentUriPermissionApis()) {
+ enforceRequireContentUriPermissionFromCallerOnIntentExtraStream(intent, contentUserHint,
+ mode, callingUid, requireContentUriPermissionFromCaller);
+ }
+
+ Uri data = intent.getData();
+ ClipData clip = intent.getClipData();
+ if (data == null && clip == null) {
+ return null;
+ }
+
int targetUid;
if (needed != null) {
targetUid = needed.targetUid;
@@ -733,6 +742,37 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub implements
}
}
+ private void enforceRequireContentUriPermissionFromCallerOnIntentExtraStream(Intent intent,
+ int contentUserHint, int mode, int callingUid,
+ @RequiredContentUriPermission Integer requireContentUriPermissionFromCaller) {
+ try {
+ final Uri uri = intent.getParcelableExtra(Intent.EXTRA_STREAM, Uri.class);
+ if (uri != null) {
+ final GrantUri grantUri = GrantUri.resolve(contentUserHint, uri, mode);
+ enforceRequireContentUriPermissionFromCaller(
+ requireContentUriPermissionFromCaller, grantUri, callingUid);
+ }
+ } catch (BadParcelableException e) {
+ Slog.w(TAG, "Failed to unparcel an URI in EXTRA_STREAM, skipping"
+ + " requireContentUriPermissionFromCaller: " + e);
+ }
+
+ try {
+ final ArrayList<Uri> uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM,
+ Uri.class);
+ if (uris != null) {
+ for (int i = uris.size() - 1; i >= 0; i--) {
+ final GrantUri grantUri = GrantUri.resolve(contentUserHint, uris.get(i), mode);
+ enforceRequireContentUriPermissionFromCaller(
+ requireContentUriPermissionFromCaller, grantUri, callingUid);
+ }
+ }
+ } catch (BadParcelableException e) {
+ Slog.w(TAG, "Failed to unparcel an ArrayList of URIs in EXTRA_STREAM, skipping"
+ + " requireContentUriPermissionFromCaller: " + e);
+ }
+ }
+
@GuardedBy("mLock")
private void readGrantedUriPermissionsLocked() {
if (DEBUG) Slog.v(TAG, "readGrantedUriPermissions()");
diff --git a/services/core/java/com/android/server/utils/EventLogger.java b/services/core/java/com/android/server/utils/EventLogger.java
index 4772bbfe97dd..2e1049b9ea32 100644
--- a/services/core/java/com/android/server/utils/EventLogger.java
+++ b/services/core/java/com/android/server/utils/EventLogger.java
@@ -20,6 +20,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.Log;
+import android.util.Slog;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -84,6 +85,17 @@ public class EventLogger {
enqueue(event.printLog(logType, tag));
}
+ /**
+ * Add a string-based event to the system log, and print it to the log with a specific severity.
+ * @param msg the message to appear in the log
+ * @param logType the log severity (verbose/info/warning/error)
+ * @param tag the tag under which the log entry will appear
+ */
+ public synchronized void enqueueAndSlog(String msg, @Event.LogType int logType, String tag) {
+ final Event event = new StringEvent(msg);
+ enqueue(event.printSlog(logType, tag));
+ }
+
/** Dumps events into the given {@link DumpSink}. */
public synchronized void dump(DumpSink dumpSink) {
dumpSink.sink(mTag, new ArrayList<>(mEvents));
@@ -138,7 +150,7 @@ public class EventLogger {
/**
* Causes the string message for the event to appear in the logcat.
* Here is an example of how to create a new event (a StringEvent), adding it to the logger
- * (an instance of AudioEventLogger) while also making it show in the logcat:
+ * (an instance of EventLogger) while also making it show in the logcat:
* <pre>
* myLogger.log(
* (new StringEvent("something for logcat and logger")).printLog(MyClass.TAG) );
@@ -167,9 +179,9 @@ public class EventLogger {
/**
* Same as {@link #printLog(String)} with a log type
- * @param type one of {@link #ALOGI}, {@link #ALOGE}, {@link #ALOGV}
- * @param tag
- * @return
+ * @param type one of {@link #ALOGI}, {@link #ALOGE}, {@link #ALOGV}, {@link #ALOGW}
+ * @param tag the tag the log entry will be printed under
+ * @return the event itself
*/
public Event printLog(@LogType int type, String tag) {
switch (type) {
@@ -191,6 +203,32 @@ public class EventLogger {
}
/**
+ * Causes the string message for the event to appear in the system log.
+ * @param type one of {@link #ALOGI}, {@link #ALOGE}, {@link #ALOGV}, {@link #ALOGW}
+ * @param tag the tag the log entry will be printed under
+ * @return the event itself
+ * @see #printLog(int, String)
+ */
+ public Event printSlog(@LogType int type, String tag) {
+ switch (type) {
+ case ALOGI:
+ Slog.i(tag, eventToString());
+ break;
+ case ALOGE:
+ Slog.e(tag, eventToString());
+ break;
+ case ALOGW:
+ Slog.w(tag, eventToString());
+ break;
+ case ALOGV:
+ default:
+ Slog.v(tag, eventToString());
+ break;
+ }
+ return this;
+ }
+
+ /**
* Convert event to String.
* This method is only called when the logger history is about to the dumped,
* so this method is where expensive String conversions should be made, not when the Event
diff --git a/services/core/java/com/android/server/utils/TimingsTraceAndSlog.java b/services/core/java/com/android/server/utils/TimingsTraceAndSlog.java
index b45c962811ad..d8dfd9f13d18 100644
--- a/services/core/java/com/android/server/utils/TimingsTraceAndSlog.java
+++ b/services/core/java/com/android/server/utils/TimingsTraceAndSlog.java
@@ -23,6 +23,7 @@ import android.util.TimingsTraceLog;
/**
* Helper class for reporting boot and shutdown timing metrics, also logging to {@link Slog}.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class TimingsTraceAndSlog extends TimingsTraceLog {
/**
diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
index 099c9ae33bfb..17f5e9585c22 100644
--- a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
+++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
@@ -44,6 +44,7 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.internal.telephony.flags.Flags;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
@@ -318,8 +319,12 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
if (SubscriptionManager.isValidSubscriptionId(subId)) {
// Get only configs as needed to save memory.
- final PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(subId,
- VcnManager.VCN_RELATED_CARRIER_CONFIG_KEYS);
+ final PersistableBundle carrierConfig =
+ Flags.fixCrashOnGettingConfigWhenPhoneIsGone()
+ ? CarrierConfigManager.getCarrierConfigSubset(mContext, subId,
+ VcnManager.VCN_RELATED_CARRIER_CONFIG_KEYS)
+ : mCarrierConfigManager.getConfigForSubId(subId,
+ VcnManager.VCN_RELATED_CARRIER_CONFIG_KEYS);
if (mDeps.isConfigForIdentifiedCarrier(carrierConfig)) {
mReadySubIdsBySlotId.put(slotId, subId);
diff --git a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
index 5f4852f77727..a25d67ab66af 100644
--- a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
+++ b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
@@ -252,7 +252,7 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor {
}
getInboundTransformInternal()
- .getIpSecTransformState(
+ .requestIpSecTransformState(
new HandlerExecutor(mHandler), new IpSecTransformStateReceiver());
// Schedule for next poll
@@ -302,7 +302,8 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor {
"packetLossRate: "
+ packetLossRate
+ "% in the past "
- + (state.getTimestamp() - mLastIpSecTransformState.getTimestamp())
+ + (state.getTimestampMillis()
+ - mLastIpSecTransformState.getTimestampMillis())
+ "ms";
mLastIpSecTransformState = state;
diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java b/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
index a79f188713e1..1704aa117a2b 100644
--- a/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
+++ b/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
@@ -138,10 +138,10 @@ public abstract class NetworkMetricMonitor implements AutoCloseable {
}
/** Poll an IpSecTransformState */
- public void getIpSecTransformState(
+ public void requestIpSecTransformState(
@NonNull Executor executor,
@NonNull OutcomeReceiver<IpSecTransformState, RuntimeException> callback) {
- ipSecTransform.getIpSecTransformState(executor, callback);
+ ipSecTransform.requestIpSecTransformState(executor, callback);
}
/** Close this instance and release the underlying resources */
diff --git a/services/core/java/com/android/server/vibrator/HalVibration.java b/services/core/java/com/android/server/vibrator/HalVibration.java
index 743d02d100b1..70e2e27a3bae 100644
--- a/services/core/java/com/android/server/vibrator/HalVibration.java
+++ b/services/core/java/com/android/server/vibrator/HalVibration.java
@@ -102,6 +102,23 @@ final class HalVibration extends Vibration {
}
/**
+ * Resolves the default vibration amplitude of {@link #getEffectToPlay()} and each fallback.
+ *
+ * @param defaultAmplitude An integer in [1,255] representing the device default amplitude to
+ * replace the {@link VibrationEffect#DEFAULT_AMPLITUDE}.
+ */
+ public void resolveEffects(int defaultAmplitude) {
+ CombinedVibration newEffect =
+ mEffectToPlay.transform(VibrationEffect::resolve, defaultAmplitude);
+ if (!Objects.equals(mEffectToPlay, newEffect)) {
+ mEffectToPlay = newEffect;
+ }
+ for (int i = 0; i < mFallbacks.size(); i++) {
+ mFallbacks.setValueAt(i, mFallbacks.valueAt(i).resolve(defaultAmplitude));
+ }
+ }
+
+ /**
* Scales the {@link #getEffectToPlay()} and each fallback effect with a scaling transformation.
*
* @param scaler A {@link VibrationEffect.Transformation<Integer>} that takes one of the
diff --git a/services/core/java/com/android/server/vibrator/TEST_MAPPING b/services/core/java/com/android/server/vibrator/TEST_MAPPING
index 92b327d9abff..c64941bebbf4 100644
--- a/services/core/java/com/android/server/vibrator/TEST_MAPPING
+++ b/services/core/java/com/android/server/vibrator/TEST_MAPPING
@@ -5,6 +5,9 @@
},
{
"path": "cts/tests/vibrator"
+ },
+ {
+ "path": "frameworks/hardware/interfaces/vibrator/aidl"
}
]
}
diff --git a/services/core/java/com/android/server/vibrator/VibrationScaler.java b/services/core/java/com/android/server/vibrator/VibrationScaler.java
index c9805c706fbc..5d17884c769b 100644
--- a/services/core/java/com/android/server/vibrator/VibrationScaler.java
+++ b/services/core/java/com/android/server/vibrator/VibrationScaler.java
@@ -19,7 +19,7 @@ package com.android.server.vibrator;
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.vibrator.V1_0.EffectStrength;
-import android.os.IExternalVibratorService;
+import android.os.ExternalVibrationScale;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
@@ -37,11 +37,13 @@ final class VibrationScaler {
// Scale levels. Each level, except MUTE, is defined as the delta between the current setting
// and the default intensity for that type of vibration (i.e. current - default).
- private static final int SCALE_VERY_LOW = IExternalVibratorService.SCALE_VERY_LOW; // -2
- private static final int SCALE_LOW = IExternalVibratorService.SCALE_LOW; // -1
- private static final int SCALE_NONE = IExternalVibratorService.SCALE_NONE; // 0
- private static final int SCALE_HIGH = IExternalVibratorService.SCALE_HIGH; // 1
- private static final int SCALE_VERY_HIGH = IExternalVibratorService.SCALE_VERY_HIGH; // 2
+ private static final int SCALE_VERY_LOW =
+ ExternalVibrationScale.ScaleLevel.SCALE_VERY_LOW; // -2
+ private static final int SCALE_LOW = ExternalVibrationScale.ScaleLevel.SCALE_LOW; // -1
+ private static final int SCALE_NONE = ExternalVibrationScale.ScaleLevel.SCALE_NONE; // 0
+ private static final int SCALE_HIGH = ExternalVibrationScale.ScaleLevel.SCALE_HIGH; // 1
+ private static final int SCALE_VERY_HIGH =
+ ExternalVibrationScale.ScaleLevel.SCALE_VERY_HIGH; // 2
// Scale factors for each level.
private static final float SCALE_FACTOR_VERY_LOW = 0.6f;
@@ -73,12 +75,19 @@ final class VibrationScaler {
}
/**
+ * Returns the default vibration amplitude configured for this device, value in [1,255].
+ */
+ public int getDefaultVibrationAmplitude() {
+ return mDefaultVibrationAmplitude;
+ }
+
+ /**
* Calculates the scale to be applied to external vibration with given usage.
*
* @param usageHint one of VibrationAttributes.USAGE_*
- * @return one of IExternalVibratorService.SCALE_*
+ * @return one of ExternalVibrationScale.ScaleLevel.SCALE_*
*/
- public int getExternalVibrationScale(int usageHint) {
+ public int getExternalVibrationScaleLevel(int usageHint) {
int defaultIntensity = mSettingsController.getDefaultIntensity(usageHint);
int currentIntensity = mSettingsController.getCurrentIntensity(usageHint);
@@ -100,6 +109,22 @@ final class VibrationScaler {
}
/**
+ * Returns the adaptive haptics scale that should be applied to the vibrations with
+ * the given usage. When no adaptive scales are available for the usages, then returns 1
+ * indicating no scaling will be applied
+ *
+ * @param usageHint one of VibrationAttributes.USAGE_*
+ * @return The adaptive haptics scale.
+ */
+ public float getAdaptiveHapticsScale(int usageHint) {
+ if (shouldApplyAdaptiveHapticsScale(usageHint)) {
+ return mAdaptiveHapticsScales.get(usageHint);
+ }
+
+ return 1f; // no scaling
+ }
+
+ /**
* Scale a {@link VibrationEffect} based on the given usage hint for this vibration.
*
* @param effect the effect to be scaled
@@ -145,11 +170,9 @@ final class VibrationScaler {
}
// If adaptive haptics scaling is available for this usage, apply it to the segment.
- if (Flags.adaptiveHapticsEnabled()
- && mAdaptiveHapticsScales.size() > 0
- && mAdaptiveHapticsScales.contains(usageHint)) {
+ if (shouldApplyAdaptiveHapticsScale(usageHint)) {
float adaptiveScale = mAdaptiveHapticsScales.get(usageHint);
- segment = segment.scale(adaptiveScale);
+ segment = segment.scaleLinearly(adaptiveScale);
}
segments.set(i, segment);
@@ -217,6 +240,10 @@ final class VibrationScaler {
mAdaptiveHapticsScales.clear();
}
+ private boolean shouldApplyAdaptiveHapticsScale(int usageHint) {
+ return Flags.adaptiveHapticsEnabled() && mAdaptiveHapticsScales.contains(usageHint);
+ }
+
/** Mapping of Vibrator.VIBRATION_INTENSITY_* values to {@link EffectStrength}. */
private static int intensityToEffectStrength(int intensity) {
switch (intensity) {
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index fab0430cc40d..99ce3e2fb740 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -31,6 +31,7 @@ import static android.os.VibrationAttributes.USAGE_UNKNOWN;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.SynchronousUserSwitchObserver;
import android.app.UidObserver;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -145,8 +146,6 @@ final class VibrationSettings {
PowerManager.GO_TO_SLEEP_REASON_INATTENTIVE,
PowerManager.GO_TO_SLEEP_REASON_TIMEOUT));
- private static final IntentFilter USER_SWITCHED_INTENT_FILTER =
- new IntentFilter(Intent.ACTION_USER_SWITCHED);
private static final IntentFilter INTERNAL_RINGER_MODE_CHANGED_INTENT_FILTER =
new IntentFilter(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
@@ -162,9 +161,11 @@ final class VibrationSettings {
@VisibleForTesting
final SettingsContentObserver mSettingObserver;
@VisibleForTesting
- final MyUidObserver mUidObserver;
- @VisibleForTesting
final SettingsBroadcastReceiver mSettingChangeReceiver;
+ @VisibleForTesting
+ final VibrationUidObserver mUidObserver;
+ @VisibleForTesting
+ final VibrationUserSwitchObserver mUserSwitchObserver;
@GuardedBy("mLock")
private final List<OnVibratorSettingsChanged> mListeners = new ArrayList<>();
@@ -205,8 +206,9 @@ final class VibrationSettings {
mContext = context;
mVibrationConfig = config;
mSettingObserver = new SettingsContentObserver(handler);
- mUidObserver = new MyUidObserver();
mSettingChangeReceiver = new SettingsBroadcastReceiver();
+ mUidObserver = new VibrationUidObserver();
+ mUserSwitchObserver = new VibrationUserSwitchObserver();
mSystemUiPackage = LocalServices.getService(PackageManagerInternal.class)
.getSystemUiServiceComponent().getPackageName();
@@ -245,7 +247,13 @@ final class VibrationSettings {
try {
ActivityManager.getService().registerUidObserver(mUidObserver,
ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE,
- ActivityManager.PROCESS_STATE_UNKNOWN, null);
+ ActivityManager.PROCESS_STATE_UNKNOWN, /* callingPackage= */ null);
+ } catch (RemoteException e) {
+ // ignored; both services live in system_server
+ }
+
+ try {
+ ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG);
} catch (RemoteException e) {
// ignored; both services live in system_server
}
@@ -270,7 +278,6 @@ final class VibrationSettings {
}
});
- registerSettingsChangeReceiver(USER_SWITCHED_INTENT_FILTER);
registerSettingsChangeReceiver(INTERNAL_RINGER_MODE_CHANGED_INTENT_FILTER);
// Listen to all settings that might affect the result of Vibrator.getVibrationIntensity.
@@ -540,41 +547,44 @@ final class VibrationSettings {
/** Update all cached settings and triggers registered listeners. */
void update() {
- updateSettings();
+ updateSettings(UserHandle.USER_CURRENT);
updateRingerMode();
notifyListeners();
}
- private void updateSettings() {
+ private void updateSettings(int userHandle) {
synchronized (mLock) {
- mVibrateInputDevices = loadSystemSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0) > 0;
- mVibrateOn = loadSystemSetting(Settings.System.VIBRATE_ON, 1) > 0;
+ mVibrateInputDevices =
+ loadSystemSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0, userHandle) > 0;
+ mVibrateOn = loadSystemSetting(Settings.System.VIBRATE_ON, 1, userHandle) > 0;
mKeyboardVibrationOn = loadSystemSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED,
- mVibrationConfig.isDefaultKeyboardVibrationEnabled() ? 1 : 0) > 0;
+ mVibrationConfig.isDefaultKeyboardVibrationEnabled() ? 1 : 0, userHandle) > 0;
int alarmIntensity = toIntensity(
- loadSystemSetting(Settings.System.ALARM_VIBRATION_INTENSITY, -1),
+ loadSystemSetting(Settings.System.ALARM_VIBRATION_INTENSITY, -1, userHandle),
getDefaultIntensity(USAGE_ALARM));
int defaultHapticFeedbackIntensity = getDefaultIntensity(USAGE_TOUCH);
int hapticFeedbackIntensity = toIntensity(
- loadSystemSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, -1),
+ loadSystemSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, -1, userHandle),
defaultHapticFeedbackIntensity);
int positiveHapticFeedbackIntensity = toPositiveIntensity(
hapticFeedbackIntensity, defaultHapticFeedbackIntensity);
int hardwareFeedbackIntensity = toIntensity(
- loadSystemSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, -1),
+ loadSystemSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, -1,
+ userHandle),
positiveHapticFeedbackIntensity);
int mediaIntensity = toIntensity(
- loadSystemSetting(Settings.System.MEDIA_VIBRATION_INTENSITY, -1),
+ loadSystemSetting(Settings.System.MEDIA_VIBRATION_INTENSITY, -1, userHandle),
getDefaultIntensity(USAGE_MEDIA));
int defaultNotificationIntensity = getDefaultIntensity(USAGE_NOTIFICATION);
int notificationIntensity = toIntensity(
- loadSystemSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, -1),
+ loadSystemSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, -1,
+ userHandle),
defaultNotificationIntensity);
int positiveNotificationIntensity = toPositiveIntensity(
notificationIntensity, defaultNotificationIntensity);
int ringIntensity = toIntensity(
- loadSystemSetting(Settings.System.RING_VIBRATION_INTENSITY, -1),
+ loadSystemSetting(Settings.System.RING_VIBRATION_INTENSITY, -1, userHandle),
getDefaultIntensity(USAGE_RINGTONE));
mCurrentVibrationIntensities.clear();
@@ -593,7 +603,7 @@ final class VibrationSettings {
mCurrentVibrationIntensities.put(USAGE_HARDWARE_FEEDBACK, hardwareFeedbackIntensity);
mCurrentVibrationIntensities.put(USAGE_PHYSICAL_EMULATION, hardwareFeedbackIntensity);
- if (!loadBooleanSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED)) {
+ if (!loadBooleanSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, userHandle)) {
// Make sure deprecated boolean setting still disables touch vibrations.
mCurrentVibrationIntensities.put(USAGE_TOUCH, Vibrator.VIBRATION_INTENSITY_OFF);
} else {
@@ -744,14 +754,13 @@ final class VibrationSettings {
return value;
}
- private boolean loadBooleanSetting(String settingKey) {
- return Settings.System.getIntForUser(mContext.getContentResolver(),
- settingKey, 0, UserHandle.USER_CURRENT) != 0;
+ private boolean loadBooleanSetting(String settingKey, int userHandle) {
+ return loadSystemSetting(settingKey, 0, userHandle) != 0;
}
- private int loadSystemSetting(String settingName, int defaultValue) {
+ private int loadSystemSetting(String settingName, int defaultValue, int userHandle) {
return Settings.System.getIntForUser(mContext.getContentResolver(),
- settingName, defaultValue, UserHandle.USER_CURRENT);
+ settingName, defaultValue, userHandle);
}
private void registerSettingsObserver(Uri settingUri) {
@@ -828,24 +837,18 @@ final class VibrationSettings {
@Override
public void onChange(boolean selfChange) {
- updateSettings();
+ updateSettings(UserHandle.USER_CURRENT);
notifyListeners();
}
}
- /**
- * Implementation of {@link BroadcastReceiver} to update settings on current user or ringer
- * mode change.
- */
+ /** Implementation of {@link BroadcastReceiver} to update on ringer mode change. */
@VisibleForTesting
final class SettingsBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
- if (Intent.ACTION_USER_SWITCHED.equals(action)) {
- // Reload all settings, as they are user-based.
- update();
- } else if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) {
+ if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) {
updateRingerMode();
notifyListeners();
}
@@ -854,7 +857,7 @@ final class VibrationSettings {
/** Implementation of {@link ContentObserver} to be registered to a setting {@link Uri}. */
@VisibleForTesting
- final class MyUidObserver extends UidObserver {
+ final class VibrationUidObserver extends UidObserver {
private final SparseArray<Integer> mProcStatesCache = new SparseArray<>();
public boolean isUidForeground(int uid) {
@@ -878,4 +881,23 @@ final class VibrationSettings {
}
}
}
+
+ /** Implementation of {@link SynchronousUserSwitchObserver} to update on user switch. */
+ @VisibleForTesting
+ final class VibrationUserSwitchObserver extends SynchronousUserSwitchObserver {
+
+ @Override
+ public void onUserSwitching(int newUserId) {
+ // Reload settings early based on new user id.
+ updateSettings(newUserId);
+ notifyListeners();
+ }
+
+ @Override
+ public void onUserSwitchComplete(int newUserId) {
+ // Reload all settings including ones from AudioManager,
+ // as they are based on UserHandle.USER_CURRENT.
+ update();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
index 9cf942e91439..f6af9ad991ff 100644
--- a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
+++ b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
@@ -160,7 +160,10 @@ final class VibrationStepConductor implements IBinder.DeathRecipient {
if (Flags.adaptiveHapticsEnabled()) {
waitForVibrationParamsIfRequired();
}
+ // Scale resolves the default amplitudes from the effect before scaling them.
mVibration.scaleEffects(mVibrationScaler::scale);
+ } else {
+ mVibration.resolveEffects(mVibrationScaler.getDefaultVibrationAmplitude());
}
mVibration.adaptToDevice(mDeviceAdapter);
diff --git a/services/core/java/com/android/server/vibrator/VibratorFrameworkStatsLogger.java b/services/core/java/com/android/server/vibrator/VibratorFrameworkStatsLogger.java
index f600a2964cbc..7e601b64ad18 100644
--- a/services/core/java/com/android/server/vibrator/VibratorFrameworkStatsLogger.java
+++ b/services/core/java/com/android/server/vibrator/VibratorFrameworkStatsLogger.java
@@ -19,10 +19,12 @@ package com.android.server.vibrator;
import android.os.Handler;
import android.os.SystemClock;
import android.util.Slog;
+import android.view.HapticFeedbackConstants;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.modules.expresslog.Counter;
import java.util.ArrayDeque;
import java.util.Queue;
@@ -137,4 +139,21 @@ public class VibratorFrameworkStatsLogger {
mVibrationReportedLogIntervalMillis);
}
}
+
+ /** Logs only if the haptics feedback effect is one of the KEYBOARD_ constants. */
+ public static void logPerformHapticsFeedbackIfKeyboard(int uid, int hapticsFeedbackEffect) {
+ boolean isKeyboard;
+ switch (hapticsFeedbackEffect) {
+ case HapticFeedbackConstants.KEYBOARD_TAP:
+ case HapticFeedbackConstants.KEYBOARD_RELEASE:
+ isKeyboard = true;
+ break;
+ default:
+ isKeyboard = false;
+ break;
+ }
+ if (isKeyboard) {
+ Counter.logIncrementWithUid("vibrator.value_perform_haptic_feedback_keyboard", uid);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 759450b528b9..78e0ebbb53fa 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.vibrator;
+import static android.os.ExternalVibrationScale.ScaleLevel.SCALE_MUTE;
import static android.os.VibrationEffect.VibrationParameter.targetAmplitude;
import static android.os.VibrationEffect.VibrationParameter.targetFrequency;
@@ -35,6 +36,7 @@ import android.os.Binder;
import android.os.Build;
import android.os.CombinedVibration;
import android.os.ExternalVibration;
+import android.os.ExternalVibrationScale;
import android.os.Handler;
import android.os.IBinder;
import android.os.IExternalVibratorService;
@@ -277,7 +279,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
context.registerReceiver(mIntentReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
injector.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService());
- if (ServiceManager.isDeclared(VIBRATOR_CONTROL_SERVICE)) {
+ if (injector.isServiceDeclared(VIBRATOR_CONTROL_SERVICE)) {
injector.addService(VIBRATOR_CONTROL_SERVICE, mVibratorControlService);
}
@@ -448,6 +450,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
VibrationAttributes attrs =
hapticVibrationProvider.getVibrationAttributesForHapticFeedback(
constant, /* bypassVibrationIntensitySetting= */ always);
+ VibratorFrameworkStatsLogger.logPerformHapticsFeedbackIfKeyboard(uid, constant);
return vibrateWithoutPermissionCheck(uid, deviceId, opPkg, combinedVibration, attrs,
"performHapticFeedback: " + reason, token);
}
@@ -883,8 +886,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
private Vibration.EndInfo startVibrationOnInputDevicesLocked(HalVibration vib) {
if (!vib.callerInfo.attrs.isFlagSet(
VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE)) {
- // Scale effect before dispatching it to the input devices.
+ // Scale resolves the default amplitudes from the effect before scaling them.
vib.scaleEffects(mVibrationScaler::scale);
+ } else {
+ vib.resolveEffects(mVibrationScaler.getDefaultVibrationAmplitude());
}
mInputDeviceDelegate.vibrateIfAvailable(vib.callerInfo, vib.getEffectToPlay());
@@ -1424,6 +1429,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
VibratorControllerHolder createVibratorControllerHolder() {
return new VibratorControllerHolder();
}
+
+ boolean isServiceDeclared(String name) {
+ return ServiceManager.isDeclared(name);
+ }
}
/**
@@ -1591,7 +1600,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
IBinder.DeathRecipient {
public final ExternalVibration externalVibration;
- public int scale;
+ public ExternalVibrationScale scale = new ExternalVibrationScale();
private Vibration.Status mStatus;
@@ -1602,7 +1611,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
// instead of using DEVICE_ID_INVALID here and relying on the UID checks.
Context.DEVICE_ID_INVALID, externalVibration.getPackage(), null));
this.externalVibration = externalVibration;
- this.scale = IExternalVibratorService.SCALE_NONE;
mStatus = Vibration.Status.RUNNING;
}
@@ -1655,7 +1663,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
public Vibration.DebugInfo getDebugInfo() {
return new Vibration.DebugInfo(mStatus, stats, /* playedEffect= */ null,
- /* originalEffect= */ null, scale, callerInfo);
+ /* originalEffect= */ null, scale.scaleLevel, callerInfo);
}
public VibrationStats.StatsInfo getStatsInfo(long completionUptimeMillis) {
@@ -1985,11 +1993,17 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
/** Implementation of {@link IExternalVibratorService} to be triggered on external control. */
@VisibleForTesting
final class ExternalVibratorService extends IExternalVibratorService.Stub {
+ private static final ExternalVibrationScale SCALE_MUTE = new ExternalVibrationScale();
+
+ static {
+ SCALE_MUTE.scaleLevel = ExternalVibrationScale.ScaleLevel.SCALE_MUTE;
+ }
@Override
- public int onExternalVibrationStart(ExternalVibration vib) {
+ public ExternalVibrationScale onExternalVibrationStart(ExternalVibration vib) {
+
if (!hasExternalControlCapability()) {
- return IExternalVibratorService.SCALE_MUTE;
+ return SCALE_MUTE;
}
if (ActivityManager.checkComponentPermission(android.Manifest.permission.VIBRATE,
vib.getUid(), -1 /*owningUid*/, true /*exported*/)
@@ -1997,7 +2011,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
Slog.w(TAG, "pkg=" + vib.getPackage() + ", uid=" + vib.getUid()
+ " tried to play externally controlled vibration"
+ " without VIBRATE permission, ignoring.");
- return IExternalVibratorService.SCALE_MUTE;
+ return SCALE_MUTE;
}
// Create Vibration.Stats as close to the received request as possible, for tracking.
@@ -2030,7 +2044,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
if (vibrationEndInfo != null) {
- vibHolder.scale = IExternalVibratorService.SCALE_MUTE;
+ vibHolder.scale = SCALE_MUTE;
// Failed to start the vibration, end it and report metrics right away.
endVibrationAndWriteStatsLocked(vibHolder, vibrationEndInfo);
return vibHolder.scale;
@@ -2071,7 +2085,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
mCurrentExternalVibration = vibHolder;
vibHolder.linkToDeath();
- vibHolder.scale = mVibrationScaler.getExternalVibrationScale(attrs.getUsage());
+ vibHolder.scale.scaleLevel = mVibrationScaler.getExternalVibrationScaleLevel(
+ attrs.getUsage());
+ vibHolder.scale.adaptiveHapticsScale = mVibrationScaler.getAdaptiveHapticsScale(
+ attrs.getUsage());
}
if (waitForCompletion) {
@@ -2083,7 +2100,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
new Vibration.EndInfo(Vibration.Status.IGNORED_ERROR_CANCELLING),
/* continueExternalControl= */ false);
}
- return IExternalVibratorService.SCALE_MUTE;
+ return SCALE_MUTE;
}
}
if (!alreadyUnderExternalControl) {
@@ -2220,9 +2237,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
// only cancel background vibrations.
IBinder deathBinder = commonOptions.background ? VibratorManagerService.this
: mShellCallbacksToken;
- HalVibration vib = vibrateWithPermissionCheck(Binder.getCallingUid(),
- Context.DEVICE_ID_DEFAULT, SHELL_PACKAGE_NAME, combined, attrs,
- commonOptions.description, deathBinder);
+ int uid = Binder.getCallingUid();
+ // Resolve the package name for the client based on the process UID, to cover cases like
+ // rooted shell clients using ROOT_UID.
+ String resolvedPackageName = AppOpsManager.resolvePackageName(uid, SHELL_PACKAGE_NAME);
+ HalVibration vib = vibrateWithPermissionCheck(uid, Context.DEVICE_ID_DEFAULT,
+ resolvedPackageName, combined, attrs, commonOptions.description, deathBinder);
maybeWaitOnVibration(vib, commonOptions);
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 118985a729aa..c3efcb14f223 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -52,6 +52,7 @@ import android.app.ApplicationExitInfo;
import android.app.ILocalWallpaperColorConsumer;
import android.app.IWallpaperManager;
import android.app.IWallpaperManagerCallback;
+import android.app.KeyguardManager;
import android.app.PendingIntent;
import android.app.UidObserver;
import android.app.UserSwitchObserver;
@@ -340,8 +341,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
// If this was the system wallpaper, rebind...
wallpaper.mBindSource = BindSource.SET_STATIC;
- bindWallpaperComponentLocked(mImageWallpaper, true, false, wallpaper,
- callback);
+ bindWallpaperComponentLocked(mImageWallpaper, true, false, wallpaper, callback);
}
if (lockWallpaperChanged) {
@@ -368,11 +368,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
if (DEBUG) {
Slog.v(TAG, "Lock screen wallpaper changed to same as home");
}
- final WallpaperData lockedWallpaper = mLockWallpaperMap.get(
- mWallpaper.userId);
- if (lockedWallpaper != null) {
- detachWallpaperLocked(lockedWallpaper);
- }
+ detachWallpaperLocked(mLockWallpaperMap.get(mWallpaper.userId));
clearWallpaperBitmaps(mWallpaper.userId, FLAG_LOCK);
mLockWallpaperMap.remove(wallpaper.userId);
}
@@ -1696,7 +1692,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
sWallpaperType.forEach((type, filename) -> {
final File record = new File(getWallpaperDir(userID), filename);
if (record.exists()) {
- Slog.w(TAG, "User:" + userID + ", wallpaper tyep = " + type
+ Slog.w(TAG, "User:" + userID + ", wallpaper type = " + type
+ ", wallpaper fail detect!! reset to default wallpaper");
clearWallpaperBitmaps(userID, type);
record.delete();
@@ -1792,10 +1788,23 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
systemWallpaper.wallpaperObserver = new WallpaperObserver(systemWallpaper);
systemWallpaper.wallpaperObserver.startWatching();
}
- if (lockWallpaper != systemWallpaper) {
- switchWallpaper(lockWallpaper, null);
+ if (Flags.reorderWallpaperDuringUserSwitch()) {
+ detachWallpaperLocked(mLastLockWallpaper);
+ detachWallpaperLocked(mLastWallpaper);
+ if (lockWallpaper == systemWallpaper) {
+ switchWallpaper(systemWallpaper, reply);
+ } else {
+ KeyguardManager km = mContext.getSystemService(KeyguardManager.class);
+ boolean isDeviceSecure = km != null && km.isDeviceSecure(userId);
+ switchWallpaper(isDeviceSecure ? lockWallpaper : systemWallpaper, reply);
+ switchWallpaper(isDeviceSecure ? systemWallpaper : lockWallpaper, null);
+ }
+ } else {
+ if (lockWallpaper != systemWallpaper) {
+ switchWallpaper(lockWallpaper, null);
+ }
+ switchWallpaper(systemWallpaper, reply);
}
- switchWallpaper(systemWallpaper, reply);
mInitialUserSwitch = false;
}
@@ -2219,8 +2228,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
if (wallpaper == null || !wallpaper.mSupportsMultiCrop) return null;
SparseArray<Rect> relativeSuggestedCrops =
mWallpaperCropper.getRelativeCropHints(wallpaper);
- Point croppedBitmapSize =
- new Point(wallpaper.cropHint.width(), wallpaper.cropHint.height());
+ Point croppedBitmapSize = new Point(
+ (int) (0.5f + wallpaper.cropHint.width() / wallpaper.mSampleSize),
+ (int) (0.5f + wallpaper.cropHint.height() / wallpaper.mSampleSize));
SparseArray<Rect> relativeDefaultCrops =
mWallpaperCropper.getDefaultCrops(relativeSuggestedCrops, croppedBitmapSize);
SparseArray<Rect> adjustedRelativeSuggestedCrops = new SparseArray<>();
@@ -3385,10 +3395,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
boolean homeUpdated = (newWallpaper.mWhich & FLAG_SYSTEM) != 0;
boolean lockUpdated = (newWallpaper.mWhich & FLAG_LOCK) != 0;
boolean systemWillBecomeLock = newWallpaper.mSystemWasBoth && !lockUpdated;
- if (mLastWallpaper != null && homeUpdated && !systemWillBecomeLock) {
+ if (homeUpdated && !systemWillBecomeLock) {
detachWallpaperLocked(mLastWallpaper);
}
- if (mLastLockWallpaper != null && lockUpdated) {
+ if (lockUpdated) {
detachWallpaperLocked(mLastLockWallpaper);
}
}
@@ -3396,7 +3406,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
// Frees up all rendering resources used by the given wallpaper so that the WallpaperData object
// can be reused: detaches Engine, unbinds WallpaperService, etc.
private void detachWallpaperLocked(WallpaperData wallpaper) {
- if (wallpaper.connection != null) {
+ if (wallpaper != null && wallpaper.connection != null) {
if (DEBUG) {
Slog.v(TAG, "Detaching wallpaper: " + wallpaper);
}
diff --git a/services/core/java/com/android/server/wearable/RemoteWearableSensingService.java b/services/core/java/com/android/server/wearable/RemoteWearableSensingService.java
index 62a637ebde13..e230b95fe907 100644
--- a/services/core/java/com/android/server/wearable/RemoteWearableSensingService.java
+++ b/services/core/java/com/android/server/wearable/RemoteWearableSensingService.java
@@ -43,16 +43,16 @@ final class RemoteWearableSensingService extends ServiceConnector.Impl<IWearable
com.android.server.wearable.RemoteWearableSensingService.class.getSimpleName();
private final static boolean DEBUG = false;
- private final Object mSecureWearableConnectionLock = new Object();
+ private final Object mSecureConnectionLock = new Object();
- // mNextSecureWearableConnectionContext will only be non-null when we are waiting for the
+ // mNextSecureConnectionContext will only be non-null when we are waiting for the
// WearableSensingService process to restart. It will be set to null after it is passed into
// WearableSensingService.
- @GuardedBy("mSecureWearableConnectionLock")
- private SecureWearableConnectionContext mNextSecureWearableConnectionContext;
+ @GuardedBy("mSecureConnectionLock")
+ private SecureWearableConnectionContext mNextSecureConnectionContext;
- @GuardedBy("mSecureWearableConnectionLock")
- private boolean mSecureWearableConnectionProvided = false;
+ @GuardedBy("mSecureConnectionLock")
+ private boolean mSecureConnectionProvided = false;
RemoteWearableSensingService(Context context, ComponentName serviceName,
int userId) {
@@ -77,23 +77,23 @@ final class RemoteWearableSensingService extends ServiceConnector.Impl<IWearable
* @param secureWearableConnection The secure connection to the wearable
* @param callback The callback for service status
*/
- public void provideSecureWearableConnection(
+ public void provideSecureConnection(
ParcelFileDescriptor secureWearableConnection, RemoteCallback callback) {
if (DEBUG) {
- Slog.i(TAG, "#provideSecureWearableConnection");
+ Slog.i(TAG, "#provideSecureConnection");
}
if (!Flags.enableRestartWssProcess()) {
Slog.d(
TAG,
"FLAG_ENABLE_RESTART_WSS_PROCESS is disabled. Do not attempt to restart the"
+ " WearableSensingService process");
- provideSecureWearableConnectionInternal(secureWearableConnection, callback);
+ provideSecureConnectionInternal(secureWearableConnection, callback);
return;
}
- synchronized (mSecureWearableConnectionLock) {
- if (mNextSecureWearableConnectionContext != null) {
+ synchronized (mSecureConnectionLock) {
+ if (mNextSecureConnectionContext != null) {
// A process restart is in progress, #binderDied is about to be called. Replace
- // the previous mNextSecureWearableConnectionContext with the current one
+ // the previous mNextSecureConnectionContext with the current one
Slog.i(
TAG,
"A new wearable connection is provided before the process restart triggered"
@@ -101,33 +101,33 @@ final class RemoteWearableSensingService extends ServiceConnector.Impl<IWearable
+ " connection.");
if (Flags.enableProvideWearableConnectionApi()) {
WearableSensingManagerPerUserService.notifyStatusCallback(
- mNextSecureWearableConnectionContext.mStatusCallback,
+ mNextSecureConnectionContext.mStatusCallback,
WearableSensingManager.STATUS_CHANNEL_ERROR);
}
- mNextSecureWearableConnectionContext =
+ mNextSecureConnectionContext =
new SecureWearableConnectionContext(secureWearableConnection, callback);
return;
}
- if (!mSecureWearableConnectionProvided) {
+ if (!mSecureConnectionProvided) {
// no need to kill the process
- provideSecureWearableConnectionInternal(secureWearableConnection, callback);
- mSecureWearableConnectionProvided = true;
+ provideSecureConnectionInternal(secureWearableConnection, callback);
+ mSecureConnectionProvided = true;
return;
}
- mNextSecureWearableConnectionContext =
+ mNextSecureConnectionContext =
new SecureWearableConnectionContext(secureWearableConnection, callback);
// Killing the process causes the binder to die. #binderDied will then be triggered
killWearableSensingServiceProcess();
}
}
- private void provideSecureWearableConnectionInternal(
+ private void provideSecureConnectionInternal(
ParcelFileDescriptor secureWearableConnection, RemoteCallback callback) {
Slog.d(TAG, "Providing secure wearable connection.");
var unused =
post(
service -> {
- service.provideSecureWearableConnection(
+ service.provideSecureConnection(
secureWearableConnection, callback);
try {
// close the local fd after it has been sent to the WSS process
@@ -141,15 +141,15 @@ final class RemoteWearableSensingService extends ServiceConnector.Impl<IWearable
@Override
public void binderDied() {
super.binderDied();
- synchronized (mSecureWearableConnectionLock) {
- if (mNextSecureWearableConnectionContext != null) {
+ synchronized (mSecureConnectionLock) {
+ if (mNextSecureConnectionContext != null) {
// This will call #post, which will recreate the process and bind to it
- provideSecureWearableConnectionInternal(
- mNextSecureWearableConnectionContext.mSecureWearableConnection,
- mNextSecureWearableConnectionContext.mStatusCallback);
- mNextSecureWearableConnectionContext = null;
+ provideSecureConnectionInternal(
+ mNextSecureConnectionContext.mSecureConnection,
+ mNextSecureConnectionContext.mStatusCallback);
+ mNextSecureConnectionContext = null;
} else {
- mSecureWearableConnectionProvided = false;
+ mSecureConnectionProvided = false;
Slog.w(TAG, "Binder died but there is no secure wearable connection to provide.");
}
}
@@ -257,13 +257,62 @@ final class RemoteWearableSensingService extends ServiceConnector.Impl<IWearable
statusCallback));
}
+ /**
+ * Request the wearable to start hotword recognition.
+ *
+ * @param wearableHotwordCallback The callback to send hotword audio data and format to.
+ * @param statusCallback The callback for service status.
+ */
+ public void startHotwordRecognition(
+ RemoteCallback wearableHotwordCallback, RemoteCallback statusCallback) {
+ if (DEBUG) {
+ Slog.i(TAG, "Starting to listen for hotword.");
+ }
+ var unused =
+ post(
+ service ->
+ service.startHotwordRecognition(
+ wearableHotwordCallback, statusCallback));
+ }
+
+ /**
+ * Request the wearable to stop hotword recognition.
+ *
+ * @param statusCallback The callback for service status.
+ */
+ public void stopHotwordRecognition(RemoteCallback statusCallback) {
+ if (DEBUG) {
+ Slog.i(TAG, "Stopping hotword recognition.");
+ }
+ var unused = post(service -> service.stopHotwordRecognition(statusCallback));
+ }
+
+ /**
+ * Signals to the {@link WearableSensingService} that hotword audio data is accepted by the
+ * {@link android.service.voice.HotwordDetectionService} as valid hotword.
+ */
+ public void onValidatedByHotwordDetectionService() {
+ if (DEBUG) {
+ Slog.i(TAG, "Requesting hotword audio data egress.");
+ }
+ var unused = post(service -> service.onValidatedByHotwordDetectionService());
+ }
+
+ /** Stops the active hotword audio stream from the wearable. */
+ public void stopActiveHotwordAudio() {
+ if (DEBUG) {
+ Slog.i(TAG, "Stopping hotword audio.");
+ }
+ var unused = post(service -> service.stopActiveHotwordAudio());
+ }
+
private static class SecureWearableConnectionContext {
- final ParcelFileDescriptor mSecureWearableConnection;
+ final ParcelFileDescriptor mSecureConnection;
final RemoteCallback mStatusCallback;
SecureWearableConnectionContext(
ParcelFileDescriptor secureWearableConnection, RemoteCallback statusCallback) {
- this.mSecureWearableConnection = secureWearableConnection;
+ this.mSecureConnection = secureWearableConnection;
this.mStatusCallback = statusCallback;
}
}
diff --git a/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java b/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java
index 9ba4433523ee..34b9fe968994 100644
--- a/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java
+++ b/services/core/java/com/android/server/wearable/WearableSensingManagerPerUserService.java
@@ -16,6 +16,8 @@
package com.android.server.wearable;
+import static android.service.wearable.WearableSensingService.HOTWORD_AUDIO_STREAM_BUNDLE_KEY;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -28,18 +30,23 @@ import android.companion.CompanionDeviceManager;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
+import android.os.Binder;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.SharedMemory;
+import android.service.voice.HotwordAudioStream;
+import android.service.voice.VoiceInteractionManagerInternal;
+import android.service.voice.VoiceInteractionManagerInternal.WearableHotwordDetectionCallback;
import android.system.OsConstants;
import android.util.IndentingPrintWriter;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
import com.android.server.infra.AbstractPerUserSystemService;
import java.io.IOException;
@@ -58,6 +65,7 @@ final class WearableSensingManagerPerUserService extends
@VisibleForTesting
RemoteWearableSensingService mRemoteService;
+ @Nullable private VoiceInteractionManagerInternal mVoiceInteractionManagerInternal;
private ComponentName mComponentName;
private final Object mSecureChannelLock = new Object();
@@ -99,6 +107,15 @@ final class WearableSensingManagerPerUserService extends
}
}
+ @GuardedBy("mLock")
+ private boolean ensureVoiceInteractionManagerInternalInitiated() {
+ if (mVoiceInteractionManagerInternal == null) {
+ mVoiceInteractionManagerInternal =
+ LocalServices.getService(VoiceInteractionManagerInternal.class);
+ }
+ return mVoiceInteractionManagerInternal != null;
+ }
+
/**
* get the currently bound component name.
*/
@@ -172,9 +189,9 @@ final class WearableSensingManagerPerUserService extends
* Creates a CompanionDeviceManager secure channel and sends a proxy to the wearable sensing
* service.
*/
- public void onProvideWearableConnection(
+ public void onProvideConnection(
ParcelFileDescriptor wearableConnection, RemoteCallback callback) {
- Slog.i(TAG, "onProvideWearableConnection in per user service.");
+ Slog.i(TAG, "onProvideConnection in per user service.");
synchronized (mLock) {
if (!setUpServiceIfNeeded()) {
Slog.w(TAG, "Detection service is not available at this moment.");
@@ -200,7 +217,7 @@ final class WearableSensingManagerPerUserService extends
Slog.i(TAG, "calling over to remote service.");
synchronized (mLock) {
ensureRemoteServiceInitiated();
- mRemoteService.provideSecureWearableConnection(
+ mRemoteService.provideSecureConnection(
secureTransport, callback);
}
}
@@ -334,4 +351,109 @@ final class WearableSensingManagerPerUserService extends
dataType, dataRequestObserverId, packageName, statusCallback);
}
}
+
+ /** Handles starting hotword listening. */
+ public void onStartHotwordRecognition(
+ ComponentName targetVisComponentName, RemoteCallback statusCallback) {
+ synchronized (mLock) {
+ if (!setUpServiceIfNeeded()) {
+ Slog.w(TAG, "Detection service is not available at this moment.");
+ notifyStatusCallback(
+ statusCallback, WearableSensingManager.STATUS_SERVICE_UNAVAILABLE);
+ return;
+ }
+ if (!ensureVoiceInteractionManagerInternalInitiated()) {
+ Slog.w(TAG, "Voice interaction manager is not available at this moment.");
+ notifyStatusCallback(
+ statusCallback, WearableSensingManager.STATUS_SERVICE_UNAVAILABLE);
+ return;
+ }
+ ensureRemoteServiceInitiated();
+ mRemoteService.startHotwordRecognition(
+ createWearableHotwordCallback(targetVisComponentName), statusCallback);
+ }
+ }
+
+ /** Handles stopping hotword listening. */
+ public void onStopHotwordRecognition(RemoteCallback statusCallback) {
+ synchronized (mLock) {
+ if (!setUpServiceIfNeeded()) {
+ Slog.w(TAG, "Detection service is not available at this moment.");
+ notifyStatusCallback(
+ statusCallback, WearableSensingManager.STATUS_SERVICE_UNAVAILABLE);
+ return;
+ }
+ ensureRemoteServiceInitiated();
+ mRemoteService.stopHotwordRecognition(statusCallback);
+ }
+ }
+
+ private void onValidatedByHotwordDetectionService() {
+ synchronized (mLock) {
+ if (!setUpServiceIfNeeded()) {
+ Slog.w(TAG, "Wearable sensing service is not available at this moment.");
+ return;
+ }
+ ensureRemoteServiceInitiated();
+ mRemoteService.onValidatedByHotwordDetectionService();
+ }
+ }
+
+ private void stopActiveHotwordAudio() {
+ synchronized (mLock) {
+ if (!setUpServiceIfNeeded()) {
+ Slog.w(TAG, "Wearable sensing service is not available at this moment.");
+ return;
+ }
+ ensureRemoteServiceInitiated();
+ mRemoteService.stopActiveHotwordAudio();
+ }
+ }
+
+ private RemoteCallback createWearableHotwordCallback(ComponentName targetVisComponentName) {
+ return new RemoteCallback(
+ result -> {
+ HotwordAudioStream hotwordAudioStream =
+ result.getParcelable(
+ HOTWORD_AUDIO_STREAM_BUNDLE_KEY, HotwordAudioStream.class);
+ if (hotwordAudioStream == null) {
+ Slog.w(TAG, "No hotword audio stream received, unable to process hotword.");
+ return;
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mVoiceInteractionManagerInternal.startListeningFromWearable(
+ hotwordAudioStream.getAudioStreamParcelFileDescriptor(),
+ hotwordAudioStream.getAudioFormat(),
+ hotwordAudioStream.getMetadata(),
+ targetVisComponentName,
+ getUserId(),
+ createHotwordDetectionCallback());
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ });
+ }
+
+ private WearableHotwordDetectionCallback createHotwordDetectionCallback() {
+ return new WearableHotwordDetectionCallback() {
+ @Override
+ public void onDetected() {
+ Slog.i(TAG, "hotwordDetectionCallback onDetected.");
+ onValidatedByHotwordDetectionService();
+ }
+
+ @Override
+ public void onRejected() {
+ Slog.i(TAG, "hotwordDetectionCallback onRejected.");
+ stopActiveHotwordAudio();
+ }
+
+ @Override
+ public void onError(String errorMessage) {
+ Slog.i(TAG, "hotwordDetectionCallback onError. ErrorMessage: " + errorMessage);
+ stopActiveHotwordAudio();
+ }
+ };
+ }
}
diff --git a/services/core/java/com/android/server/wearable/WearableSensingManagerService.java b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
index 78952fa4590b..5f6ffd988c84 100644
--- a/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
+++ b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
@@ -55,6 +55,7 @@ import com.android.server.pm.KnownPackages;
import com.android.server.utils.quota.MultiRateLimiter;
import java.io.FileDescriptor;
+import java.time.Duration;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
@@ -106,7 +107,7 @@ public class WearableSensingManagerService extends
private final Context mContext;
private final AtomicInteger mNextDataRequestObserverId = new AtomicInteger(1);
private final Set<DataRequestObserverContext> mDataRequestObserverContexts = new HashSet<>();
- private final MultiRateLimiter mDataRequestRateLimiter;
+ @NonNull private volatile MultiRateLimiter mDataRequestRateLimiter;
volatile boolean mIsServiceEnabled;
public WearableSensingManagerService(Context context) {
@@ -238,6 +239,57 @@ public class WearableSensingManagerService extends
}
}
+ /**
+ * Sets the window size used in data request rate limiting.
+ *
+ * <p>The new value will not be reflected in {@link
+ * WearableSensingDataRequest#getRateLimitWindowSize()}.
+ *
+ * <p>{@code windowSize} will be automatically capped between
+ * com.android.server.utils.quota.QuotaTracker#MIN_WINDOW_SIZE_MS and
+ * com.android.server.utils.quota.QuotaTracker#MAX_WINDOW_SIZE_MS
+ *
+ * <p>The current rate limit will also be reset.
+ *
+ * <p>This method is only used for testing and must not be called in production code because
+ * it effectively bypasses the rate limiting introduced to enhance privacy protection.
+ */
+ @VisibleForTesting
+ void setDataRequestRateLimitWindowSize(@NonNull Duration windowSize) {
+ Slog.w(
+ TAG,
+ TextUtils.formatSimple(
+ "Setting the data request rate limit window size to %s. This also resets"
+ + " the current limit and should only be callable from a test.",
+ windowSize));
+ mDataRequestRateLimiter =
+ new MultiRateLimiter.Builder(mContext)
+ .addRateLimit(WearableSensingDataRequest.getRateLimit(), windowSize)
+ .build();
+ }
+
+ /**
+ * Resets the window size used in data request rate limiting back to the default value.
+ *
+ * <p>The current rate limit will also be reset.
+ *
+ * <p>This method is only used for testing and must not be called in production code because
+ * it effectively bypasses the rate limiting introduced to enhance privacy protection.
+ */
+ @VisibleForTesting
+ void resetDataRequestRateLimitWindowSize() {
+ Slog.w(
+ TAG,
+ "Resetting the data request rate limit window size back to the default value. This"
+ + " also resets the current limit and should only be callable from a test.");
+ mDataRequestRateLimiter =
+ new MultiRateLimiter.Builder(mContext)
+ .addRateLimit(
+ WearableSensingDataRequest.getRateLimit(),
+ WearableSensingDataRequest.getRateLimitWindowSize())
+ .build();
+ }
+
private DataRequestObserverContext getDataRequestObserverContext(
int dataType, int userId, PendingIntent dataRequestPendingIntent) {
synchronized (mDataRequestObserverContexts) {
@@ -347,9 +399,9 @@ public class WearableSensingManagerService extends
private final class WearableSensingManagerInternal extends IWearableSensingManager.Stub {
@Override
- public void provideWearableConnection(
+ public void provideConnection(
ParcelFileDescriptor wearableConnection, RemoteCallback callback) {
- Slog.i(TAG, "WearableSensingManagerInternal provideWearableConnection.");
+ Slog.i(TAG, "WearableSensingManagerInternal provideConnection.");
Objects.requireNonNull(wearableConnection);
Objects.requireNonNull(callback);
mContext.enforceCallingOrSelfPermission(
@@ -361,7 +413,7 @@ public class WearableSensingManagerService extends
return;
}
callPerUserServiceIfExist(
- service -> service.onProvideWearableConnection(wearableConnection, callback),
+ service -> service.onProvideConnection(wearableConnection, callback),
callback);
}
@@ -496,6 +548,42 @@ public class WearableSensingManagerService extends
}
@Override
+ public void startHotwordRecognition(
+ ComponentName targetVisComponentName, RemoteCallback statusCallback) {
+ Slog.i(TAG, "WearableSensingManagerInternal startHotwordRecognition.");
+ Objects.requireNonNull(statusCallback);
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE, TAG);
+ if (!mIsServiceEnabled) {
+ Slog.w(TAG, "Service not available.");
+ WearableSensingManagerPerUserService.notifyStatusCallback(
+ statusCallback, WearableSensingManager.STATUS_SERVICE_UNAVAILABLE);
+ return;
+ }
+ callPerUserServiceIfExist(
+ service ->
+ service.onStartHotwordRecognition(
+ targetVisComponentName, statusCallback),
+ statusCallback);
+ }
+
+ @Override
+ public void stopHotwordRecognition(RemoteCallback statusCallback) {
+ Slog.i(TAG, "WearableSensingManagerInternal stopHotwordRecognition.");
+ Objects.requireNonNull(statusCallback);
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.MANAGE_WEARABLE_SENSING_SERVICE, TAG);
+ if (!mIsServiceEnabled) {
+ Slog.w(TAG, "Service not available.");
+ WearableSensingManagerPerUserService.notifyStatusCallback(
+ statusCallback, WearableSensingManager.STATUS_SERVICE_UNAVAILABLE);
+ return;
+ }
+ callPerUserServiceIfExist(
+ service -> service.onStopHotwordRecognition(statusCallback), statusCallback);
+ }
+
+ @Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
new WearableSensingShellCommand(WearableSensingManagerService.this).exec(
diff --git a/services/core/java/com/android/server/wearable/WearableSensingShellCommand.java b/services/core/java/com/android/server/wearable/WearableSensingShellCommand.java
index 842bccbe0847..0a9cf344e7b2 100644
--- a/services/core/java/com/android/server/wearable/WearableSensingShellCommand.java
+++ b/services/core/java/com/android/server/wearable/WearableSensingShellCommand.java
@@ -29,6 +29,7 @@ import android.util.Slog;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
+import java.time.Duration;
final class WearableSensingShellCommand extends ShellCommand {
private static final String TAG = WearableSensingShellCommand.class.getSimpleName();
@@ -90,6 +91,8 @@ final class WearableSensingShellCommand extends ShellCommand {
return getBoundPackageName();
case "set-temporary-service":
return setTemporaryService();
+ case "set-data-request-rate-limit-window-size":
+ return setDataRequestRateLimitWindowSize();
default:
return handleDefaultCommands(cmd);
}
@@ -114,6 +117,11 @@ final class WearableSensingShellCommand extends ShellCommand {
pw.println(" set-temporary-service USER_ID [PACKAGE_NAME] [COMPONENT_NAME DURATION]");
pw.println(" Temporarily (for DURATION ms) changes the service implementation.");
pw.println(" To reset, call with just the USER_ID argument.");
+ pw.println(" set-data-request-rate-limit-window-size WINDOW_SIZE");
+ pw.println(" Set the window size used in data request rate limiting to WINDOW_SIZE"
+ + " seconds.");
+ pw.println(" positive WINDOW_SIZE smaller than 20 will be automatically set to 20.");
+ pw.println(" To reset, call with 0 or a negative WINDOW_SIZE.");
}
private int createDataStream() {
@@ -209,4 +217,20 @@ final class WearableSensingShellCommand extends ShellCommand {
resultPrinter.println(componentName == null ? "" : componentName.getPackageName());
return 0;
}
+
+ private int setDataRequestRateLimitWindowSize() {
+ Slog.d(TAG, "setDataRequestRateLimitWindowSize");
+ int windowSizeSeconds = Integer.parseInt(getNextArgRequired());
+ if (windowSizeSeconds <= 0) {
+ mService.resetDataRequestRateLimitWindowSize();
+ } else {
+ // 20 is the minimum window size supported by the rate limiter.
+ // It is defined by com.android.server.utils.quota.QuotaTracker#MIN_WINDOW_SIZE_MS
+ if (windowSizeSeconds < 20) {
+ windowSizeSeconds = 20;
+ }
+ mService.setDataRequestRateLimitWindowSize(Duration.ofSeconds(windowSizeSeconds));
+ }
+ return 0;
+ }
}
diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java
index ea8a801ff697..c6e8eb8d3743 100644
--- a/services/core/java/com/android/server/webkit/SystemImpl.java
+++ b/services/core/java/com/android/server/webkit/SystemImpl.java
@@ -21,7 +21,9 @@ import static android.webkit.Flags.updateServiceV2;
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
@@ -39,11 +41,14 @@ import android.webkit.WebViewProviderInfo;
import android.webkit.WebViewZygote;
import com.android.internal.util.XmlUtils;
+import com.android.server.LocalServices;
+import com.android.server.PinnerService;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -60,6 +65,7 @@ public class SystemImpl implements SystemInterface {
private static final String TAG_AVAILABILITY = "availableByDefault";
private static final String TAG_SIGNATURE = "signature";
private static final String TAG_FALLBACK = "isFallback";
+ private static final String PIN_GROUP = "webview";
private final WebViewProviderInfo[] mWebViewProviderPackages;
// Initialization-on-demand holder idiom for getting the WebView provider packages once and
@@ -220,6 +226,21 @@ public class SystemImpl implements SystemInterface {
}
@Override
+ public void installExistingPackageForAllUsers(Context context, String packageName) {
+ UserManager userManager = context.getSystemService(UserManager.class);
+ for (UserInfo userInfo : userManager.getUsers()) {
+ installPackageForUser(packageName, userInfo.id);
+ }
+ }
+
+ private void installPackageForUser(String packageName, int userId) {
+ final Context context = AppGlobals.getInitialApplication();
+ final Context contextAsUser = context.createContextAsUser(UserHandle.of(userId), 0);
+ final PackageInstaller installer = contextAsUser.getPackageManager().getPackageInstaller();
+ installer.installExistingPackage(packageName, PackageManager.INSTALL_REASON_UNKNOWN, null);
+ }
+
+ @Override
public boolean systemIsDebuggable() {
return Build.IS_DEBUGGABLE;
}
@@ -277,6 +298,36 @@ public class SystemImpl implements SystemInterface {
return true;
}
+ @Override
+ public void pinWebviewIfRequired(ApplicationInfo appInfo) {
+ PinnerService pinnerService = LocalServices.getService(PinnerService.class);
+ int webviewPinQuota = pinnerService.getWebviewPinQuota();
+ if (webviewPinQuota <= 0) {
+ return;
+ }
+
+ pinnerService.unpinGroup(PIN_GROUP);
+
+ ArrayList<String> apksToPin = new ArrayList<>();
+ boolean pinSharedFirst = appInfo.metaData.getBoolean("PIN_SHARED_LIBS_FIRST", true);
+ for (String sharedLib : appInfo.sharedLibraryFiles) {
+ apksToPin.add(sharedLib);
+ }
+ apksToPin.add(appInfo.sourceDir);
+ if (!pinSharedFirst) {
+ // We want to prioritize pinning of the native library that is most likely used by apps
+ // which in some build flavors live in the main apk and as a shared library for others.
+ Collections.reverse(apksToPin);
+ }
+ for (String apk : apksToPin) {
+ if (webviewPinQuota <= 0) {
+ break;
+ }
+ int bytesPinned = pinnerService.pinFile(apk, webviewPinQuota, appInfo, PIN_GROUP);
+ webviewPinQuota -= bytesPinned;
+ }
+ }
+
// flags declaring we want extra info from the package manager for webview providers
private final static int PACKAGE_FLAGS = PackageManager.GET_META_DATA
| PackageManager.GET_SIGNATURES | PackageManager.GET_SHARED_LIBRARY_FILES
diff --git a/services/core/java/com/android/server/webkit/SystemInterface.java b/services/core/java/com/android/server/webkit/SystemInterface.java
index 09c23a7229ad..ad32f623c80d 100644
--- a/services/core/java/com/android/server/webkit/SystemInterface.java
+++ b/services/core/java/com/android/server/webkit/SystemInterface.java
@@ -17,6 +17,7 @@
package com.android.server.webkit;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.webkit.UserPackage;
@@ -42,6 +43,7 @@ public interface SystemInterface {
public void killPackageDependents(String packageName);
public void enablePackageForAllUsers(Context context, String packageName, boolean enable);
+ public void installExistingPackageForAllUsers(Context context, String packageName);
public boolean systemIsDebuggable();
public PackageInfo getPackageInfoForProvider(WebViewProviderInfo configInfo)
@@ -61,4 +63,6 @@ public interface SystemInterface {
/** Start the zygote if it's not already running. */
public void ensureZygoteStarted();
public boolean isMultiProcessDefaultEnabled();
+
+ public void pinWebviewIfRequired(ApplicationInfo appInfo);
}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
index d95b431752ec..1d6ad6d3a6d9 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -17,7 +17,6 @@ package com.android.server.webkit;
import android.annotation.Nullable;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
@@ -30,12 +29,8 @@ import android.webkit.WebViewFactory;
import android.webkit.WebViewProviderInfo;
import android.webkit.WebViewProviderResponse;
-import com.android.server.LocalServices;
-import com.android.server.PinnerService;
-
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
/**
@@ -93,8 +88,6 @@ class WebViewUpdateServiceImpl implements WebViewUpdateServiceInterface {
private static final int MULTIPROCESS_SETTING_ON_VALUE = Integer.MAX_VALUE;
private static final int MULTIPROCESS_SETTING_OFF_VALUE = Integer.MIN_VALUE;
- private static final String PIN_GROUP = "webview";
-
private final SystemInterface mSystemInterface;
private final Context mContext;
@@ -346,38 +339,6 @@ class WebViewUpdateServiceImpl implements WebViewUpdateServiceInterface {
return newPackage;
}
- private void pinWebviewIfRequired(ApplicationInfo appInfo) {
- PinnerService pinnerService = LocalServices.getService(PinnerService.class);
- if (pinnerService == null) {
- // This happens in unit tests which do not have services.
- return;
- }
- int webviewPinQuota = pinnerService.getWebviewPinQuota();
- if (webviewPinQuota <= 0) {
- return;
- }
-
- pinnerService.unpinGroup(PIN_GROUP);
-
- ArrayList<String> apksToPin = new ArrayList<>();
- boolean pinSharedFirst = appInfo.metaData.getBoolean("PIN_SHARED_LIBS_FIRST", true);
- for (String sharedLib : appInfo.sharedLibraryFiles) {
- apksToPin.add(sharedLib);
- }
- apksToPin.add(appInfo.sourceDir);
- if (!pinSharedFirst) {
- // We want to prioritize pinning of the native library that is most likely used by apps
- // which in some build flavors live in the main apk and as a shared library for others.
- Collections.reverse(apksToPin);
- }
- for (String apk : apksToPin) {
- if (webviewPinQuota <= 0) {
- break;
- }
- int bytesPinned = pinnerService.pinFile(apk, webviewPinQuota, appInfo, PIN_GROUP);
- webviewPinQuota -= bytesPinned;
- }
- }
/**
* This is called when we change WebView provider, either when the current provider is
* updated or a new provider is chosen / takes precedence.
@@ -386,7 +347,7 @@ class WebViewUpdateServiceImpl implements WebViewUpdateServiceInterface {
synchronized (mLock) {
mAnyWebViewInstalled = true;
if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
- pinWebviewIfRequired(newPackage.applicationInfo);
+ mSystemInterface.pinWebviewIfRequired(newPackage.applicationInfo);
mCurrentWebViewPackage = newPackage;
// The relro creations might 'finish' (not start at all) before
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
index 1bc635bd9ca3..596de686089e 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
@@ -17,7 +17,6 @@ package com.android.server.webkit;
import android.annotation.Nullable;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
@@ -32,12 +31,8 @@ import android.webkit.WebViewFactory;
import android.webkit.WebViewProviderInfo;
import android.webkit.WebViewProviderResponse;
-import com.android.server.LocalServices;
-import com.android.server.PinnerService;
-
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
/**
@@ -88,10 +83,9 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface {
private static final int VALIDITY_INCORRECT_SIGNATURE = 3;
private static final int VALIDITY_NO_LIBRARY_FLAG = 4;
- private static final String PIN_GROUP = "webview";
-
private final SystemInterface mSystemInterface;
private final Context mContext;
+ private final WebViewProviderInfo mDefaultProvider;
private long mMinimumVersionCode = -1;
@@ -112,6 +106,21 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface {
WebViewUpdateServiceImpl2(Context context, SystemInterface systemInterface) {
mContext = context;
mSystemInterface = systemInterface;
+ WebViewProviderInfo[] webviewProviders = getWebViewPackages();
+
+ WebViewProviderInfo defaultProvider = null;
+ for (WebViewProviderInfo provider : webviewProviders) {
+ if (provider.availableByDefault) {
+ defaultProvider = provider;
+ break;
+ }
+ }
+ if (defaultProvider == null) {
+ // This should be unreachable because the config parser enforces that there is at least
+ // one availableByDefault provider.
+ throw new AndroidRuntimeException("No available by default WebView Provider.");
+ }
+ mDefaultProvider = defaultProvider;
}
@Override
@@ -170,11 +179,10 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface {
if (mCurrentWebViewPackage == null) {
return true;
}
- WebViewProviderInfo defaultProvider = getDefaultWebViewPackage();
- if (mCurrentWebViewPackage.packageName.equals(defaultProvider.packageName)) {
+ if (mCurrentWebViewPackage.packageName.equals(mDefaultProvider.packageName)) {
List<UserPackage> userPackages =
mSystemInterface.getPackageInfoForProviderAllUsers(
- mContext, defaultProvider);
+ mContext, mDefaultProvider);
return !isInstalledAndEnabledForAllUsers(userPackages);
} else {
return false;
@@ -203,17 +211,18 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface {
}
if (repairNeeded) {
- // We didn't find a valid WebView implementation. Try explicitly re-enabling the
- // default package for all users in case it was disabled, even if we already did the
- // one-time migration before. If this actually changes the state, we will see the
- // PackageManager broadcast shortly and try again.
- WebViewProviderInfo defaultProvider = getDefaultWebViewPackage();
+ // We didn't find a valid WebView implementation. Try explicitly re-installing and
+ // re-enabling the default package for all users in case it was disabled, even if we
+ // already did the one-time migration before. If this actually changes the state, we
+ // will see the PackageManager broadcast shortly and try again.
Slog.w(
TAG,
- "No provider available for all users, trying to enable "
- + defaultProvider.packageName);
+ "No provider available for all users, trying to install and enable "
+ + mDefaultProvider.packageName);
+ mSystemInterface.installExistingPackageForAllUsers(
+ mContext, mDefaultProvider.packageName);
mSystemInterface.enablePackageForAllUsers(
- mContext, defaultProvider.packageName, true);
+ mContext, mDefaultProvider.packageName, true);
}
} catch (Throwable t) {
@@ -356,39 +365,6 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface {
return newPackage;
}
- private void pinWebviewIfRequired(ApplicationInfo appInfo) {
- PinnerService pinnerService = LocalServices.getService(PinnerService.class);
- if (pinnerService == null) {
- // This happens in unit tests which do not have services.
- return;
- }
- int webviewPinQuota = pinnerService.getWebviewPinQuota();
- if (webviewPinQuota <= 0) {
- return;
- }
-
- pinnerService.unpinGroup(PIN_GROUP);
-
- ArrayList<String> apksToPin = new ArrayList<>();
- boolean pinSharedFirst = appInfo.metaData.getBoolean("PIN_SHARED_LIBS_FIRST", true);
- for (String sharedLib : appInfo.sharedLibraryFiles) {
- apksToPin.add(sharedLib);
- }
- apksToPin.add(appInfo.sourceDir);
- if (!pinSharedFirst) {
- // We want to prioritize pinning of the native library that is most likely used by apps
- // which in some build flavors live in the main apk and as a shared library for others.
- Collections.reverse(apksToPin);
- }
- for (String apk : apksToPin) {
- if (webviewPinQuota <= 0) {
- break;
- }
- int bytesPinned = pinnerService.pinFile(apk, webviewPinQuota, appInfo, PIN_GROUP);
- webviewPinQuota -= bytesPinned;
- }
- }
-
/**
* This is called when we change WebView provider, either when the current provider is
* updated or a new provider is chosen / takes precedence.
@@ -397,7 +373,7 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface {
synchronized (mLock) {
mAnyWebViewInstalled = true;
if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
- pinWebviewIfRequired(newPackage.applicationInfo);
+ mSystemInterface.pinWebviewIfRequired(newPackage.applicationInfo);
mCurrentWebViewPackage = newPackage;
// The relro creations might 'finish' (not start at all) before
@@ -438,15 +414,7 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface {
*/
@Override
public WebViewProviderInfo getDefaultWebViewPackage() {
- WebViewProviderInfo[] webviewProviders = getWebViewPackages();
- for (WebViewProviderInfo provider : webviewProviders) {
- if (provider.availableByDefault) {
- return provider;
- }
- }
- // This should be unreachable because the config parser enforces that there is at least one
- // availableByDefault provider.
- throw new AndroidRuntimeException("No available by default WebView Provider.");
+ return mDefaultProvider;
}
private static class ProviderAndPackageInfo {
@@ -507,14 +475,13 @@ class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface {
// User did not choose, or the choice failed; return the default provider even if it is not
// installed or enabled for all users.
- WebViewProviderInfo defaultProvider = getDefaultWebViewPackage();
try {
- PackageInfo packageInfo = mSystemInterface.getPackageInfoForProvider(defaultProvider);
- if (validityResult(defaultProvider, packageInfo) == VALIDITY_OK) {
+ PackageInfo packageInfo = mSystemInterface.getPackageInfoForProvider(mDefaultProvider);
+ if (validityResult(mDefaultProvider, packageInfo) == VALIDITY_OK) {
return packageInfo;
}
} catch (NameNotFoundException e) {
- Slog.w(TAG, "Default WebView package (" + defaultProvider.packageName + ") not found");
+ Slog.w(TAG, "Default WebView package (" + mDefaultProvider.packageName + ") not found");
}
// This should never happen during normal operation (only with modified system images).
diff --git a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java
index e5c743cc69e4..fd4b06148c5f 100644
--- a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java
+++ b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java
@@ -147,6 +147,7 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer,
@Nullable
protected abstract ActivityRecord findAppTokenForSnapshot(TYPE source);
protected abstract boolean use16BitFormat();
+ protected abstract Rect getLetterboxInsets(ActivityRecord topActivity);
/**
* This is different than {@link #recordSnapshotInner(TYPE)} because it doesn't store
@@ -309,7 +310,7 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer,
final WindowState mainWindow = result.second;
final Rect contentInsets = getSystemBarInsets(mainWindow.getFrame(),
mainWindow.getInsetsStateWithVisibilityOverride());
- final Rect letterboxInsets = activity.getLetterboxInsets();
+ final Rect letterboxInsets = getLetterboxInsets(activity);
InsetUtils.addInsets(contentInsets, letterboxInsets);
builder.setIsRealSnapshot(true);
builder.setId(System.currentTimeMillis());
@@ -335,22 +336,27 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer,
final Configuration taskConfig = activity.getTask().getConfiguration();
final int displayRotation = taskConfig.windowConfiguration.getDisplayRotation();
final Rect outCrop = new Rect();
+ final Point taskSize = new Point();
final Transition.ChangeInfo changeInfo = mCurrentChangeInfo;
if (changeInfo != null && changeInfo.mRotation != displayRotation) {
// For example, the source is closing and display rotation changes at the same time.
// The snapshot should record the state in previous rotation.
outCrop.set(changeInfo.mAbsoluteBounds);
+ taskSize.set(changeInfo.mAbsoluteBounds.right, changeInfo.mAbsoluteBounds.bottom);
builder.setRotation(changeInfo.mRotation);
builder.setOrientation(changeInfo.mAbsoluteBounds.height()
>= changeInfo.mAbsoluteBounds.width()
? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE);
} else {
- outCrop.set(taskConfig.windowConfiguration.getBounds());
+ final Configuration srcConfig = source.getConfiguration();
+ outCrop.set(srcConfig.windowConfiguration.getBounds());
+ final Rect taskBounds = taskConfig.windowConfiguration.getBounds();
+ taskSize.set(taskBounds.width(), taskBounds.height());
builder.setRotation(displayRotation);
- builder.setOrientation(taskConfig.orientation);
+ builder.setOrientation(srcConfig.orientation);
}
outCrop.offsetTo(0, 0);
- builder.setTaskSize(new Point(outCrop.right, outCrop.bottom));
+ builder.setTaskSize(taskSize);
return outCrop;
}
@@ -438,7 +444,7 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer,
return null;
}
final Rect contentInsets = new Rect(systemBarInsets);
- final Rect letterboxInsets = topActivity.getLetterboxInsets();
+ final Rect letterboxInsets = getLetterboxInsets(topActivity);
InsetUtils.addInsets(contentInsets, letterboxInsets);
// Note, the app theme snapshot is never translucent because we enforce a non-translucent
// color above
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index ee865d3a588a..44a0547a6828 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -523,15 +523,15 @@ final class AccessibilityController {
|| mWindowsForAccessibilityObserver.size() > 0);
}
- void setForceShowMagnifiableBounds(int displayId, boolean show) {
+ void setFullscreenMagnificationActivated(int displayId, boolean activated) {
if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
- mAccessibilityTracing.logTrace(TAG + ".setForceShowMagnifiableBounds",
- FLAGS_MAGNIFICATION_CALLBACK, "displayId=" + displayId + "; show=" + show);
+ mAccessibilityTracing.logTrace(TAG + ".setFullscreenMagnificationActivated",
+ FLAGS_MAGNIFICATION_CALLBACK,
+ "displayId=" + displayId + "; activated=" + activated);
}
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
- displayMagnifier.setForceShowMagnifiableBounds(show);
- displayMagnifier.showMagnificationBoundsIfNeeded();
+ displayMagnifier.setFullscreenMagnificationActivated(activated);
}
}
@@ -624,10 +624,21 @@ final class AccessibilityController {
private final long mLongAnimationDuration;
- private boolean mForceShowMagnifiableBounds = false;
+ private boolean mIsFullscreenMagnificationActivated = false;
+ private final Region mMagnificationRegion = new Region();
+ private final Region mOldMagnificationRegion = new Region();
private final MagnificationSpec mMagnificationSpec = new MagnificationSpec();
+ // Following fields are used for computing magnification region
+ private final Path mCircularPath;
+ private int mTempLayer = 0;
+ private final Point mScreenSize = new Point();
+ private final SparseArray<WindowState> mTempWindowStates =
+ new SparseArray<WindowState>();
+ private final RectF mTempRectF = new RectF();
+ private final Matrix mTempMatrix = new Matrix();
+
DisplayMagnifier(WindowManagerService windowManagerService,
DisplayContent displayContent,
Display display,
@@ -643,6 +654,15 @@ final class AccessibilityController {
AccessibilityController.getAccessibilityControllerInternal(mService);
mLongAnimationDuration = mDisplayContext.getResources().getInteger(
com.android.internal.R.integer.config_longAnimTime);
+ if (mDisplayContext.getResources().getConfiguration().isScreenRound()) {
+ mCircularPath = new Path();
+
+ getDisplaySizeLocked(mScreenSize);
+ final int centerXY = mScreenSize.x / 2;
+ mCircularPath.addCircle(centerXY, centerXY, centerXY, Path.Direction.CW);
+ } else {
+ mCircularPath = null;
+ }
if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
mAccessibilityTracing.logTrace(LOG_TAG + ".DisplayMagnifier.constructor",
FLAGS_MAGNIFICATION_CALLBACK,
@@ -650,6 +670,7 @@ final class AccessibilityController {
+ displayContent + "}; display={" + display + "}; callbacks={"
+ callbacks + "}");
}
+ recomputeBounds();
}
void setMagnificationSpec(MagnificationSpec spec) {
@@ -658,7 +679,7 @@ final class AccessibilityController {
FLAGS_MAGNIFICATION_CALLBACK, "spec={" + spec + "}");
}
updateMagnificationSpec(spec);
- mMagnifedViewport.recomputeBounds();
+ recomputeBounds();
mService.applyMagnificationSpecLocked(mDisplay.getDisplayId(), spec);
mService.scheduleAnimationLocked();
@@ -670,30 +691,26 @@ final class AccessibilityController {
} else {
mMagnificationSpec.clear();
}
- // If this message is pending we are in a rotation animation and do not want
- // to show the border. We will do so when the pending message is handled.
- if (!mHandler.hasMessages(
- MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) {
- mMagnifedViewport.setMagnifiedRegionBorderShown(
- isForceShowingMagnifiableBounds(), true);
- }
+
+ mMagnifedViewport.setShowMagnifiedBorderIfNeeded();
}
- void setForceShowMagnifiableBounds(boolean show) {
+ void setFullscreenMagnificationActivated(boolean activated) {
if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
- mAccessibilityTracing.logTrace(LOG_TAG + ".setForceShowMagnifiableBounds",
- FLAGS_MAGNIFICATION_CALLBACK, "show=" + show);
+ mAccessibilityTracing.logTrace(LOG_TAG + ".setFullscreenMagnificationActivated",
+ FLAGS_MAGNIFICATION_CALLBACK, "activated=" + activated);
}
- mForceShowMagnifiableBounds = show;
- mMagnifedViewport.setMagnifiedRegionBorderShown(show, true);
+ mIsFullscreenMagnificationActivated = activated;
+ mMagnifedViewport.setMagnifiedRegionBorderShown(activated, true);
+ mMagnifedViewport.showMagnificationBoundsIfNeeded();
}
- boolean isForceShowingMagnifiableBounds() {
+ boolean isFullscreenMagnificationActivated() {
if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
- mAccessibilityTracing.logTrace(LOG_TAG + ".isForceShowingMagnifiableBounds",
+ mAccessibilityTracing.logTrace(LOG_TAG + ".isFullscreenMagnificationActivated",
FLAGS_MAGNIFICATION_CALLBACK);
}
- return mForceShowMagnifiableBounds;
+ return mIsFullscreenMagnificationActivated;
}
void onWindowLayersChanged() {
@@ -704,7 +721,7 @@ final class AccessibilityController {
if (DEBUG_LAYERS) {
Slog.i(LOG_TAG, "Layers changed.");
}
- mMagnifedViewport.recomputeBounds();
+ recomputeBounds();
mService.scheduleAnimationLocked();
}
@@ -718,6 +735,8 @@ final class AccessibilityController {
Slog.i(LOG_TAG, "Rotation: " + Surface.rotationToString(rotation)
+ " displayId: " + displayContent.getDisplayId());
}
+
+ recomputeBounds();
mMagnifedViewport.onDisplaySizeChanged();
mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_DISPLAY_SIZE_CHANGED);
}
@@ -733,7 +752,7 @@ final class AccessibilityController {
+ AppTransition.appTransitionOldToString(transition)
+ " displayId: " + displayId);
}
- final boolean isMagnifierActivated = isForceShowingMagnifiableBounds();
+ final boolean isMagnifierActivated = isFullscreenMagnificationActivated();
if (isMagnifierActivated) {
switch (transition) {
case WindowManager.TRANSIT_OLD_ACTIVITY_OPEN:
@@ -758,7 +777,7 @@ final class AccessibilityController {
Slog.i(LOG_TAG, "Window transition: " + WindowManager.transitTypeToString(type)
+ " displayId: " + displayId);
}
- final boolean isMagnifierActivated = isForceShowingMagnifiableBounds();
+ final boolean isMagnifierActivated = isFullscreenMagnificationActivated();
if (isMagnifierActivated) {
// All opening/closing situations.
switch (type) {
@@ -782,7 +801,7 @@ final class AccessibilityController {
+ AppTransition.appTransitionOldToString(transition)
+ " displayId: " + windowState.getDisplayId());
}
- final boolean isMagnifierActivated = isForceShowingMagnifiableBounds();
+ final boolean isMagnifierActivated = isFullscreenMagnificationActivated();
final int type = windowState.mAttrs.type;
switch (transition) {
case WindowManagerPolicy.TRANSIT_ENTER:
@@ -835,9 +854,7 @@ final class AccessibilityController {
}
void getMagnifiedFrameInContentCoords(Rect rect) {
- Region magnificationRegion = new Region();
- mMagnifedViewport.getMagnificationRegion(magnificationRegion);
- magnificationRegion.getBounds(rect);
+ mMagnificationRegion.getBounds(rect);
rect.offset((int) -mMagnificationSpec.offsetX, (int) -mMagnificationSpec.offsetY);
rect.scale(1.0f / mMagnificationSpec.scale);
}
@@ -872,8 +889,8 @@ final class AccessibilityController {
"outMagnificationRegion={" + outMagnificationRegion + "}");
}
// Make sure we're working with the most current bounds
- mMagnifedViewport.recomputeBounds();
- mMagnifedViewport.getMagnificationRegion(outMagnificationRegion);
+ recomputeBounds();
+ outMagnificationRegion.set(mMagnificationRegion);
}
boolean isMagnifying() {
@@ -887,16 +904,6 @@ final class AccessibilityController {
mMagnifedViewport.destroyWindow();
}
- // Can be called outside of a surface transaction
- void showMagnificationBoundsIfNeeded() {
- if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
- mAccessibilityTracing.logTrace(LOG_TAG + ".showMagnificationBoundsIfNeeded",
- FLAGS_MAGNIFICATION_CALLBACK);
- }
- mHandler.obtainMessage(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)
- .sendToTarget();
- }
-
void drawMagnifiedRegionBorderIfNeeded() {
if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
mAccessibilityTracing.logTrace(LOG_TAG + ".drawMagnifiedRegionBorderIfNeeded",
@@ -905,221 +912,236 @@ final class AccessibilityController {
mMagnifedViewport.drawWindowIfNeeded();
}
- void dump(PrintWriter pw, String prefix) {
- mMagnifedViewport.dump(pw, prefix);
- }
+ void recomputeBounds() {
+ getDisplaySizeLocked(mScreenSize);
+ final int screenWidth = mScreenSize.x;
+ final int screenHeight = mScreenSize.y;
- private final class MagnifiedViewport {
-
- private final SparseArray<WindowState> mTempWindowStates =
- new SparseArray<WindowState>();
+ mMagnificationRegion.set(0, 0, 0, 0);
+ final Region availableBounds = mTempRegion1;
+ availableBounds.set(0, 0, screenWidth, screenHeight);
- private final RectF mTempRectF = new RectF();
+ if (mCircularPath != null) {
+ availableBounds.setPath(mCircularPath, availableBounds);
+ }
- private final Point mScreenSize = new Point();
+ Region nonMagnifiedBounds = mTempRegion4;
+ nonMagnifiedBounds.set(0, 0, 0, 0);
- private final Matrix mTempMatrix = new Matrix();
+ SparseArray<WindowState> visibleWindows = mTempWindowStates;
+ visibleWindows.clear();
+ populateWindowsOnScreen(visibleWindows);
- private final Region mMagnificationRegion = new Region();
- private final Region mOldMagnificationRegion = new Region();
+ final int visibleWindowCount = visibleWindows.size();
+ for (int i = visibleWindowCount - 1; i >= 0; i--) {
+ WindowState windowState = visibleWindows.valueAt(i);
+ final int windowType = windowState.mAttrs.type;
+ if (isExcludedWindowType(windowType)
+ || ((windowState.mAttrs.privateFlags
+ & PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION) != 0)
+ || ((windowState.mAttrs.privateFlags
+ & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0)) {
+ continue;
+ }
- private final Path mCircularPath;
+ // Consider the touchable portion of the window
+ Matrix matrix = mTempMatrix;
+ populateTransformationMatrix(windowState, matrix);
+ Region touchableRegion = mTempRegion3;
+ windowState.getTouchableRegion(touchableRegion);
+ Rect touchableFrame = mTempRect1;
+ touchableRegion.getBounds(touchableFrame);
+ RectF windowFrame = mTempRectF;
+ windowFrame.set(touchableFrame);
+ windowFrame.offset(-windowState.getFrame().left,
+ -windowState.getFrame().top);
+ matrix.mapRect(windowFrame);
+ Region windowBounds = mTempRegion2;
+ windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
+ (int) windowFrame.right, (int) windowFrame.bottom);
+ // Only update new regions
+ Region portionOfWindowAlreadyAccountedFor = mTempRegion3;
+ portionOfWindowAlreadyAccountedFor.set(mMagnificationRegion);
+ portionOfWindowAlreadyAccountedFor.op(nonMagnifiedBounds, Region.Op.UNION);
+ windowBounds.op(portionOfWindowAlreadyAccountedFor, Region.Op.DIFFERENCE);
+
+ if (windowState.shouldMagnify()) {
+ mMagnificationRegion.op(windowBounds, Region.Op.UNION);
+ mMagnificationRegion.op(availableBounds, Region.Op.INTERSECT);
+ } else {
+ nonMagnifiedBounds.op(windowBounds, Region.Op.UNION);
+ availableBounds.op(windowBounds, Region.Op.DIFFERENCE);
+ }
- private final float mBorderWidth;
- private final int mHalfBorderWidth;
- private final int mDrawBorderInset;
+ // If the navigation bar window doesn't have touchable region, count
+ // navigation bar insets into nonMagnifiedBounds. It happens when
+ // navigation mode is gestural.
+ if (isUntouchableNavigationBar(windowState, mTempRegion3)) {
+ final Rect navBarInsets = getSystemBarInsetsFrame(windowState);
+ nonMagnifiedBounds.op(navBarInsets, Region.Op.UNION);
+ availableBounds.op(navBarInsets, Region.Op.DIFFERENCE);
+ }
- private final ViewportWindow mWindow;
+ // Count letterbox into nonMagnifiedBounds
+ if (windowState.areAppWindowBoundsLetterboxed()) {
+ Region letterboxBounds = getLetterboxBounds(windowState);
+ nonMagnifiedBounds.op(letterboxBounds, Region.Op.UNION);
+ availableBounds.op(letterboxBounds, Region.Op.DIFFERENCE);
+ }
- private boolean mFullRedrawNeeded;
- private int mTempLayer = 0;
+ // Update accounted bounds
+ Region accountedBounds = mTempRegion2;
+ accountedBounds.set(mMagnificationRegion);
+ accountedBounds.op(nonMagnifiedBounds, Region.Op.UNION);
+ accountedBounds.op(0, 0, screenWidth, screenHeight, Region.Op.INTERSECT);
+
+ if (accountedBounds.isRect()) {
+ Rect accountedFrame = mTempRect1;
+ accountedBounds.getBounds(accountedFrame);
+ if (accountedFrame.width() == screenWidth
+ && accountedFrame.height() == screenHeight) {
+ break;
+ }
+ }
+ }
+ visibleWindows.clear();
- MagnifiedViewport() {
- mBorderWidth = mDisplayContext.getResources().getDimension(
- com.android.internal.R.dimen.accessibility_magnification_indicator_width);
- mHalfBorderWidth = (int) Math.ceil(mBorderWidth / 2);
- mDrawBorderInset = (int) mBorderWidth / 2;
- mWindow = new ViewportWindow(mDisplayContext);
+ mMagnifedViewport.intersectWithDrawBorderInset(screenWidth, screenHeight);
- if (mDisplayContext.getResources().getConfiguration().isScreenRound()) {
- mCircularPath = new Path();
- getDisplaySizeLocked(mScreenSize);
- final int centerXY = mScreenSize.x / 2;
- mCircularPath.addCircle(centerXY, centerXY, centerXY, Path.Direction.CW);
- } else {
- mCircularPath = null;
- }
+ final boolean magnifiedChanged =
+ !mOldMagnificationRegion.equals(mMagnificationRegion);
+ if (magnifiedChanged) {
+ mMagnifedViewport.updateBorderDrawingStatus(screenWidth, screenHeight);
- recomputeBounds();
+ mOldMagnificationRegion.set(mMagnificationRegion);
+ final SomeArgs args = SomeArgs.obtain();
+ args.arg1 = Region.obtain(mMagnificationRegion);
+ mHandler.obtainMessage(
+ MyHandler.MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED, args)
+ .sendToTarget();
}
+ }
- void getMagnificationRegion(@NonNull Region outMagnificationRegion) {
- outMagnificationRegion.set(mMagnificationRegion);
+ private Region getLetterboxBounds(WindowState windowState) {
+ final ActivityRecord appToken = windowState.mActivityRecord;
+ if (appToken == null) {
+ return new Region();
}
- void recomputeBounds() {
- getDisplaySizeLocked(mScreenSize);
- final int screenWidth = mScreenSize.x;
- final int screenHeight = mScreenSize.y;
+ final Rect boundsWithoutLetterbox = windowState.getBounds();
+ final Rect letterboxInsets = appToken.getLetterboxInsets();
- mMagnificationRegion.set(0, 0, 0, 0);
- final Region availableBounds = mTempRegion1;
- availableBounds.set(0, 0, screenWidth, screenHeight);
+ final Rect boundsIncludingLetterbox = Rect.copyOrNull(boundsWithoutLetterbox);
+ // Letterbox insets from mActivityRecord are positive, so we negate them to grow the
+ // bounds to include the letterbox.
+ boundsIncludingLetterbox.inset(
+ Insets.subtract(Insets.NONE, Insets.of(letterboxInsets)));
- if (mCircularPath != null) {
- availableBounds.setPath(mCircularPath, availableBounds);
+ final Region letterboxBounds = new Region();
+ letterboxBounds.set(boundsIncludingLetterbox);
+ letterboxBounds.op(boundsWithoutLetterbox, Region.Op.DIFFERENCE);
+ return letterboxBounds;
+ }
+
+ private boolean isExcludedWindowType(int windowType) {
+ return windowType == TYPE_MAGNIFICATION_OVERLAY
+ // Omit the touch region of window magnification to avoid the cut out of the
+ // magnification and the magnified center of window magnification could be
+ // in the bounds
+ || windowType == TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
+ }
+
+ private void populateWindowsOnScreen(SparseArray<WindowState> outWindows) {
+ mTempLayer = 0;
+ mDisplayContent.forAllWindows((w) -> {
+ if (w.isOnScreen() && w.isVisible()
+ && (w.mAttrs.alpha != 0)) {
+ mTempLayer++;
+ outWindows.put(mTempLayer, w);
}
+ }, /* traverseTopToBottom= */ false);
+ }
- Region nonMagnifiedBounds = mTempRegion4;
- nonMagnifiedBounds.set(0, 0, 0, 0);
-
- SparseArray<WindowState> visibleWindows = mTempWindowStates;
- visibleWindows.clear();
- populateWindowsOnScreen(visibleWindows);
-
- final int visibleWindowCount = visibleWindows.size();
- for (int i = visibleWindowCount - 1; i >= 0; i--) {
- WindowState windowState = visibleWindows.valueAt(i);
- final int windowType = windowState.mAttrs.type;
- if (isExcludedWindowType(windowType)
- || ((windowState.mAttrs.privateFlags
- & PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION) != 0)
- || ((windowState.mAttrs.privateFlags
- & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0)) {
- continue;
- }
+ private void getDisplaySizeLocked(Point outSize) {
+ final Rect bounds =
+ mDisplayContent.getConfiguration().windowConfiguration.getBounds();
+ outSize.set(bounds.width(), bounds.height());
+ }
- // Consider the touchable portion of the window
- Matrix matrix = mTempMatrix;
- populateTransformationMatrix(windowState, matrix);
- Region touchableRegion = mTempRegion3;
- windowState.getTouchableRegion(touchableRegion);
- Rect touchableFrame = mTempRect1;
- touchableRegion.getBounds(touchableFrame);
- RectF windowFrame = mTempRectF;
- windowFrame.set(touchableFrame);
- windowFrame.offset(-windowState.getFrame().left,
- -windowState.getFrame().top);
- matrix.mapRect(windowFrame);
- Region windowBounds = mTempRegion2;
- windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
- (int) windowFrame.right, (int) windowFrame.bottom);
- // Only update new regions
- Region portionOfWindowAlreadyAccountedFor = mTempRegion3;
- portionOfWindowAlreadyAccountedFor.set(mMagnificationRegion);
- portionOfWindowAlreadyAccountedFor.op(nonMagnifiedBounds, Region.Op.UNION);
- windowBounds.op(portionOfWindowAlreadyAccountedFor, Region.Op.DIFFERENCE);
-
- if (windowState.shouldMagnify()) {
- mMagnificationRegion.op(windowBounds, Region.Op.UNION);
- mMagnificationRegion.op(availableBounds, Region.Op.INTERSECT);
- } else {
- nonMagnifiedBounds.op(windowBounds, Region.Op.UNION);
- availableBounds.op(windowBounds, Region.Op.DIFFERENCE);
- }
+ void dump(PrintWriter pw, String prefix) {
+ mMagnifedViewport.dump(pw, prefix);
+ }
- // If the navigation bar window doesn't have touchable region, count
- // navigation bar insets into nonMagnifiedBounds. It happens when
- // navigation mode is gestural.
- if (isUntouchableNavigationBar(windowState, mTempRegion3)) {
- final Rect navBarInsets = getSystemBarInsetsFrame(windowState);
- nonMagnifiedBounds.op(navBarInsets, Region.Op.UNION);
- availableBounds.op(navBarInsets, Region.Op.DIFFERENCE);
- }
+ private final class MagnifiedViewport {
- // Count letterbox into nonMagnifiedBounds
- if (windowState.areAppWindowBoundsLetterboxed()) {
- Region letterboxBounds = getLetterboxBounds(windowState);
- nonMagnifiedBounds.op(letterboxBounds, Region.Op.UNION);
- availableBounds.op(letterboxBounds, Region.Op.DIFFERENCE);
- }
+ private final float mBorderWidth;
+ private final int mHalfBorderWidth;
+ private final int mDrawBorderInset;
- // Update accounted bounds
- Region accountedBounds = mTempRegion2;
- accountedBounds.set(mMagnificationRegion);
- accountedBounds.op(nonMagnifiedBounds, Region.Op.UNION);
- accountedBounds.op(0, 0, screenWidth, screenHeight, Region.Op.INTERSECT);
-
- if (accountedBounds.isRect()) {
- Rect accountedFrame = mTempRect1;
- accountedBounds.getBounds(accountedFrame);
- if (accountedFrame.width() == screenWidth
- && accountedFrame.height() == screenHeight) {
- break;
- }
- }
- }
- visibleWindows.clear();
+ private final ViewportWindow mWindow;
- mMagnificationRegion.op(mDrawBorderInset, mDrawBorderInset,
- screenWidth - mDrawBorderInset, screenHeight - mDrawBorderInset,
- Region.Op.INTERSECT);
+ private boolean mFullRedrawNeeded;
- final boolean magnifiedChanged =
- !mOldMagnificationRegion.equals(mMagnificationRegion);
- if (magnifiedChanged) {
- mWindow.setBounds(mMagnificationRegion);
- final Rect dirtyRect = mTempRect1;
- if (mFullRedrawNeeded) {
- mFullRedrawNeeded = false;
- dirtyRect.set(mDrawBorderInset, mDrawBorderInset,
- screenWidth - mDrawBorderInset,
- screenHeight - mDrawBorderInset);
- mWindow.invalidate(dirtyRect);
- } else {
- final Region dirtyRegion = mTempRegion3;
- dirtyRegion.set(mMagnificationRegion);
- dirtyRegion.op(mOldMagnificationRegion, Region.Op.XOR);
- dirtyRegion.getBounds(dirtyRect);
- mWindow.invalidate(dirtyRect);
- }
+ MagnifiedViewport() {
+ mBorderWidth = mDisplayContext.getResources().getDimension(
+ com.android.internal.R.dimen.accessibility_magnification_indicator_width);
+ mHalfBorderWidth = (int) Math.ceil(mBorderWidth / 2);
+ mDrawBorderInset = (int) mBorderWidth / 2;
+ mWindow = new ViewportWindow(mDisplayContext);
+ }
- mOldMagnificationRegion.set(mMagnificationRegion);
- final SomeArgs args = SomeArgs.obtain();
- args.arg1 = Region.obtain(mMagnificationRegion);
- mHandler.obtainMessage(
- MyHandler.MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED, args)
- .sendToTarget();
+ void updateBorderDrawingStatus(int screenWidth, int screenHeight) {
+ mWindow.setBounds(mMagnificationRegion);
+ final Rect dirtyRect = mTempRect1;
+ if (mFullRedrawNeeded) {
+ mFullRedrawNeeded = false;
+ dirtyRect.set(mDrawBorderInset, mDrawBorderInset,
+ screenWidth - mDrawBorderInset,
+ screenHeight - mDrawBorderInset);
+ mWindow.invalidate(dirtyRect);
+ } else {
+ final Region dirtyRegion = mTempRegion3;
+ dirtyRegion.set(mMagnificationRegion);
+ dirtyRegion.op(mOldMagnificationRegion, Region.Op.XOR);
+ dirtyRegion.getBounds(dirtyRect);
+ mWindow.invalidate(dirtyRect);
}
}
- private Region getLetterboxBounds(WindowState windowState) {
- final ActivityRecord appToken = windowState.mActivityRecord;
- if (appToken == null) {
- return new Region();
+ void setShowMagnifiedBorderIfNeeded() {
+ // If this message is pending, we are in a rotation animation and do not want
+ // to show the border. We will do so when the pending message is handled.
+ if (!mHandler.hasMessages(
+ MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) {
+ setMagnifiedRegionBorderShown(
+ isFullscreenMagnificationActivated(), true);
}
+ }
- final Rect boundsWithoutLetterbox = windowState.getBounds();
- final Rect letterboxInsets = appToken.getLetterboxInsets();
-
- final Rect boundsIncludingLetterbox = Rect.copyOrNull(boundsWithoutLetterbox);
- // Letterbox insets from mActivityRecord are positive, so we negate them to grow the
- // bounds to include the letterbox.
- boundsIncludingLetterbox.inset(
- Insets.subtract(Insets.NONE, Insets.of(letterboxInsets)));
-
- final Region letterboxBounds = new Region();
- letterboxBounds.set(boundsIncludingLetterbox);
- letterboxBounds.op(boundsWithoutLetterbox, Region.Op.DIFFERENCE);
- return letterboxBounds;
+ // Can be called outside of a surface transaction
+ void showMagnificationBoundsIfNeeded() {
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".showMagnificationBoundsIfNeeded",
+ FLAGS_MAGNIFICATION_CALLBACK);
+ }
+ mHandler.obtainMessage(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)
+ .sendToTarget();
}
- private boolean isExcludedWindowType(int windowType) {
- return windowType == TYPE_MAGNIFICATION_OVERLAY
- // Omit the touch region of window magnification to avoid the cut out of the
- // magnification and the magnified center of window magnification could be
- // in the bounds
- || windowType == TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
+ void intersectWithDrawBorderInset(int screenWidth, int screenHeight) {
+ mMagnificationRegion.op(mDrawBorderInset, mDrawBorderInset,
+ screenWidth - mDrawBorderInset, screenHeight - mDrawBorderInset,
+ Region.Op.INTERSECT);
}
void onDisplaySizeChanged() {
- // If we are showing the magnification border, hide it immediately so
+ // If fullscreen magnification is activated, hide the border immediately so
// the user does not see strange artifacts during display size changed caused by
// rotation or folding/unfolding the device. In the rotation case, the screenshot
// used for rotation already has the border. After the rotation is complete
// we will show the border.
- if (isForceShowingMagnifiableBounds()) {
+ if (isFullscreenMagnificationActivated()) {
setMagnifiedRegionBorderShown(false, false);
final long delay = (long) (mLongAnimationDuration
* mService.getWindowAnimationScaleLocked());
@@ -1127,7 +1149,6 @@ final class AccessibilityController {
MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED);
mHandler.sendMessageDelayed(message, delay);
}
- recomputeBounds();
mWindow.updateSize();
}
@@ -1148,23 +1169,6 @@ final class AccessibilityController {
mWindow.releaseSurface();
}
- private void populateWindowsOnScreen(SparseArray<WindowState> outWindows) {
- mTempLayer = 0;
- mDisplayContent.forAllWindows((w) -> {
- if (w.isOnScreen() && w.isVisible()
- && (w.mAttrs.alpha != 0)) {
- mTempLayer++;
- outWindows.put(mTempLayer, w);
- }
- }, false /* traverseTopToBottom */ );
- }
-
- private void getDisplaySizeLocked(Point outSize) {
- final Rect bounds =
- mDisplayContent.getConfiguration().windowConfiguration.getBounds();
- outSize.set(bounds.width(), bounds.height());
- }
-
void dump(PrintWriter pw, String prefix) {
mWindow.dump(pw, prefix);
}
@@ -1490,7 +1494,7 @@ final class AccessibilityController {
case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : {
synchronized (mService.mGlobalLock) {
- if (isForceShowingMagnifiableBounds()) {
+ if (isFullscreenMagnificationActivated()) {
mMagnifedViewport.setMagnifiedRegionBorderShown(true, true);
mService.scheduleAnimationLocked();
}
@@ -1539,8 +1543,6 @@ final class AccessibilityController {
private final Set<IBinder> mTempBinderSet = new ArraySet<>();
- private final Point mTempPoint = new Point();
-
private final Region mTempRegion = new Region();
private final Region mTempRegion2 = new Region();
@@ -1610,8 +1612,9 @@ final class AccessibilityController {
Slog.i(LOG_TAG, "computeChangedWindows()");
}
- final List<WindowInfo> windows;
+ List<WindowInfo> windows = null;
final List<AccessibilityWindow> visibleWindows = new ArrayList<>();
+ final Point screenSize = new Point();
final int topFocusedDisplayId;
IBinder topFocusedWindowToken = null;
@@ -1639,19 +1642,27 @@ final class AccessibilityController {
return;
}
final Display display = dc.getDisplay();
- display.getRealSize(mTempPoint);
+ display.getRealSize(screenSize);
mA11yWindowsPopulator.populateVisibleWindowsOnScreenLocked(
mDisplayId, visibleWindows);
- windows = buildWindowInfoListLocked(visibleWindows, mTempPoint);
+ if (!com.android.server.accessibility.Flags.computeWindowChangesOnA11y()) {
+ windows = buildWindowInfoListLocked(visibleWindows, screenSize);
+ }
// Gets the top focused display Id and window token for supporting multi-display.
topFocusedDisplayId = mService.mRoot.getTopFocusedDisplayContent().getDisplayId();
topFocusedWindowToken = topFocusedWindowState.mClient.asBinder();
}
- mCallback.onWindowsForAccessibilityChanged(forceSend, topFocusedDisplayId,
- topFocusedWindowToken, windows);
+
+ if (com.android.server.accessibility.Flags.computeWindowChangesOnA11y()) {
+ mCallback.onAccessibilityWindowsChanged(forceSend, topFocusedDisplayId,
+ topFocusedWindowToken, screenSize, visibleWindows);
+ } else {
+ mCallback.onWindowsForAccessibilityChanged(forceSend, topFocusedDisplayId,
+ topFocusedWindowToken, windows);
+ }
// Recycle the windows as we do not need them.
for (final AccessibilityWindowsPopulator.AccessibilityWindow window : visibleWindows) {
@@ -1660,6 +1671,9 @@ final class AccessibilityController {
mInitialized = true;
}
+ // Here are old code paths, called when computeWindowChangesOnA11y flag is disabled.
+ // LINT.IfChange
+
/**
* From a list of windows, decides windows to be exposed to accessibility based on touchable
* region in the screen.
@@ -1819,6 +1833,8 @@ final class AccessibilityController {
&& windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION);
}
+ // LINT.ThenChange(/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java)
+
private WindowState getTopFocusWindow() {
return mService.mRoot.getTopFocusedDisplayContent().mCurrentFocus;
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
index 3cf19ddbd89d..ac3251c9bb12 100644
--- a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
+++ b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
@@ -22,6 +22,7 @@ import static com.android.internal.util.DumpUtils.dumpSparseArray;
import static com.android.server.wm.utils.RegionUtils.forEachRect;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -655,6 +656,7 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
private final Region mTouchableRegionInScreen = new Region();
private final Region mTouchableRegionInWindow = new Region();
private WindowInfo mWindowInfo;
+ private Rect mSystemBarInsetFrame = null;
/**
@@ -718,6 +720,16 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
Slog.w(TAG, "can't find spec");
}
}
+
+ // Compute system bar insets frame if needed.
+ if (com.android.server.accessibility.Flags.computeWindowChangesOnA11y()
+ && windowState != null && instance.isUntouchableNavigationBar()) {
+ final InsetsSourceProvider provider =
+ windowState.getControllableInsetProvider();
+ if (provider != null) {
+ instance.mSystemBarInsetFrame = provider.getSource().getFrame();
+ }
+ }
return instance;
}
@@ -812,6 +824,15 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
return mIsPIPMenu;
}
+ /**
+ * @return the system inset frame size if the window is untouchable navigation bar.
+ * Returns null otherwise.
+ */
+ @Nullable
+ public Rect getSystemBarInsetsFrame() {
+ return mSystemBarInsetFrame;
+ }
+
private static void getTouchableRegionInWindow(boolean shouldMagnify, Region inRegion,
Region outRegion, Rect frame, Matrix inverseMatrix, Matrix displayMatrix) {
// Some modal windows, like the activity with Theme.dialog, has the full screen
diff --git a/services/core/java/com/android/server/wm/ActivityCallerState.java b/services/core/java/com/android/server/wm/ActivityCallerState.java
index 4416605d9f04..fa0b17631b0d 100644
--- a/services/core/java/com/android/server/wm/ActivityCallerState.java
+++ b/services/core/java/com/android/server/wm/ActivityCallerState.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import static android.os.Process.INVALID_UID;
+
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -28,6 +30,7 @@ import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Intent;
import android.net.Uri;
+import android.os.BadParcelableException;
import android.os.IBinder;
import android.os.UserHandle;
import android.util.ArraySet;
@@ -41,6 +44,7 @@ import com.android.server.uri.GrantUri;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.WeakHashMap;
/**
@@ -51,6 +55,9 @@ final class ActivityCallerState {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityCallerState" : TAG_ATM;
// XML tags for CallerInfo
+ private static final String ATTR_CALLER_UID = "caller_uid";
+ private static final String ATTR_CALLER_PACKAGE = "caller_package";
+ private static final String ATTR_CALLER_IS_SHARE_ENABLED = "caller_is_share_enabled";
private static final String TAG_READABLE_CONTENT_URI = "readable_content_uri";
private static final String TAG_WRITABLE_CONTENT_URI = "writable_content_uri";
private static final String TAG_INACCESSIBLE_CONTENT_URI = "inaccessible_content_uri";
@@ -71,12 +78,33 @@ final class ActivityCallerState {
return mCallerTokenInfoMap.getOrDefault(callerToken, null);
}
+ boolean hasCaller(IBinder callerToken) {
+ return getCallerInfoOrNull(callerToken) != null;
+ }
+
+ int getUid(IBinder callerToken) {
+ CallerInfo callerInfo = getCallerInfoOrNull(callerToken);
+ return callerInfo != null ? callerInfo.mUid : INVALID_UID;
+ }
+
+ String getPackage(IBinder callerToken) {
+ CallerInfo callerInfo = getCallerInfoOrNull(callerToken);
+ return callerInfo != null ? callerInfo.mPackageName : null;
+ }
+
+ boolean isShareIdentityEnabled(IBinder callerToken) {
+ CallerInfo callerInfo = getCallerInfoOrNull(callerToken);
+ return callerInfo != null ? callerInfo.mIsShareIdentityEnabled : false;
+ }
+
void add(IBinder callerToken, CallerInfo callerInfo) {
mCallerTokenInfoMap.put(callerToken, callerInfo);
}
- void computeCallerInfo(IBinder callerToken, Intent intent, int callerUid) {
- final CallerInfo callerInfo = new CallerInfo();
+ void computeCallerInfo(IBinder callerToken, Intent intent, int callerUid,
+ String callerPackageName, boolean isCallerShareIdentityEnabled) {
+ final CallerInfo callerInfo = new CallerInfo(callerUid, callerPackageName,
+ isCallerShareIdentityEnabled);
mCallerTokenInfoMap.put(callerToken, callerInfo);
final ArraySet<Uri> contentUris = getContentUrisFromIntent(intent);
@@ -117,8 +145,8 @@ final class ActivityCallerState {
final boolean writeMet = callerInfo.mWritableContentUris.contains(grantUri);
if (!readMet && !writeMet) {
- throw new IllegalArgumentException("The supplied URI wasn't passed at launch: "
- + grantUri.uri.toSafeString());
+ throw new IllegalArgumentException("The supplied URI wasn't passed at launch in"
+ + " #getData, #EXTRA_STREAM, nor #getClipData: " + grantUri.uri.toSafeString());
}
final boolean checkRead = (modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0;
@@ -158,6 +186,18 @@ final class ActivityCallerState {
// getData
addUriIfContentUri(intent.getData(), uris);
+ // EXTRA_STREAM
+ if (intent.hasExtra(Intent.EXTRA_STREAM)) {
+ final ArrayList<Uri> streams = tryToUnparcelArrayListExtraStreamsUri(intent);
+ if (streams == null) {
+ addUriIfContentUri(tryToUnparcelExtraStreamUri(intent), uris);
+ } else {
+ for (int i = streams.size() - 1; i >= 0; i--) {
+ addUriIfContentUri(streams.get(i), uris);
+ }
+ }
+ }
+
final ClipData clipData = intent.getClipData();
if (clipData == null) return uris;
@@ -173,6 +213,33 @@ final class ActivityCallerState {
return uris;
}
+ private static Uri tryToUnparcelExtraStreamUri(Intent intent) {
+ try {
+ return intent.getParcelableExtra(Intent.EXTRA_STREAM, Uri.class);
+ } catch (BadParcelableException e) {
+ // Even though the system "defuses" all the parsed Bundles, i.e. suppresses and logs
+ // instances of {@link BadParcelableException}, we still want to be on the safer side
+ // and catch the exception to ensure no breakages happen. If the unparcel fails, the
+ // item is still preserved with the underlying parcel.
+ Slog.w(TAG, "Failed to unparcel an URI in EXTRA_STREAM, returning null: " + e);
+ return null;
+ }
+ }
+
+ private static ArrayList<Uri> tryToUnparcelArrayListExtraStreamsUri(Intent intent) {
+ try {
+ return intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM, Uri.class);
+ } catch (BadParcelableException e) {
+ // Even though the system "defuses" all the parsed Bundles, i.e. suppresses and logs
+ // instances of {@link BadParcelableException}, we still want to be on the safer side
+ // and catch the exception to ensure no breakages happen. If the unparcel fails, the
+ // item is still preserved with the underlying parcel.
+ Slog.w(TAG, "Failed to unparcel an ArrayList of URIs in EXTRA_STREAM, returning null: "
+ + e);
+ return null;
+ }
+ }
+
private static void addUriIfContentUri(Uri uri, ArraySet<Uri> uris) {
if (uri != null && ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
uris.add(uri);
@@ -180,12 +247,26 @@ final class ActivityCallerState {
}
public static final class CallerInfo {
+ final int mUid;
+ final String mPackageName;
+ final boolean mIsShareIdentityEnabled;
final ArraySet<GrantUri> mReadableContentUris = new ArraySet<>();
final ArraySet<GrantUri> mWritableContentUris = new ArraySet<>();
final ArraySet<GrantUri> mInaccessibleContentUris = new ArraySet<>();
+ CallerInfo(int uid, String packageName, boolean isShareIdentityEnabled) {
+ mUid = uid;
+ mPackageName = packageName;
+ mIsShareIdentityEnabled = isShareIdentityEnabled;
+ }
+
public void saveToXml(TypedXmlSerializer out)
throws IOException, XmlPullParserException {
+ out.attributeInt(null, ATTR_CALLER_UID, mUid);
+ if (mPackageName != null) {
+ out.attribute(null, ATTR_CALLER_PACKAGE, mPackageName);
+ }
+ out.attributeBoolean(null, ATTR_CALLER_IS_SHARE_ENABLED, mIsShareIdentityEnabled);
for (int i = mReadableContentUris.size() - 1; i >= 0; i--) {
saveGrantUriToXml(out, mReadableContentUris.valueAt(i), TAG_READABLE_CONTENT_URI);
}
@@ -202,7 +283,12 @@ final class ActivityCallerState {
public static CallerInfo restoreFromXml(TypedXmlPullParser in)
throws IOException, XmlPullParserException {
- CallerInfo callerInfo = new CallerInfo();
+ int uid = in.getAttributeInt(null, ATTR_CALLER_UID, 0);
+ String packageName = in.getAttributeValue(null, ATTR_CALLER_PACKAGE);
+ boolean isShareIdentityEnabled = in.getAttributeBoolean(null,
+ ATTR_CALLER_IS_SHARE_ENABLED, false);
+
+ CallerInfo callerInfo = new CallerInfo(uid, packageName, isShareIdentityEnabled);
final int outerDepth = in.getDepth();
int event;
while (((event = in.next()) != END_DOCUMENT)
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index efaa467e43e4..ed5df5fab017 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -695,30 +695,57 @@ class ActivityClientController extends IActivityClientController.Stub {
@Override
public int getLaunchedFromUid(IBinder token) {
+ return getUid(token, /* callerToken */ null, /* isActivityCallerCall */ false);
+ }
+
+ @Override
+ public String getLaunchedFromPackage(IBinder token) {
+ return getPackage(token, /* callerToken */ null, /* isActivityCallerCall */ false);
+ }
+
+ @Override
+ public int getActivityCallerUid(IBinder activityToken, IBinder callerToken) {
+ return getUid(activityToken, callerToken, /* isActivityCallerCall */ true);
+ }
+
+ @Override
+ public String getActivityCallerPackage(IBinder activityToken, IBinder callerToken) {
+ return getPackage(activityToken, callerToken, /* isActivityCallerCall */ true);
+ }
+
+ private int getUid(IBinder activityToken, IBinder callerToken, boolean isActivityCallerCall) {
final int uid = Binder.getCallingUid();
final boolean isInternalCaller = isInternalCallerGetLaunchedFrom(uid);
synchronized (mGlobalLock) {
- final ActivityRecord r = ActivityRecord.forTokenLocked(token);
- if (r != null && (isInternalCaller || canGetLaunchedFromLocked(uid, r))) {
- return r.launchedFromUid;
+ final ActivityRecord r = ActivityRecord.forTokenLocked(activityToken);
+ if (r != null && (isInternalCaller || canGetLaunchedFromLocked(uid, r, callerToken,
+ isActivityCallerCall)) && isValidCaller(r, callerToken, isActivityCallerCall)) {
+ return isActivityCallerCall ? r.getCallerUid(callerToken) : r.launchedFromUid;
}
}
return INVALID_UID;
}
- @Override
- public String getLaunchedFromPackage(IBinder token) {
+ private String getPackage(IBinder activityToken, IBinder callerToken,
+ boolean isActivityCallerCall) {
final int uid = Binder.getCallingUid();
final boolean isInternalCaller = isInternalCallerGetLaunchedFrom(uid);
synchronized (mGlobalLock) {
- final ActivityRecord r = ActivityRecord.forTokenLocked(token);
- if (r != null && (isInternalCaller || canGetLaunchedFromLocked(uid, r))) {
- return r.launchedFromPackage;
+ final ActivityRecord r = ActivityRecord.forTokenLocked(activityToken);
+ if (r != null && (isInternalCaller || canGetLaunchedFromLocked(uid, r, callerToken,
+ isActivityCallerCall)) && isValidCaller(r, callerToken, isActivityCallerCall)) {
+ return isActivityCallerCall
+ ? r.getCallerPackage(callerToken) : r.launchedFromPackage;
}
}
return null;
}
+ private boolean isValidCaller(ActivityRecord r, IBinder callerToken,
+ boolean isActivityCallerCall) {
+ return isActivityCallerCall ? r.hasCaller(callerToken) : callerToken == null;
+ }
+
/**
* @param uri This uri must NOT contain an embedded userId.
* @param userId The userId in which the uri is to be resolved.
@@ -768,9 +795,13 @@ class ActivityClientController extends IActivityClientController.Stub {
* verifying whether the provided {@code ActivityRecord r} has opted in to sharing its
* identity or if the uid of the activity matches that of the launching app.
*/
- private static boolean canGetLaunchedFromLocked(int uid, ActivityRecord r) {
+ private static boolean canGetLaunchedFromLocked(int uid, ActivityRecord r,
+ IBinder callerToken, boolean isActivityCallerCall) {
if (CompatChanges.isChangeEnabled(ACCESS_SHARED_IDENTITY, uid)) {
- return r.mShareIdentity || r.launchedFromUid == uid;
+ boolean isShareIdentityEnabled = isActivityCallerCall
+ ? r.isCallerShareIdentityEnabled(callerToken) : r.mShareIdentity;
+ int callerUid = isActivityCallerCall ? r.getCallerUid(callerToken) : r.launchedFromUid;
+ return isShareIdentityEnabled || callerUid == uid;
}
return false;
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 09c329be7d09..d60fe4b7d7e1 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -303,6 +303,7 @@ import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ConstrainDisplayApisConfig;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.UserProperties;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -352,6 +353,7 @@ import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.view.WindowManager.TransitionOldType;
import android.view.animation.Animation;
+import android.window.ActivityWindowInfo;
import android.window.ITaskFragmentOrganizer;
import android.window.RemoteTransition;
import android.window.SizeConfigurationBuckets;
@@ -518,6 +520,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
private int mLastReportedDisplayId;
boolean mLastReportedMultiWindowMode;
boolean mLastReportedPictureInPictureMode;
+ private final ActivityWindowInfo mLastReportedActivityWindowInfo = new ActivityWindowInfo();
ActivityRecord resultTo; // who started this entry, so will get our reply
final String resultWho; // additional identifier for use by resultTo.
final int requestCode; // code given by requester (resultTo)
@@ -957,6 +960,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
*/
private final Configuration mTmpConfig = new Configuration();
private final Rect mTmpBounds = new Rect();
+ private final ActivityWindowInfo mTmpActivityWindowInfo = new ActivityWindowInfo();
// Token for targeting this activity for assist purposes.
final Binder assistToken = new Binder();
@@ -1095,6 +1099,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
pw.println(prefix + "mLastReportedConfigurations:");
mLastReportedConfiguration.dump(pw, prefix + " ");
+ if (Flags.activityWindowInfoFlag()) {
+ pw.print(prefix);
+ pw.print("mLastReportedActivityWindowInfo=");
+ pw.println(mLastReportedActivityWindowInfo);
+ }
+
pw.print(prefix); pw.print("CurrentConfiguration="); pw.println(getConfiguration());
if (!getRequestedOverrideConfiguration().equals(EMPTY)) {
pw.println(prefix + "RequestedOverrideConfiguration="
@@ -1534,11 +1544,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Picture-in-picture mode changes also trigger a multi-window mode change as well, so
// update that here in order. Set the last reported MW state to the same as the PiP
// state since we haven't yet actually resized the task (these callbacks need to
- // precede the configuration change from the resize.
+ // precede the configuration change from the resize.)
mLastReportedPictureInPictureMode = inPictureInPictureMode;
mLastReportedMultiWindowMode = inPictureInPictureMode;
ensureActivityConfiguration(true /* ignoreVisibility */);
- if (inPictureInPictureMode && findMainWindow() == null) {
+ if (inPictureInPictureMode && findMainWindow() == null
+ && task.topRunningActivity() == this) {
// Prevent malicious app entering PiP without valid WindowState, which can in turn
// result a non-touchable PiP window since the InputConsumer for PiP requires it.
EventLog.writeEvent(0x534e4554, "265293293", -1, "");
@@ -1846,20 +1857,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mLetterboxUiController.onMovedToDisplay(mDisplayContent.getDisplayId());
}
- void layoutLetterbox(WindowState winHint) {
- mLetterboxUiController.layoutLetterbox(winHint);
+ void layoutLetterboxIfNeeded(WindowState winHint) {
+ mLetterboxUiController.layoutLetterboxIfNeeded(winHint);
}
boolean hasWallpaperBackgroundForLetterbox() {
return mLetterboxUiController.hasWallpaperBackgroundForLetterbox();
}
- void updateLetterboxSurface(WindowState winHint, Transaction t) {
- mLetterboxUiController.updateLetterboxSurface(winHint, t);
+ void updateLetterboxSurfaceIfNeeded(WindowState winHint, Transaction t) {
+ mLetterboxUiController.updateLetterboxSurfaceIfNeeded(winHint, t);
}
- void updateLetterboxSurface(WindowState winHint) {
- mLetterboxUiController.updateLetterboxSurface(winHint);
+ void updateLetterboxSurfaceIfNeeded(WindowState winHint) {
+ mLetterboxUiController.updateLetterboxSurfaceIfNeeded(winHint);
}
/** Gets the letterbox insets. The insets will be empty if there is no letterbox. */
@@ -2024,12 +2035,31 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
+ boolean hasCaller(IBinder callerToken) {
+ return mCallerState.hasCaller(callerToken);
+ }
+
+ int getCallerUid(IBinder callerToken) {
+ return mCallerState.getUid(callerToken);
+ }
+
+ String getCallerPackage(IBinder callerToken) {
+ return mCallerState.getPackage(callerToken);
+ }
+
+ boolean isCallerShareIdentityEnabled(IBinder callerToken) {
+ return mCallerState.isShareIdentityEnabled(callerToken);
+ }
+
void computeInitialCallerInfo() {
- computeCallerInfo(initialCallerInfoAccessToken, intent, launchedFromUid);
+ computeCallerInfo(initialCallerInfoAccessToken, intent, launchedFromUid,
+ launchedFromPackage, mShareIdentity);
}
- void computeCallerInfo(IBinder callerToken, Intent intent, int callerUid) {
- mCallerState.computeCallerInfo(callerToken, intent, callerUid);
+ void computeCallerInfo(IBinder callerToken, Intent intent, int callerUid,
+ String callerPackageName, boolean isCallerShareIdentityEnabled) {
+ mCallerState.computeCallerInfo(callerToken, intent, callerUid, callerPackageName,
+ isCallerShareIdentityEnabled);
}
boolean checkContentUriPermission(IBinder callerToken, GrantUri grantUri, int modeFlags) {
@@ -3129,6 +3159,30 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
+ /**
+ * This is different from {@link #isEmbedded()}.
+ * {@link #isEmbedded()} is {@code true} when any of the parent {@link TaskFragment} is created
+ * by a {@link android.window.TaskFragmentOrganizer}, while this method is {@code true} when
+ * the parent {@link TaskFragment} is embedded and has bounds override that does not fill the
+ * leaf {@link Task}.
+ */
+ boolean isEmbeddedInHostContainer() {
+ final TaskFragment taskFragment = getOrganizedTaskFragment();
+ return taskFragment != null && taskFragment.isEmbeddedWithBoundsOverride();
+ }
+
+ @NonNull
+ ActivityWindowInfo getActivityWindowInfo() {
+ if (!Flags.activityWindowInfoFlag() || !isAttached()) {
+ return mTmpActivityWindowInfo;
+ }
+ mTmpActivityWindowInfo.set(
+ isEmbeddedInHostContainer(),
+ getTask().getBounds(),
+ getTaskFragment().getBounds());
+ return mTmpActivityWindowInfo;
+ }
+
@Override
@Nullable
TaskDisplayArea getDisplayArea() {
@@ -3526,6 +3580,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mAtmService.mUgmInternal.grantUriPermissionUncheckedFromIntent(resultGrants,
resultTo.getUriPermissionsLocked());
}
+ IBinder callerToken = new Binder();
+ if (android.security.Flags.contentUriPermissionApis()) {
+ try {
+ resultTo.computeCallerInfo(callerToken, resultData, this.getUid(),
+ mAtmService.getPackageManager().getNameForUid(this.getUid()),
+ /* isShareIdentityEnabled */ false);
+ // Result callers cannot share their identity via
+ // {@link ActivityOptions#setShareIdentityEnabled(boolean)} since
+ // {@link android.app.Activity#setResult} doesn't have a
+ // {@link android.os.Bundle}.
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
if (mForceSendResultForMediaProjection || resultTo.isState(RESUMED)) {
// Sending the result to the resultTo activity asynchronously to prevent the
// resultTo activity getting results before this Activity paused.
@@ -3533,12 +3601,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mAtmService.mH.post(() -> {
synchronized (mAtmService.mGlobalLock) {
resultToActivity.sendResult(this.getUid(), resultWho, requestCode,
- resultCode, resultData, resultGrants,
+ resultCode, resultData, callerToken, resultGrants,
mForceSendResultForMediaProjection);
}
});
} else {
- resultTo.addResultLocked(this, resultWho, requestCode, resultCode, resultData);
+ resultTo.addResultLocked(this, resultWho, requestCode, resultCode, resultData,
+ callerToken);
}
resultTo = null;
} else if (DEBUG_RESULTS) {
@@ -4512,7 +4581,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
super.removeChild(child);
checkKeyguardFlagsChanged();
- updateLetterboxSurface(child);
+ updateLetterboxSurfaceIfNeeded(child);
}
void setAppLayoutChanges(int changes, String reason) {
@@ -4822,9 +4891,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
void addResultLocked(ActivityRecord from, String resultWho,
int requestCode, int resultCode,
- Intent resultData) {
+ Intent resultData, IBinder callerToken) {
ActivityResult r = new ActivityResult(from, resultWho,
- requestCode, resultCode, resultData);
+ requestCode, resultCode, resultData, callerToken);
if (results == null) {
results = new ArrayList<ResultInfo>();
}
@@ -4850,13 +4919,27 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
void sendResult(int callingUid, String resultWho, int requestCode, int resultCode,
- Intent data, NeededUriGrants dataGrants) {
- sendResult(callingUid, resultWho, requestCode, resultCode, data, dataGrants,
+ Intent data, IBinder callerToken, NeededUriGrants dataGrants) {
+ sendResult(callingUid, resultWho, requestCode, resultCode, data, callerToken, dataGrants,
false /* forceSendForMediaProjection */);
}
void sendResult(int callingUid, String resultWho, int requestCode, int resultCode,
- Intent data, NeededUriGrants dataGrants, boolean forceSendForMediaProjection) {
+ Intent data, IBinder callerToken, NeededUriGrants dataGrants,
+ boolean forceSendForMediaProjection) {
+ if (android.security.Flags.contentUriPermissionApis()
+ && !mCallerState.hasCaller(callerToken)) {
+ try {
+ computeCallerInfo(callerToken, data, callingUid,
+ mAtmService.getPackageManager().getNameForUid(callingUid),
+ false /* isShareIdentityEnabled */);
+ // Result callers cannot share their identity via
+ // {@link ActivityOptions#setShareIdentityEnabled(boolean)} since
+ // {@link android.app.Activity#setResult} doesn't have a {@link android.os.Bundle}.
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
if (callingUid > 0) {
mAtmService.mUgmInternal.grantUriPermissionUncheckedFromIntent(dataGrants,
getUriPermissionsLocked());
@@ -4872,7 +4955,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (isState(RESUMED) && attachedToProcess()) {
try {
final ArrayList<ResultInfo> list = new ArrayList<>();
- list.add(new ResultInfo(resultWho, requestCode, resultCode, data));
+ list.add(new ResultInfo(resultWho, requestCode, resultCode, data, callerToken));
mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
ActivityResultItem.obtain(token, list));
return;
@@ -4886,7 +4969,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
STOPPING, STOPPED)) {
// Build result to be returned immediately.
final ActivityResultItem activityResultItem = ActivityResultItem.obtain(
- token, List.of(new ResultInfo(resultWho, requestCode, resultCode, data)));
+ token, List.of(new ResultInfo(resultWho, requestCode, resultCode, data,
+ callerToken)));
// When the activity result is delivered, the activity will transition to RESUMED.
// Since the activity is only resumed so the result can be immediately delivered,
// return it to its original lifecycle state.
@@ -4910,7 +4994,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return;
}
- addResultLocked(null /* from */, resultWho, requestCode, resultCode, data);
+ addResultLocked(null /* from */, resultWho, requestCode, resultCode, data, callerToken);
}
/**
@@ -4960,11 +5044,21 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* method will be called at the proper time.
*/
final void deliverNewIntentLocked(int callingUid, Intent intent, NeededUriGrants intentGrants,
- String referrer) {
+ String referrer, boolean isShareIdentityEnabled, int userId, int recipientAppId) {
+ IBinder callerToken = new Binder();
+ if (android.security.Flags.contentUriPermissionApis()) {
+ computeCallerInfo(callerToken, intent, callingUid, referrer, isShareIdentityEnabled);
+ }
// The activity now gets access to the data associated with this Intent.
mAtmService.mUgmInternal.grantUriPermissionUncheckedFromIntent(intentGrants,
getUriPermissionsLocked());
- final ReferrerIntent rintent = new ReferrerIntent(intent, getFilteredReferrer(referrer));
+ if (isShareIdentityEnabled && android.security.Flags.contentUriPermissionApis()) {
+ final PackageManagerInternal pmInternal = mAtmService.getPackageManagerInternalLocked();
+ pmInternal.grantImplicitAccess(userId, intent, recipientAppId /*recipient*/,
+ callingUid /*visible*/, true /*direct*/);
+ }
+ final ReferrerIntent rintent = new ReferrerIntent(intent, getFilteredReferrer(referrer),
+ callerToken);
boolean unsent = true;
final boolean isTopActivityWhileSleeping = isTopRunningActivity() && isSleeping();
@@ -5982,7 +6076,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (destroyedSomething) {
final DisplayContent dc = getDisplayContent();
dc.assignWindowLayers(true /*setLayoutNeeded*/);
- updateLetterboxSurface(null);
+ updateLetterboxSurfaceIfNeeded(null);
}
}
@@ -7634,7 +7728,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
if (mNeedsLetterboxedAnimation) {
- updateLetterboxSurface(findMainWindow(), t);
+ updateLetterboxSurfaceIfNeeded(findMainWindow(), t);
mNeedsAnimationBoundsLayer = true;
}
@@ -7802,7 +7896,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mNeedsAnimationBoundsLayer = false;
if (mNeedsLetterboxedAnimation) {
mNeedsLetterboxedAnimation = false;
- updateLetterboxSurface(findMainWindow(), t);
+ updateLetterboxSurfaceIfNeeded(findMainWindow(), t);
}
if (mAnimatingActivityRegistry != null) {
@@ -8152,6 +8246,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mLastReportedConfiguration.setConfiguration(global, override);
}
+ void setLastReportedActivityWindowInfo(@NonNull ActivityWindowInfo activityWindowInfo) {
+ if (Flags.activityWindowInfoFlag()) {
+ mLastReportedActivityWindowInfo.set(activityWindowInfo);
+ }
+ }
+
@Nullable
CompatDisplayInsets getCompatDisplayInsets() {
if (mLetterboxUiController.hasInheritedLetterboxBehavior()) {
@@ -8889,6 +8989,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
+ // Fixed orientation bounds are the same as its parent container, so clear the fixed
+ // orientation bounds. This can happen in close to square displays where the orientation
+ // is not respected with insets, but the display still matches or is less than the
+ // activity aspect ratio.
+ if (resolvedBounds.equals(parentBounds)) {
+ resolvedBounds.set(prevResolvedBounds);
+ return;
+ }
+
// Calculate app bounds using fixed orientation bounds because they will be needed later
// for comparison with size compat app bounds in {@link resolveSizeCompatModeConfiguration}.
getTaskFragment().computeConfigResourceOverrides(getResolvedOverrideConfiguration(),
diff --git a/services/core/java/com/android/server/wm/ActivityResult.java b/services/core/java/com/android/server/wm/ActivityResult.java
index f2510de6dfb2..c5c51b50961c 100644
--- a/services/core/java/com/android/server/wm/ActivityResult.java
+++ b/services/core/java/com/android/server/wm/ActivityResult.java
@@ -18,16 +18,17 @@ package com.android.server.wm;
import android.app.ResultInfo;
import android.content.Intent;
+import android.os.IBinder;
/**
* Pending result information to send back to an activity.
*/
final class ActivityResult extends ResultInfo {
final ActivityRecord mFrom;
-
+
public ActivityResult(ActivityRecord from, String resultWho,
- int requestCode, int resultCode, Intent data) {
- super(resultWho, requestCode, resultCode, data);
+ int requestCode, int resultCode, Intent data, IBinder callerToken) {
+ super(resultWho, requestCode, resultCode, data, callerToken);
mFrom = from;
}
}
diff --git a/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java b/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java
index db27f607c867..01d077a5bc55 100644
--- a/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java
+++ b/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java
@@ -22,12 +22,10 @@ import static com.android.server.wm.ActivityStarter.ASM_RESTRICTIONS;
import android.annotation.NonNull;
import android.app.compat.CompatChanges;
-import android.content.pm.PackageManager;
import android.provider.DeviceConfig;
import com.android.internal.annotations.GuardedBy;
-import java.util.HashSet;
import java.util.concurrent.Executor;
/**
@@ -50,74 +48,49 @@ class ActivitySecurityModelFeatureFlags {
private static final String KEY_ASM_RESTRICTIONS_ENABLED = KEY_ASM_PREFIX
+ "asm_restrictions_enabled";
private static final String KEY_ASM_TOASTS_ENABLED = KEY_ASM_PREFIX + "asm_toasts_enabled";
- private static final String KEY_ASM_EXEMPTED_PACKAGES = KEY_ASM_PREFIX
- + "asm_exempted_packages";
+
private static final int VALUE_DISABLE = 0;
private static final int VALUE_ENABLE_FOR_V = 1;
private static final int VALUE_ENABLE_FOR_ALL = 2;
private static final int DEFAULT_VALUE = VALUE_DISABLE;
- private static final String DEFAULT_EXCEPTION_LIST = "";
private static int sAsmToastsEnabled;
private static int sAsmRestrictionsEnabled;
- private static final HashSet<String> sExcludedPackageNames = new HashSet<>();
- private static PackageManager sPm;
@GuardedBy("ActivityTaskManagerService.mGlobalLock")
- static void initialize(@NonNull Executor executor, @NonNull PackageManager pm) {
+ static void initialize(@NonNull Executor executor) {
updateFromDeviceConfig();
DeviceConfig.addOnPropertiesChangedListener(NAMESPACE, executor,
properties -> updateFromDeviceConfig());
- sPm = pm;
}
@GuardedBy("ActivityTaskManagerService.mGlobalLock")
static boolean shouldShowToast(int uid) {
- return flagEnabledForUid(sAsmToastsEnabled, uid);
+ return sAsmToastsEnabled == VALUE_ENABLE_FOR_ALL
+ || (sAsmToastsEnabled == VALUE_ENABLE_FOR_V
+ && CompatChanges.isChangeEnabled(ASM_RESTRICTIONS, uid));
}
@GuardedBy("ActivityTaskManagerService.mGlobalLock")
static boolean shouldRestrictActivitySwitch(int uid) {
- return flagEnabledForUid(sAsmRestrictionsEnabled, uid);
- }
-
- private static boolean flagEnabledForUid(int flag, int uid) {
- boolean flagEnabled = flag == VALUE_ENABLE_FOR_ALL
- || (flag == VALUE_ENABLE_FOR_V
- && CompatChanges.isChangeEnabled(ASM_RESTRICTIONS, uid));
-
- if (flagEnabled) {
- String[] packageNames = sPm.getPackagesForUid(uid);
- if (packageNames == null) {
- return true;
- }
- for (int i = 0; i < packageNames.length; i++) {
- if (sExcludedPackageNames.contains(packageNames[i])) {
- return false;
- }
- }
- return true;
+ if (android.security.Flags.asmRestrictionsEnabled()) {
+ return CompatChanges.isChangeEnabled(ASM_RESTRICTIONS, uid)
+ || asmRestrictionsEnabledForAll();
}
return false;
}
+ @GuardedBy("ActivityTaskManagerService.mGlobalLock")
+ static boolean asmRestrictionsEnabledForAll() {
+ return sAsmRestrictionsEnabled == VALUE_ENABLE_FOR_ALL;
+ }
+
private static void updateFromDeviceConfig() {
sAsmToastsEnabled = DeviceConfig.getInt(NAMESPACE, KEY_ASM_TOASTS_ENABLED,
DEFAULT_VALUE);
sAsmRestrictionsEnabled = DeviceConfig.getInt(NAMESPACE, KEY_ASM_RESTRICTIONS_ENABLED,
DEFAULT_VALUE);
-
- String rawExceptionList = DeviceConfig.getString(NAMESPACE,
- KEY_ASM_EXEMPTED_PACKAGES, DEFAULT_EXCEPTION_LIST);
- sExcludedPackageNames.clear();
- String[] packages = rawExceptionList.split(",");
- for (String packageName : packages) {
- String packageNameTrimmed = packageName.trim();
- if (!packageNameTrimmed.isEmpty()) {
- sExcludedPackageNames.add(packageNameTrimmed);
- }
- }
}
}
diff --git a/services/core/java/com/android/server/wm/ActivitySnapshotCache.java b/services/core/java/com/android/server/wm/ActivitySnapshotCache.java
index a54dd826b5bb..3609837f417b 100644
--- a/services/core/java/com/android/server/wm/ActivitySnapshotCache.java
+++ b/services/core/java/com/android/server/wm/ActivitySnapshotCache.java
@@ -23,18 +23,20 @@ import android.window.TaskSnapshot;
*/
class ActivitySnapshotCache extends SnapshotCache<ActivityRecord> {
- ActivitySnapshotCache(WindowManagerService service) {
- super(service, "Activity");
+ ActivitySnapshotCache() {
+ super("Activity");
}
@Override
void putSnapshot(ActivityRecord ar, TaskSnapshot snapshot) {
final int hasCode = System.identityHashCode(ar);
- final CacheEntry entry = mRunningCache.get(hasCode);
- if (entry != null) {
- mAppIdMap.remove(entry.topApp);
+ synchronized (mLock) {
+ final CacheEntry entry = mRunningCache.get(hasCode);
+ if (entry != null) {
+ mAppIdMap.remove(entry.topApp);
+ }
+ mAppIdMap.put(ar, hasCode);
+ mRunningCache.put(hasCode, new CacheEntry(snapshot, ar));
}
- mAppIdMap.put(ar, hasCode);
- mRunningCache.put(hasCode, new CacheEntry(snapshot, ar));
}
}
diff --git a/services/core/java/com/android/server/wm/ActivitySnapshotController.java b/services/core/java/com/android/server/wm/ActivitySnapshotController.java
index 1f013b913283..62fb4bfc74d7 100644
--- a/services/core/java/com/android/server/wm/ActivitySnapshotController.java
+++ b/services/core/java/com/android/server/wm/ActivitySnapshotController.java
@@ -21,6 +21,7 @@ import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.graphics.Rect;
import android.os.Environment;
import android.os.SystemProperties;
import android.os.Trace;
@@ -102,7 +103,7 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord
Environment::getDataSystemCeDirectory);
mPersister = new TaskSnapshotPersister(persistQueue, mPersistInfoProvider);
mSnapshotLoader = new AppSnapshotLoader(mPersistInfoProvider);
- initialize(new ActivitySnapshotCache(service));
+ initialize(new ActivitySnapshotCache());
final boolean snapshotEnabled =
!service.mContext
@@ -617,6 +618,12 @@ class ActivitySnapshotController extends AbsAppSnapshotController<ActivityRecord
return mPersistInfoProvider.use16BitFormat();
}
+ @Override
+ protected Rect getLetterboxInsets(ActivityRecord topActivity) {
+ // Do not capture letterbox for ActivityRecord
+ return Letterbox.EMPTY_RECT;
+ }
+
@NonNull
private SparseArray<UserSavedFile> getUserFiles(int userId) {
if (mUserSavedFiles.get(userId) == null) {
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 2c492035140b..0e401ebc94b5 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -434,6 +434,9 @@ public class ActivityStartController {
// Don't modify the client's object!
intent = new Intent(intent);
+ // Remove existing mismatch flag so it can be properly updated later
+ intent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
+
// Collect information about the target of the Intent.
ActivityInfo aInfo = mSupervisor.resolveActivity(intent, resolvedTypes[i],
0 /* startFlags */, null /* profilerInfo */, userId, filterCallingUid,
@@ -559,11 +562,14 @@ public class ActivityStartController {
@Nullable IBinder errorCallbackToken) {
final ActivityRecord caller =
resultTo != null ? ActivityRecord.forTokenLocked(resultTo) : null;
+ final String resolvedType =
+ activityIntent.resolveTypeIfNeeded(mService.mContext.getContentResolver());
return obtainStarter(activityIntent, "startActivityInTaskFragment")
.setActivityOptions(activityOptions)
.setInTaskFragment(taskFragment)
.setResultTo(resultTo)
.setRequestCode(-1)
+ .setResolvedType(resolvedType)
.setCallingUid(callingUid)
.setCallingPid(callingPid)
.setRealCallingUid(callingUid)
@@ -639,6 +645,10 @@ public class ActivityStartController {
return mPendingRemoteAnimationRegistry;
}
+ ActivityRecord getLastStartActivity() {
+ return mLastStarter != null ? mLastStarter.mStartActivity : null;
+ }
+
void dumpLastHomeActivityStartResult(PrintWriter pw, String prefix) {
pw.print(prefix);
pw.print("mLastHomeActivityStartResult=");
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 07afa5fc21be..066d26222fbd 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -414,6 +414,7 @@ class ActivityStarter {
int filterCallingUid;
PendingIntentRecord originatingPendingIntent;
BackgroundStartPrivileges forcedBalByPiSender;
+ boolean freezeScreen;
final StringBuilder logMessage = new StringBuilder();
@@ -477,6 +478,7 @@ class ActivityStarter {
filterCallingUid = UserHandle.USER_NULL;
originatingPendingIntent = null;
forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ freezeScreen = false;
errorCallbackToken = null;
}
@@ -520,6 +522,7 @@ class ActivityStarter {
filterCallingUid = request.filterCallingUid;
originatingPendingIntent = request.originatingPendingIntent;
forcedBalByPiSender = request.forcedBalByPiSender;
+ freezeScreen = request.freezeScreen;
errorCallbackToken = request.errorCallbackToken;
}
@@ -713,9 +716,14 @@ class ActivityStarter {
try {
onExecutionStarted();
- // Refuse possible leaked file descriptors
- if (mRequest.intent != null && mRequest.intent.hasFileDescriptors()) {
- throw new IllegalArgumentException("File descriptors passed in Intent");
+ if (mRequest.intent != null) {
+ // Refuse possible leaked file descriptors
+ if (mRequest.intent.hasFileDescriptors()) {
+ throw new IllegalArgumentException("File descriptors passed in Intent");
+ }
+
+ // Remove existing mismatch flag so it can be properly updated later
+ mRequest.intent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
}
final LaunchingState launchingState;
@@ -1103,7 +1111,7 @@ class ActivityStarter {
if (err != START_SUCCESS) {
if (resultRecord != null) {
resultRecord.sendResult(INVALID_UID, resultWho, requestCode, RESULT_CANCELED,
- null /* data */, null /* dataGrants */);
+ null /* data */, null /* callerToken */, null /* dataGrants */);
}
SafeActivityOptions.abort(options);
return err;
@@ -1128,7 +1136,8 @@ class ActivityStarter {
.filterAppAccess(targetPackageName, callingUid, userId)) {
if (resultRecord != null) {
resultRecord.sendResult(INVALID_UID, resultWho, requestCode,
- RESULT_CANCELED, null /* data */, null /* dataGrants */);
+ RESULT_CANCELED, null /* data */, null /* callerToken */,
+ null /* dataGrants */);
}
SafeActivityOptions.abort(options);
return ActivityManager.START_CLASS_NOT_FOUND;
@@ -1216,7 +1225,7 @@ class ActivityStarter {
if (abort) {
if (resultRecord != null) {
resultRecord.sendResult(INVALID_UID, resultWho, requestCode, RESULT_CANCELED,
- null /* data */, null /* dataGrants */);
+ null /* data */, null /* callerToken */, null /* dataGrants */);
}
// We pretend to the caller that it was really started, but they will just get a
// cancel result.
@@ -1380,7 +1389,7 @@ class ActivityStarter {
int requestCode = r.requestCode;
if (resultRecord != null) {
resultRecord.sendResult(INVALID_UID, resultWho, requestCode, RESULT_CANCELED,
- null /* data */, null /* dataGrants */);
+ null /* data */, null /* callerToken */, null /* dataGrants */);
}
// We pretend to the caller that it was really started to make it backward compatible, but
// they will just get a cancel result.
@@ -1519,6 +1528,18 @@ class ActivityStarter {
Transition newTransition = transitionController.isShellTransitionsEnabled()
? transitionController.createAndStartCollecting(TRANSIT_OPEN) : null;
RemoteTransition remoteTransition = r.takeRemoteTransition();
+ // Create a display snapshot as soon as possible.
+ if (newTransition != null && mRequest.freezeScreen) {
+ final TaskDisplayArea tda = mLaunchParams.hasPreferredTaskDisplayArea()
+ ? mLaunchParams.mPreferredTaskDisplayArea
+ : mRootWindowContainer.getDefaultTaskDisplayArea();
+ final DisplayContent dc = mRootWindowContainer.getDisplayContentOrCreate(
+ tda.getDisplayId());
+ if (dc != null) {
+ transitionController.collect(dc);
+ transitionController.collectVisibleChange(dc);
+ }
+ }
try {
mService.deferWindowLayout();
transitionController.collect(r);
@@ -1727,7 +1748,7 @@ class ActivityStarter {
if (startResult != START_SUCCESS) {
if (r.resultTo != null) {
r.resultTo.sendResult(INVALID_UID, r.resultWho, r.requestCode, RESULT_CANCELED,
- null /* data */, null /* dataGrants */);
+ null /* data */, null /* callerToken */, null /* dataGrants */);
}
return startResult;
}
@@ -2226,7 +2247,7 @@ class ActivityStarter {
if (mStartActivity.resultTo != null) {
mStartActivity.resultTo.sendResult(INVALID_UID, mStartActivity.resultWho,
mStartActivity.requestCode, RESULT_CANCELED,
- null /* data */, null /* dataGrants */);
+ null /* data */, null /* callerToken */, null /* dataGrants */);
mStartActivity.resultTo = null;
}
@@ -2607,7 +2628,7 @@ class ActivityStarter {
Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
mStartActivity.resultTo.sendResult(INVALID_UID, mStartActivity.resultWho,
mStartActivity.requestCode, RESULT_CANCELED,
- null /* data */, null /* dataGrants */);
+ null /* data */, null /* callerToken */, null /* dataGrants */);
mStartActivity.resultTo = null;
}
}
@@ -2909,7 +2930,9 @@ class ActivityStarter {
activity.logStartActivity(EventLogTags.WM_NEW_INTENT, activity.getTask());
activity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, intentGrants,
- mStartActivity.launchedFromPackage);
+ mStartActivity.launchedFromPackage, mStartActivity.mShareIdentity,
+ mStartActivity.mUserId,
+ UserHandle.getAppId(mStartActivity.info.applicationInfo.uid));
mIntentDelivered = true;
}
@@ -3262,6 +3285,11 @@ class ActivityStarter {
return this;
}
+ ActivityStarter setFreezeScreen(boolean freezeScreen) {
+ mRequest.freezeScreen = freezeScreen;
+ return this;
+ }
+
ActivityStarter setErrorCallbackToken(@Nullable IBinder errorCallbackToken) {
mRequest.errorCallbackToken = errorCallbackToken;
return this;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index ed556a55010e..4a5b2211800c 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -275,6 +275,19 @@ public abstract class ActivityTaskManagerInternal {
int startFlags, @Nullable Bundle options, int userId);
/**
+ * Start activity {@code intent} with initially under screenshot. The screen of launching
+ * display will be frozen before transition occur.
+ *
+ * - DO NOT call it with the calling UID cleared.
+ * - The caller must do the calling user ID check.
+ *
+ * @return error codes used by {@link IActivityManager#startActivity} and its siblings.
+ */
+ public abstract int startActivityWithScreenshot(@NonNull Intent intent,
+ @NonNull String callingPackage, int callingUid, int callingPid,
+ @Nullable IBinder resultTo, @Nullable Bundle options, int userId);
+
+ /**
* Called after virtual display Id is updated by
* {@link com.android.server.vr.Vr2dDisplay} with a specific
* {@param vr2dDisplayId}.
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 0e6c06dad486..514a3d87ecf0 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -102,6 +102,9 @@ import static com.android.server.wm.ActivityInterceptorCallback.MAINLINE_FIRST_O
import static com.android.server.wm.ActivityInterceptorCallback.MAINLINE_LAST_ORDERED_ID;
import static com.android.server.wm.ActivityInterceptorCallback.SYSTEM_FIRST_ORDERED_ID;
import static com.android.server.wm.ActivityInterceptorCallback.SYSTEM_LAST_ORDERED_ID;
+import static com.android.server.wm.ActivityRecord.State.DESTROYED;
+import static com.android.server.wm.ActivityRecord.State.DESTROYING;
+import static com.android.server.wm.ActivityRecord.State.FINISHING;
import static com.android.server.wm.ActivityRecord.State.PAUSING;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
@@ -881,7 +884,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
mTaskSupervisor.onSystemReady();
mActivityClientController.onSystemReady();
// TODO(b/258792202) Cleanup once ASM is ready to launch
- ActivitySecurityModelFeatureFlags.initialize(mContext.getMainExecutor(), pm);
+ ActivitySecurityModelFeatureFlags.initialize(mContext.getMainExecutor());
mGrammaticalManagerInternal = LocalServices.getService(
GrammaticalInflectionManagerInternal.class);
}
@@ -1134,22 +1137,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
* Return the global configuration used by the process corresponding to the input pid. This is
* usually the global configuration with some overrides specific to that process.
*/
- Configuration getGlobalConfigurationForCallingPid() {
+ private Configuration getGlobalConfigurationForCallingPid() {
final int pid = Binder.getCallingPid();
- return getGlobalConfigurationForPid(pid);
- }
-
- /**
- * Return the global configuration used by the process corresponding to the given pid.
- */
- Configuration getGlobalConfigurationForPid(int pid) {
if (pid == MY_PID || pid < 0) {
return getGlobalConfiguration();
}
- synchronized (mGlobalLock) {
- final WindowProcessController app = mProcessMap.getProcess(pid);
- return app != null ? app.getConfiguration() : getGlobalConfiguration();
- }
+ final WindowProcessController app = mProcessMap.getProcess(pid);
+ return app != null ? app.getConfiguration() : getGlobalConfiguration();
}
/**
@@ -1317,9 +1311,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
IBinder allowlistToken, Intent fillInIntent, String resolvedType, IBinder resultTo,
String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle bOptions) {
enforceNotIsolatedCaller("startActivityIntentSender");
- // Refuse possible leaked file descriptors
- if (fillInIntent != null && fillInIntent.hasFileDescriptors()) {
- throw new IllegalArgumentException("File descriptors passed in Intent");
+ if (fillInIntent != null) {
+ // Refuse possible leaked file descriptors
+ if (fillInIntent.hasFileDescriptors()) {
+ throw new IllegalArgumentException("File descriptors passed in Intent");
+ }
+ // Remove existing mismatch flag so it can be properly updated later
+ fillInIntent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
}
if (!(target instanceof PendingIntentRecord)) {
@@ -1363,6 +1361,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
return false;
}
intent = new Intent(intent);
+ // Remove existing mismatch flag so it can be properly updated later
+ intent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
// The caller is not allowed to change the data.
intent.setDataAndType(r.intent.getData(), r.intent.getType());
// And we are resetting to find the next component...
@@ -2505,6 +2505,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
userId = handleIncomingUser(Binder.getCallingPid(), callingUid, userId, "getRecentTasks");
final boolean allowed = isGetTasksAllowed("getRecentTasks", Binder.getCallingPid(),
callingUid);
+ if (!mAmInternal.isUserRunning(userId, ActivityManager.FLAG_AND_UNLOCKED)) {
+ Slog.i(TAG, "User " + userId + " is locked. Cannot load recents");
+ return ParceledListSlice.emptyList();
+ }
+ mRecentTasks.loadRecentTasksIfNeeded(userId);
synchronized (mGlobalLock) {
return mRecentTasks.getRecentTasks(maxNum, flags, allowed, userId, callingUid);
}
@@ -3524,10 +3529,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
final long callingIdentity = Binder.clearCallingIdentity();
try {
- hasRestrictedWindow = displayContent.forAllWindows(windowState -> {
- return windowState.isOnScreen() && UserManager.isUserTypePrivateProfile(
- getUserManager().getProfileType(windowState.mShowUserId));
- }, true /* traverseTopToBottom */);
+ hasRestrictedWindow = displayContent.forAllWindows(
+ windowState -> windowState.isOnScreen() && (
+ UserManager.isUserTypePrivateProfile(
+ getUserManager().getProfileType(windowState.mShowUserId))
+ || hasUserRestriction(
+ UserManager.DISALLOW_ASSIST_CONTENT,
+ windowState.mShowUserId)), true /* traverseTopToBottom */);
} finally {
Binder.restoreCallingIdentity(callingIdentity);
}
@@ -4164,7 +4172,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
task = mRootWindowContainer.getDefaultTaskDisplayArea().getRootTask(
t -> t.isActivityTypeStandard());
}
- if (task != null && task.getTopMostActivity() != null) {
+ if (task != null && task.getTopMostActivity() != null
+ && !task.getTopMostActivity().isState(FINISHING, DESTROYING, DESTROYED)) {
mWindowManager.mAtmService.mActivityClientController.onPictureInPictureUiStateChanged(
task.getTopMostActivity(), pipState);
}
@@ -5551,7 +5560,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
* Saves the current activity manager state and includes the saved state in the next dump of
* activity manager.
*/
- void saveANRState(String reason) {
+ void saveANRState(ActivityRecord activity, String reason) {
final StringWriter sw = new StringWriter();
final PrintWriter pw = new FastPrintWriter(sw, false, 1024);
pw.println(" ANR time: " + DateFormat.getDateTimeInstance().format(new Date()));
@@ -5559,14 +5568,25 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
pw.println(" Reason: " + reason);
}
pw.println();
- getActivityStartController().dump(pw, " ", null);
- pw.println();
+ if (activity != null) {
+ final Task rootTask = activity.getRootTask();
+ if (rootTask != null) {
+ rootTask.forAllTaskFragments(
+ tf -> tf.dumpInner(" ", pw, true /* dumpAll */, null /* dumpPackage */));
+ pw.println();
+ }
+ mActivityStartController.dump(pw, " ", activity.packageName);
+ if (mActivityStartController.getLastStartActivity() != activity) {
+ activity.dump(pw, " ", true /* dumpAll */);
+ }
+ }
+ ActivityTaskSupervisor.printThisActivity(pw, mRootWindowContainer.getTopResumedActivity(),
+ null /* dumpPackage */, INVALID_DISPLAY, true /* needSep */,
+ " ResumedActivity: ", /* header= */ null /* header */);
+ mLockTaskController.dump(pw, " ");
+ mKeyguardController.dump(pw, " ");
pw.println("-------------------------------------------------------------------"
+ "------------");
- dumpActivitiesLocked(null /* fd */, pw, null /* args */, 0 /* opti */,
- true /* dumpAll */, false /* dumpClient */, null /* dumpPackage */,
- INVALID_DISPLAY, "" /* header */);
- pw.println();
pw.close();
mLastANRState = sw.toString();
@@ -5999,6 +6019,24 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
false /*validateIncomingUser*/);
}
+ @Override
+ public int startActivityWithScreenshot(@NonNull Intent intent,
+ @NonNull String callingPackage, int callingUid, int callingPid,
+ @Nullable IBinder resultTo, @Nullable Bundle options, int userId) {
+ return getActivityStartController()
+ .obtainStarter(intent, "startActivityWithScreenshot")
+ .setCallingUid(callingUid)
+ .setCallingPid(callingPid)
+ .setCallingPackage(callingPackage)
+ .setResultTo(resultTo)
+ .setActivityOptions(createSafeActivityOptionsWithBalAllowed(options))
+ .setRealCallingUid(Binder.getCallingUid())
+ .setUserId(userId)
+ .setBackgroundStartPrivileges(BackgroundStartPrivileges.ALLOW_BAL)
+ .setFreezeScreen(true)
+ .execute();
+ }
+
/**
* Called after virtual display Id is updated by
* {@link com.android.server.vr.Vr2dDisplay} with a specific
@@ -6334,7 +6372,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final NeededUriGrants dataGrants = collectGrants(data, r);
synchronized (mGlobalLock) {
- r.sendResult(callingUid, resultWho, requestCode, resultCode, data, dataGrants);
+ r.sendResult(callingUid, resultWho, requestCode, resultCode, data, new Binder(),
+ dataGrants);
}
}
@@ -7022,11 +7061,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
@Override
public void loadRecentTasksForUser(int userId) {
- synchronized (mGlobalLock) {
- mRecentTasks.loadUserRecentsLocked(userId);
- // TODO renaming the methods(?)
- mPackageConfigPersister.loadUserPackages(userId);
- }
+ // This runs on android.fg thread when the user is unlocking.
+ mRecentTasks.loadRecentTasksIfNeeded(userId);
+ mPackageConfigPersister.loadUserPackages(userId);
}
@Override
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 49df39664b1c..e0faddf171ca 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -138,6 +138,7 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.Display;
+import android.window.ActivityWindowInfo;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -909,6 +910,9 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
final Configuration overrideConfig = r.getMergedOverrideConfiguration();
r.setLastReportedConfiguration(procConfig, overrideConfig);
+ final ActivityWindowInfo activityWindowInfo = r.getActivityWindowInfo();
+ r.setLastReportedActivityWindowInfo(activityWindowInfo);
+
logIfTransactionTooLarge(r.intent, r.getSavedState());
final TaskFragment organizedTaskFragment = r.getOrganizedTaskFragment();
@@ -931,7 +935,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
results, newIntents, r.takeSceneTransitionInfo(), isTransitionForward,
proc.createProfilerInfoIfNeeded(), r.assistToken, activityClientController,
r.shareableActivityToken, r.getLaunchedFromBubble(), fragmentToken,
- r.initialCallerInfoAccessToken);
+ r.initialCallerInfoAccessToken, activityWindowInfo);
// Set desired final state.
final ActivityLifecycleItem lifecycleItem;
@@ -1118,7 +1122,8 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
|| actionRestriction == ACTIVITY_RESTRICTION_PERMISSION) {
if (resultRecord != null) {
resultRecord.sendResult(INVALID_UID, resultWho, requestCode,
- Activity.RESULT_CANCELED, null /* data */, null /* dataGrants */);
+ Activity.RESULT_CANCELED, null /* data */, null /* callerToken */,
+ null /* dataGrants */);
}
final String msg;
if (actionRestriction == ACTIVITY_RESTRICTION_PERMISSION) {
diff --git a/services/core/java/com/android/server/wm/AnrController.java b/services/core/java/com/android/server/wm/AnrController.java
index b9f6e177e637..0013c5c63798 100644
--- a/services/core/java/com/android/server/wm/AnrController.java
+++ b/services/core/java/com/android/server/wm/AnrController.java
@@ -367,7 +367,7 @@ class AnrController {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "dumpAnrStateLocked()");
synchronized (mService.mGlobalLock) {
mService.saveANRStateLocked(activity, windowState, reason);
- mService.mAtmService.saveANRState(reason);
+ mService.mAtmService.saveANRState(activity, reason);
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 939cf1ae471b..1a63f14e1b8c 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -137,7 +137,6 @@ import android.view.animation.TranslateAnimation;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.TransitionAnimation;
-import com.android.internal.protolog.ProtoLogImpl;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.DumpUtils.Dump;
import com.android.internal.util.function.pooled.PooledLambda;
@@ -248,7 +247,7 @@ public class AppTransition implements Dump {
mHandler = new Handler(service.mH.getLooper());
mDisplayContent = displayContent;
mTransitionAnimation = new TransitionAnimation(
- context, ProtoLogImpl.isEnabled(WM_DEBUG_ANIM), TAG);
+ context, ProtoLog.isEnabled(WM_DEBUG_ANIM), TAG);
mGridLayoutRecentsEnabled = SystemProperties.getBoolean("ro.recents.grid", false);
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index c2dfa21016fa..b51f89931128 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -35,6 +35,7 @@ import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_PREDICT_BACK;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.ResourceId;
import android.graphics.Point;
import android.graphics.Rect;
@@ -629,7 +630,7 @@ class BackNavigationController {
final ActivityRecord ar = openApps.valueAt(i);
if (mAnimationHandler.isTarget(ar, true /* open */)) {
openApps.removeAt(i);
- mAnimationHandler.markStartingSurfaceMatch();
+ mAnimationHandler.markStartingSurfaceMatch(null /* reparentTransaction */);
}
}
for (int i = closeApps.size() - 1; i >= 0; --i) {
@@ -773,10 +774,15 @@ class BackNavigationController {
for (int i = mTmpOpenApps.size() - 1; i >= 0; --i) {
final WindowContainer wc = mTmpOpenApps.get(i);
if (mAnimationHandler.isTarget(wc, true /* open */)) {
- mAnimationHandler.markStartingSurfaceMatch();
+ mAnimationHandler.markStartingSurfaceMatch(startTransaction);
break;
}
}
+ // release animation leash
+ if (mAnimationHandler.mOpenAnimAdaptor.mCloseTransaction != null) {
+ startTransaction.merge(mAnimationHandler.mOpenAnimAdaptor.mCloseTransaction);
+ mAnimationHandler.mOpenAnimAdaptor.mCloseTransaction = null;
+ }
// Because the target will reparent to transition root, so it cannot be controlled by
// animation leash. Hide the close target when transition starts.
startTransaction.hide(mAnimationHandler.mCloseAdaptor.mTarget.getSurfaceControl());
@@ -993,7 +999,7 @@ class BackNavigationController {
}
final RemoteAnimationTarget[] targets = new RemoteAnimationTarget[2];
targets[0] = mCloseAdaptor.mAnimationTarget;
- targets[1] = mOpenAnimAdaptor.getOrCreateAnimationTarget();
+ targets[1] = mOpenAnimAdaptor.mRemoteAnimationTarget;
return targets;
}
@@ -1060,18 +1066,20 @@ class BackNavigationController {
if (mOpenActivities != null) {
for (int i = mOpenActivities.length - 1; i >= 0; --i) {
- if (mOpenActivities[i].mLaunchTaskBehind) {
- restoreLaunchBehind(mOpenActivities[i]);
+ final ActivityRecord resetActivity = mOpenActivities[i];
+ if (resetActivity.mLaunchTaskBehind) {
+ restoreLaunchBehind(resetActivity);
}
}
}
}
- void markStartingSurfaceMatch() {
- mStartingSurfaceTargetMatch = true;
- for (int i = mOpenAnimAdaptor.mAdaptors.length - 1; i >= 0; --i) {
- mOpenAnimAdaptor.mAdaptors[i].reparentWindowlessSurfaceToTarget();
+ void markStartingSurfaceMatch(SurfaceControl.Transaction reparentTransaction) {
+ if (mStartingSurfaceTargetMatch) {
+ return;
}
+ mStartingSurfaceTargetMatch = true;
+ mOpenAnimAdaptor.reparentWindowlessSurfaceToTarget(reparentTransaction);
}
void clearBackAnimateTarget() {
@@ -1140,14 +1148,23 @@ class BackNavigationController {
private static class BackWindowAnimationAdaptorWrapper {
final BackWindowAnimationAdaptor[] mAdaptors;
+ // The highest remote animation target, which can be a wrapper if multiple adaptors,
+ // or the single opening target.
+ final RemoteAnimationTarget mRemoteAnimationTarget;
SurfaceControl.Transaction mCloseTransaction;
+ // The starting surface task Id. Used to clear the starting surface if the animation has
+ // requested one during animating.
+ private int mRequestedStartingSurfaceId = INVALID_TASK_ID;
+ private SurfaceControl mStartingSurface;
BackWindowAnimationAdaptorWrapper(boolean isOpen, int switchType,
@NonNull WindowContainer... targets) {
mAdaptors = new BackWindowAnimationAdaptor[targets.length];
for (int i = targets.length - 1; i >= 0; --i) {
mAdaptors[i] = createAdaptor(targets[i], isOpen, switchType);
}
+ mRemoteAnimationTarget = targets.length > 1 ? createWrapTarget()
+ : mAdaptors[0].mAnimationTarget;
}
boolean isValid() {
@@ -1160,75 +1177,150 @@ class BackNavigationController {
}
void cleanUp(boolean startingSurfaceMatch) {
+ cleanUpWindowlessSurface(startingSurfaceMatch);
for (int i = mAdaptors.length - 1; i >= 0; --i) {
- mAdaptors[i].cleanUpWindowlessSurface(startingSurfaceMatch);
mAdaptors[i].mTarget.cancelAnimation();
}
+ mRequestedStartingSurfaceId = INVALID_TASK_ID;
+ mStartingSurface = null;
if (mCloseTransaction != null) {
mCloseTransaction.apply();
mCloseTransaction = null;
}
}
- void onAnimationFinish() {
- final SurfaceControl.Transaction pt = mAdaptors[0].mTarget.getPendingTransaction();
- if (mCloseTransaction != null) {
- pt.merge(mCloseTransaction);
- mCloseTransaction = null;
- }
- if (mAdaptors.length > 1) {
- for (int i = mAdaptors.length - 1; i >= 0; --i) {
- final WindowContainer wc = mAdaptors[i].mTarget;
- final WindowContainer parent = wc.getParent();
- if (parent != null) {
- pt.reparent(wc.getSurfaceControl(),
- parent.getSurfaceControl());
- }
- }
- }
- }
-
- @NonNull RemoteAnimationTarget getOrCreateAnimationTarget() {
+ private RemoteAnimationTarget createWrapTarget() {
// Special handle for opening two activities together.
// If we animate both activities separately, the animation area and rounded corner
// would also being handled separately. To make them seem like "open" together, wrap
// their leash with another animation leash.
- if (mAdaptors.length > 1 && mCloseTransaction == null) {
- final Rect unionBounds = new Rect();
- for (int i = mAdaptors.length - 1; i >= 0; --i) {
- unionBounds.union(mAdaptors[i].mAnimationTarget.localBounds);
+ final Rect unionBounds = new Rect();
+ for (int i = mAdaptors.length - 1; i >= 0; --i) {
+ unionBounds.union(mAdaptors[i].mAnimationTarget.localBounds);
+ }
+ final WindowContainer wc = mAdaptors[0].mTarget;
+ final Task task = wc.asActivityRecord() != null
+ ? wc.asActivityRecord().getTask() : wc.asTask();
+ final RemoteAnimationTarget represent = mAdaptors[0].mAnimationTarget;
+ final SurfaceControl leashSurface = new SurfaceControl.Builder()
+ .setName("cross-animation-leash")
+ .setContainerLayer()
+ .setHidden(false)
+ .setParent(task.getSurfaceControl())
+ .build();
+ mCloseTransaction = new SurfaceControl.Transaction();
+ mCloseTransaction.reparent(leashSurface, null);
+ final SurfaceControl.Transaction pt = wc.getPendingTransaction();
+ pt.setLayer(leashSurface, wc.getParent().getLastLayer());
+ for (int i = mAdaptors.length - 1; i >= 0; --i) {
+ BackWindowAnimationAdaptor adaptor = mAdaptors[i];
+ pt.reparent(adaptor.mAnimationTarget.leash, leashSurface);
+ pt.setPosition(adaptor.mAnimationTarget.leash,
+ adaptor.mAnimationTarget.localBounds.left,
+ adaptor.mAnimationTarget.localBounds.top);
+ // For adjacent activity embedded, reparent Activity to TaskFragment when
+ // animation finish
+ final WindowContainer parent = adaptor.mTarget.getParent();
+ if (parent != null) {
+ mCloseTransaction.reparent(adaptor.mTarget.getSurfaceControl(),
+ parent.getSurfaceControl());
}
- final WindowContainer wc = mAdaptors[0].mTarget;
- final Task task = wc.asActivityRecord() != null
- ? wc.asActivityRecord().getTask() : wc.asTask();
- final RemoteAnimationTarget represent = mAdaptors[0].mAnimationTarget;
- final SurfaceControl leashSurface = new SurfaceControl.Builder()
- .setName("cross-animation-leash")
- .setContainerLayer()
- .setHidden(false)
- .setParent(task.getSurfaceControl())
- .build();
- final SurfaceControl.Transaction pt = wc.getPendingTransaction();
- pt.setLayer(leashSurface, wc.getParent().getLastLayer());
- mCloseTransaction = new SurfaceControl.Transaction();
- mCloseTransaction.reparent(leashSurface, null);
- for (int i = mAdaptors.length - 1; i >= 0; --i) {
- BackWindowAnimationAdaptor adaptor = mAdaptors[i];
- pt.reparent(adaptor.mAnimationTarget.leash, leashSurface);
- pt.setPosition(adaptor.mAnimationTarget.leash,
- adaptor.mAnimationTarget.localBounds.left,
- adaptor.mAnimationTarget.localBounds.top);
+ }
+ return new RemoteAnimationTarget(represent.taskId, represent.mode, leashSurface,
+ represent.isTranslucent, represent.clipRect, represent.contentInsets,
+ represent.prefixOrderIndex,
+ new Point(unionBounds.left, unionBounds.top),
+ unionBounds, unionBounds, represent.windowConfiguration,
+ true /* isNotInRecents */, null, null, represent.taskInfo,
+ represent.allowEnterPip);
+ }
+
+ void createStartingSurface(ActivityRecord[] visibleOpenActivities) {
+ if (mAdaptors[0].mSwitchType == DIALOG_CLOSE) {
+ return;
+ }
+ final WindowContainer mainOpen = mAdaptors[0].mTarget;
+ final int switchType = mAdaptors[0].mSwitchType;
+ final Task openTask = switchType == TASK_SWITCH
+ ? mainOpen.asTask() : switchType == ACTIVITY_SWITCH
+ ? mainOpen.asActivityRecord().getTask() : null;
+ if (openTask == null) {
+ return;
+ }
+ final ActivityRecord mainActivity = switchType == ACTIVITY_SWITCH
+ ? mainOpen.asActivityRecord()
+ : openTask.getTopNonFinishingActivity();
+ if (mainActivity == null) {
+ return;
+ }
+ final TaskSnapshot snapshot = getSnapshot(mainOpen, visibleOpenActivities);
+ // If there is only one adaptor, attach the windowless window to top activity,
+ // because fixed rotation only applies on activity.
+ // Note that embedded activity won't use fixed rotation.
+ final Configuration openConfig = mAdaptors.length == 1
+ ? mainActivity.getConfiguration() : openTask.getConfiguration();
+ mRequestedStartingSurfaceId = openTask.mAtmService.mTaskOrganizerController
+ .addWindowlessStartingSurface(openTask, mainActivity,
+ mAdaptors.length == 1 ? mainActivity.getSurfaceControl()
+ : mRemoteAnimationTarget.leash, snapshot, openConfig,
+ new IWindowlessStartingSurfaceCallback.Stub() {
+ // Once the starting surface has been created in shell, it will call
+ // onSurfaceAdded to pass the created surface to core, so if a
+ // transition is triggered by the back gesture, there doesn't need to
+ // create another starting surface for the opening target, just reparent
+ // the starting surface to the opening target.
+ // Note if cleanUpWindowlessSurface happen prior than onSurfaceAdded
+ // called, there won't be able to reparent the starting surface on
+ // opening target. But if that happens and transition target is matched,
+ // the app window should already draw.
+ @Override
+ public void onSurfaceAdded(SurfaceControl sc) {
+ synchronized (openTask.mWmService.mGlobalLock) {
+ if (mRequestedStartingSurfaceId != INVALID_TASK_ID) {
+ mStartingSurface = sc;
+ }
+ }
+ }
+ });
+ }
+
+ // When back gesture has triggered and transition target matches navigation target,
+ // reparent the starting surface to the opening target as it's starting window.
+ void reparentWindowlessSurfaceToTarget(SurfaceControl.Transaction reparentTransaction) {
+ if (mRequestedStartingSurfaceId == INVALID_TASK_ID) {
+ return;
+ }
+ // If open target matches, reparent to open activity or task
+ if (mStartingSurface != null && mStartingSurface.isValid()) {
+ SurfaceControl.Transaction transaction = reparentTransaction != null
+ ? reparentTransaction : mAdaptors[0].mTarget.getPendingTransaction();
+ if (mAdaptors.length != 1) {
+ // More than one opening window, reparent starting surface to leaf task.
+ final WindowContainer wc = mAdaptors[0].mTarget;
+ final Task task = wc.asActivityRecord() != null
+ ? wc.asActivityRecord().getTask() : wc.asTask();
+ transaction.reparent(mStartingSurface, task != null
+ ? task.getSurfaceControl()
+ : mAdaptors[0].mTarget.getSurfaceControl());
}
- return new RemoteAnimationTarget(represent.taskId, represent.mode, leashSurface,
- represent.isTranslucent, represent.clipRect, represent.contentInsets,
- represent.prefixOrderIndex,
- new Point(unionBounds.left, unionBounds.top),
- unionBounds, unionBounds, represent.windowConfiguration,
- true /* isNotInRecents */, null, null, represent.taskInfo,
- represent.allowEnterPip);
- } else {
- return mAdaptors[0].mAnimationTarget;
+ // remove starting surface.
+ mStartingSurface = null;
+ }
+ }
+
+ /**
+ * Ask shell to clear the starting surface.
+ * @param openTransitionMatch if true, shell will play the remove starting window
+ * animation, otherwise remove it directly.
+ */
+ void cleanUpWindowlessSurface(boolean openTransitionMatch) {
+ if (mRequestedStartingSurfaceId == INVALID_TASK_ID) {
+ return;
}
+ mAdaptors[0].mTarget.mWmService.mAtmService.mTaskOrganizerController
+ .removeWindowlessStartingSurface(mRequestedStartingSurfaceId,
+ !openTransitionMatch);
+ mRequestedStartingSurfaceId = INVALID_TASK_ID;
}
}
@@ -1240,11 +1332,6 @@ class BackNavigationController {
private RemoteAnimationTarget mAnimationTarget;
private final int mSwitchType;
- // The starting surface task Id. Used to clear the starting surface if the animation has
- // requested one during animating.
- private int mRequestedStartingSurfaceId = INVALID_TASK_ID;
- private SurfaceControl mStartingSurface;
-
BackWindowAnimationAdaptor(@NonNull WindowContainer target, boolean isOpen,
int switchType) {
mBounds.set(target.getBounds());
@@ -1276,8 +1363,6 @@ class BackNavigationController {
public void onAnimationCancelled(SurfaceControl animationLeash) {
if (mCapturedLeash == animationLeash) {
mCapturedLeash = null;
- mRequestedStartingSurfaceId = INVALID_TASK_ID;
- mStartingSurface = null;
}
}
@@ -1345,84 +1430,6 @@ class BackNavigationController {
r.checkEnterPictureInPictureAppOpsState());
return mAnimationTarget;
}
-
- void createStartingSurface(@NonNull WindowContainer closeWindow,
- @NonNull ActivityRecord[] visibleOpenActivities) {
- if (!mIsOpen) {
- return;
- }
- if (mSwitchType == DIALOG_CLOSE) {
- return;
- }
- final Task openTask = mSwitchType == TASK_SWITCH
- ? mTarget.asTask() : mSwitchType == ACTIVITY_SWITCH
- ? mTarget.asActivityRecord().getTask() : null;
- if (openTask == null) {
- return;
- }
- final ActivityRecord mainActivity = mSwitchType == ACTIVITY_SWITCH
- ? mTarget.asActivityRecord()
- : openTask.getTopNonFinishingActivity();
- if (mainActivity == null) {
- return;
- }
- final TaskSnapshot snapshot = getSnapshot(mTarget, visibleOpenActivities);
- mRequestedStartingSurfaceId = openTask.mAtmService.mTaskOrganizerController
- .addWindowlessStartingSurface(openTask, mainActivity,
- // Choose configuration from closeWindow, because the configuration
- // of opening target may not update before resume, so the starting
- // surface should occlude it entirely.
- mAnimationTarget.leash, snapshot, closeWindow.getConfiguration(),
- new IWindowlessStartingSurfaceCallback.Stub() {
- // Once the starting surface has been created in shell, it will call
- // onSurfaceAdded to pass the created surface to core, so if a
- // transition is triggered by the back gesture, there doesn't need to
- // create another starting surface for the opening target, just reparent
- // the starting surface to the opening target.
- // Note if cleanUpWindowlessSurface happen prior than onSurfaceAdded
- // called, there won't be able to reparent the starting surface on
- // opening target. But if that happens and transition target is matched,
- // the app window should already draw.
- @Override
- public void onSurfaceAdded(SurfaceControl sc) {
- synchronized (mTarget.mWmService.mGlobalLock) {
- if (mRequestedStartingSurfaceId != INVALID_TASK_ID) {
- mStartingSurface = sc;
- }
- }
- }
- });
- }
-
- // When back gesture has triggered and transition target matches navigation target,
- // reparent the starting surface to the opening target as it's starting window.
- void reparentWindowlessSurfaceToTarget() {
- if (mRequestedStartingSurfaceId == INVALID_TASK_ID) {
- return;
- }
- // If open target matches, reparent to open activity or task
- if (mStartingSurface != null && mStartingSurface.isValid()) {
- mTarget.getPendingTransaction()
- .reparent(mStartingSurface, mTarget.getSurfaceControl());
- // remove starting surface.
- mStartingSurface = null;
- }
- }
-
- /**
- * Ask shell to clear the starting surface.
- * @param openTransitionMatch if true, shell will play the remove starting window
- * animation, otherwise remove it directly.
- */
- void cleanUpWindowlessSurface(boolean openTransitionMatch) {
- if (mRequestedStartingSurfaceId == INVALID_TASK_ID) {
- return;
- }
- mTarget.mWmService.mAtmService.mTaskOrganizerController
- .removeWindowlessStartingSurface(mRequestedStartingSurfaceId,
- !openTransitionMatch);
- mRequestedStartingSurfaceId = INVALID_TASK_ID;
- }
}
ScheduleAnimationBuilder prepareAnimation(
@@ -1493,24 +1500,15 @@ class BackNavigationController {
/**
* Apply preview strategy on the opening target
- * @param closeWindow The close window, where it's configuration should cover all
- * open target(s).
+ *
* @param openAnimationAdaptor The animator who can create starting surface.
* @param visibleOpenActivities The visible activities in opening targets.
*/
- private void applyPreviewStrategy(@NonNull WindowContainer closeWindow,
- @NonNull BackWindowAnimationAdaptor[] openAnimationAdaptor,
+ private void applyPreviewStrategy(
+ @NonNull BackWindowAnimationAdaptorWrapper openAnimationAdaptor,
@NonNull ActivityRecord[] visibleOpenActivities) {
- if (isSupportWindowlessSurface() && mShowWindowlessSurface && !mIsLaunchBehind
- // TODO (b/274997067) Draw two snapshot in a single starting surface.
- // We are using TaskId as the key of
- // StartingSurfaceDrawer#StartingWindowRecordManager, so we cannot create
- // two activity snapshot with WindowlessStartingWindow.
- // Try to draw two snapshot within a WindowlessStartingWindow, or find
- // another key for StartingWindowRecordManager.
- && openAnimationAdaptor.length == 1) {
- openAnimationAdaptor[0].createStartingSurface(closeWindow,
- visibleOpenActivities);
+ if (isSupportWindowlessSurface() && mShowWindowlessSurface && !mIsLaunchBehind) {
+ openAnimationAdaptor.createStartingSurface(visibleOpenActivities);
} else {
for (int i = visibleOpenActivities.length - 1; i >= 0; --i) {
setLaunchBehind(visibleOpenActivities[i]);
@@ -1541,7 +1539,7 @@ class BackNavigationController {
}
mCloseTarget.mTransitionController.mSnapshotController
.mActivitySnapshotController.clearOnBackPressedActivities();
- applyPreviewStrategy(mCloseTarget, mOpenAnimAdaptor.mAdaptors, openingActivities);
+ applyPreviewStrategy(mOpenAnimAdaptor, openingActivities);
final IBackAnimationFinishedCallback callback = makeAnimationFinishedCallback();
final RemoteAnimationTarget[] targets = getAnimationTargets();
@@ -1565,7 +1563,6 @@ class BackNavigationController {
// animation was canceled
return;
}
- mOpenAnimAdaptor.onAnimationFinish();
if (!triggerBack) {
clearBackAnimateTarget();
} else {
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index b65b2b2ae959..fdae53f59ad4 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -56,6 +56,7 @@ import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.content.ComponentName;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Process;
import android.os.SystemClock;
@@ -93,6 +94,11 @@ public class BackgroundActivityStartController {
private static final long ASM_GRACEPERIOD_TIMEOUT_MS = TIMEOUT_MS;
private static final int ASM_GRACEPERIOD_MAX_REPEATS = 5;
private static final int NO_PROCESS_UID = -1;
+
+ static final String AUTO_OPT_IN_NOT_PENDING_INTENT = "notPendingIntent";
+ static final String AUTO_OPT_IN_CALL_FOR_RESULT = "callForResult";
+ static final String AUTO_OPT_IN_SAME_UID = "sameUid";
+
/** If enabled the creator will not allow BAL on its behalf by default. */
@ChangeId
@EnabledAfter(targetSdkVersion = UPSIDE_DOWN_CAKE)
@@ -231,9 +237,9 @@ public class BackgroundActivityStartController {
private final boolean mCallingUidHasAnyVisibleWindow;
private final @ActivityManager.ProcessState int mCallingUidProcState;
private final boolean mIsCallingUidPersistentSystemProcess;
- private final BackgroundStartPrivileges mBalAllowedByPiSender;
- private final BackgroundStartPrivileges mBalAllowedByPiCreatorWithHardening;
- private final BackgroundStartPrivileges mBalAllowedByPiCreator;
+ final BackgroundStartPrivileges mBalAllowedByPiSender;
+ final BackgroundStartPrivileges mBalAllowedByPiCreatorWithHardening;
+ final BackgroundStartPrivileges mBalAllowedByPiCreator;
private final String mRealCallingPackage;
private final int mRealCallingUid;
private final int mRealCallingPid;
@@ -247,11 +253,12 @@ public class BackgroundActivityStartController {
private final WindowProcessController mRealCallerApp;
private final boolean mIsCallForResult;
private final ActivityOptions mCheckedOptions;
- private final String mAutoOptInReason;
+ final String mAutoOptInReason;
+ private final boolean mAutoOptInCaller;
private BalVerdict mResultForCaller;
private BalVerdict mResultForRealCaller;
- private BalState(int callingUid, int callingPid, final String callingPackage,
+ @VisibleForTesting BalState(int callingUid, int callingPid, final String callingPackage,
int realCallingUid, int realCallingPid,
WindowProcessController callerApp,
PendingIntentRecord originatingPendingIntent,
@@ -279,26 +286,27 @@ public class BackgroundActivityStartController {
if (!balImproveRealCallerVisibilityCheck()) {
// without this fix the auto-opt ins below would violate CTS tests
mAutoOptInReason = null;
- } else if (mIsCallForResult) {
- mAutoOptInReason = "callForResult";
+ mAutoOptInCaller = false;
} else if (originatingPendingIntent == null) {
- mAutoOptInReason = "notPendingIntent";
+ mAutoOptInReason = AUTO_OPT_IN_NOT_PENDING_INTENT;
+ mAutoOptInCaller = true;
+ } else if (mIsCallForResult) {
+ mAutoOptInReason = AUTO_OPT_IN_CALL_FOR_RESULT;
+ mAutoOptInCaller = false;
} else if (callingUid == realCallingUid && !balRequireOptInSameUid()) {
- mAutoOptInReason = "sameUid";
+ mAutoOptInReason = AUTO_OPT_IN_SAME_UID;
+ mAutoOptInCaller = false;
} else {
mAutoOptInReason = null;
+ mAutoOptInCaller = false;
}
- if (mAutoOptInReason != null) {
+ if (mAutoOptInCaller) {
// grant BAL privileges unless explicitly opted out
mBalAllowedByPiCreatorWithHardening = mBalAllowedByPiCreator =
callerBackgroundActivityStartMode == MODE_BACKGROUND_ACTIVITY_START_DENIED
? BackgroundStartPrivileges.NONE
: BackgroundStartPrivileges.ALLOW_BAL;
- mBalAllowedByPiSender = realCallerBackgroundActivityStartMode
- == MODE_BACKGROUND_ACTIVITY_START_DENIED
- ? BackgroundStartPrivileges.NONE
- : BackgroundStartPrivileges.ALLOW_BAL;
} else {
// for PendingIntents we restrict BAL based on target_sdk
mBalAllowedByPiCreatorWithHardening = getBackgroundStartPrivilegesAllowedByCreator(
@@ -311,10 +319,21 @@ public class BackgroundActivityStartController {
mBalAllowedByPiCreator = balRequireOptInByPendingIntentCreator()
? mBalAllowedByPiCreatorWithHardening
: mBalAllowedByPiCreatorWithoutHardening;
+ }
+
+ if (mAutoOptInReason != null) {
+ // grant BAL privileges unless explicitly opted out
+ mBalAllowedByPiSender = realCallerBackgroundActivityStartMode
+ == MODE_BACKGROUND_ACTIVITY_START_DENIED
+ ? BackgroundStartPrivileges.NONE
+ : BackgroundStartPrivileges.ALLOW_BAL;
+ } else {
+ // for PendingIntents we restrict BAL based on target_sdk
mBalAllowedByPiSender =
PendingIntentRecord.getBackgroundStartPrivilegesAllowedByCaller(
checkedOptions, realCallingUid, mRealCallingPackage);
}
+
mAppSwitchState = mService.getBalAppSwitchesState();
mCallingUidProcState = mService.mActiveUids.getUidState(callingUid);
mIsCallingUidPersistentSystemProcess =
@@ -406,7 +425,7 @@ public class BackgroundActivityStartController {
return mRealCallingUid != NO_PROCESS_UID;
}
- private boolean isPendingIntent() {
+ boolean isPendingIntent() {
return mOriginatingPendingIntent != null && hasRealCaller();
}
@@ -484,23 +503,19 @@ public class BackgroundActivityStartController {
}
public boolean callerExplicitOptInOrAutoOptIn() {
- if (mAutoOptInReason == null) {
- return mCheckedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
- == MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
- } else {
- return mCheckedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
- != MODE_BACKGROUND_ACTIVITY_START_DENIED;
+ if (mAutoOptInCaller) {
+ return !callerExplicitOptOut();
}
+ return mCheckedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
+ == MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
}
public boolean realCallerExplicitOptInOrAutoOptIn() {
- if (mAutoOptInReason == null) {
- return mCheckedOptions.getPendingIntentBackgroundActivityStartMode()
- == MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
- } else {
- return mCheckedOptions.getPendingIntentBackgroundActivityStartMode()
- != MODE_BACKGROUND_ACTIVITY_START_DENIED;
+ if (mAutoOptInReason != null) {
+ return !realCallerExplicitOptOut();
}
+ return mCheckedOptions.getPendingIntentBackgroundActivityStartMode()
+ == MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
}
public boolean callerExplicitOptOut() {
@@ -522,6 +537,11 @@ public class BackgroundActivityStartController {
return mCheckedOptions.getPendingIntentBackgroundActivityStartMode()
!= MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
}
+
+ @Override
+ public String toString() {
+ return dump();
+ }
}
static class BalVerdict {
@@ -529,6 +549,9 @@ public class BackgroundActivityStartController {
static final BalVerdict BLOCK = new BalVerdict(BAL_BLOCK, false, "Blocked");
static final BalVerdict ALLOW_BY_DEFAULT =
new BalVerdict(BAL_ALLOW_DEFAULT, false, "Default");
+ // Careful using this - it will bypass all ASM checks.
+ static final BalVerdict ALLOW_PRIVILEGED =
+ new BalVerdict(BAL_ALLOW_ALLOWLISTED_UID, false, "PRIVILEGED");
private final @BalCode int mCode;
private final boolean mBackground;
private final String mMessage;
@@ -722,9 +745,8 @@ public class BackgroundActivityStartController {
// Allowed before V by creator
if (state.mBalAllowedByPiCreator.allowsBackgroundActivityStarts()) {
Slog.wtf(TAG, "With Android 15 BAL hardening this activity start may be blocked"
- + " if the PI creator upgrades target_sdk to 35+! "
- + " (missing opt in by PI creator)! "
- + state.dump());
+ + " if the PI creator upgrades target_sdk to 35+! "
+ + " (missing opt in by PI creator)!" + state.dump());
showBalRiskToast();
return allowBasedOnCaller(state);
}
@@ -733,17 +755,15 @@ public class BackgroundActivityStartController {
// Allowed before U by sender
if (state.mBalAllowedByPiSender.allowsBackgroundActivityStarts()) {
Slog.wtf(TAG, "With Android 14 BAL hardening this activity start will be blocked"
- + " if the PI sender upgrades target_sdk to 34+! "
- + " (missing opt in by PI sender)! "
- + state.dump());
+ + " if the PI sender upgrades target_sdk to 34+! "
+ + " (missing opt in by PI sender)!" + state.dump());
showBalRiskToast();
return allowBasedOnRealCaller(state);
}
}
// caller or real caller could start the activity, but would need to explicitly opt in
if (callerCanAllow || realCallerCanAllow) {
- Slog.wtf(TAG, "Without BAL hardening this activity start would be allowed "
- + state.dump());
+ Slog.w(TAG, "Without BAL hardening this activity start would be allowed");
}
// neither the caller not the realCaller can allow or have explicitly opted out
return abortLaunch(state);
@@ -766,7 +786,7 @@ public class BackgroundActivityStartController {
}
private BalVerdict abortLaunch(BalState state) {
- Slog.w(TAG, "Background activity launch blocked! "
+ Slog.wtf(TAG, "Background activity launch blocked! "
+ state.dump());
showBalBlockedToast();
return statsLog(BalVerdict.BLOCK, state);
@@ -913,8 +933,10 @@ public class BackgroundActivityStartController {
// Normal apps with visible app window will be allowed to start activity if app switching
// is allowed, or apps like live wallpaper with non app visible window will be allowed.
+ // The home app can start apps even if app switches are usually disallowed.
final boolean appSwitchAllowedOrFg = state.mAppSwitchState == APP_SWITCH_ALLOW
- || state.mAppSwitchState == APP_SWITCH_FG_ONLY;
+ || state.mAppSwitchState == APP_SWITCH_FG_ONLY
+ || isHomeApp(state.mRealCallingUid, state.mRealCallingPackage);
if (balImproveRealCallerVisibilityCheck()) {
if (appSwitchAllowedOrFg && state.mRealCallingUidHasAnyVisibleWindow) {
return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW,
@@ -969,7 +991,7 @@ public class BackgroundActivityStartController {
* String, int, boolean, boolean, boolean, long, long, long)} for details on the
* exceptions.
*/
- private BalVerdict checkProcessAllowsBal(WindowProcessController app,
+ @VisibleForTesting BalVerdict checkProcessAllowsBal(WindowProcessController app,
BalState state) {
if (app == null) {
return BalVerdict.BLOCK;
@@ -1232,7 +1254,8 @@ public class BackgroundActivityStartController {
boolean shouldBlockActivityStart = ActivitySecurityModelFeatureFlags
.shouldRestrictActivitySwitch(callingUid);
int[] finishCount = new int[0];
- if (shouldBlockActivityStart) {
+ if (shouldBlockActivityStart
+ && blockCrossUidActivitySwitchFromBelowForActivity(targetTaskTop)) {
ActivityRecord activity = targetTask.getActivity(isLaunchingOrLaunched);
if (activity == null) {
// mStartActivity is not in task, so clear everything
@@ -1317,7 +1340,7 @@ public class BackgroundActivityStartController {
boolean restrictActivitySwitch = ActivitySecurityModelFeatureFlags
.shouldRestrictActivitySwitch(callingUid)
- && bas.mBlockActivityStartIfFlagEnabled;
+ && bas.mBlockActivityStartIfFlagEnabled;
PackageManager pm = mService.mContext.getPackageManager();
String callingPackage = pm.getNameForUid(callingUid);
@@ -1371,19 +1394,19 @@ public class BackgroundActivityStartController {
int uid, @Nullable ActivityRecord sourceRecord) {
// If the source is visible, consider it 'top'.
if (sourceRecord != null && sourceRecord.isVisibleRequested()) {
- return new BlockActivityStart(false, false);
+ return BlockActivityStart.ACTIVITY_START_ALLOWED;
}
// Always allow actual top activity to clear task
ActivityRecord topActivity = task.getTopMostActivity();
if (topActivity != null && topActivity.isUid(uid)) {
- return new BlockActivityStart(false, false);
+ return BlockActivityStart.ACTIVITY_START_ALLOWED;
}
// If UID is visible in target task, allow launch
if (task.forAllActivities((Predicate<ActivityRecord>)
ar -> ar.isUid(uid) && ar.isVisibleRequested())) {
- return new BlockActivityStart(false, false);
+ return BlockActivityStart.ACTIVITY_START_ALLOWED;
}
// Consider the source activity, whether or not it is finishing. Do not consider any other
@@ -1450,12 +1473,11 @@ public class BackgroundActivityStartController {
private BlockActivityStart blockCrossUidActivitySwitchFromBelow(ActivityRecord ar,
int sourceUid) {
if (ar.isUid(sourceUid)) {
- return new BlockActivityStart(false, false);
+ return BlockActivityStart.ACTIVITY_START_ALLOWED;
}
- // If mAllowCrossUidActivitySwitchFromBelow is set, honor it.
- if (ar.mAllowCrossUidActivitySwitchFromBelow) {
- return new BlockActivityStart(false, false);
+ if (!blockCrossUidActivitySwitchFromBelowForActivity(ar)) {
+ return BlockActivityStart.ACTIVITY_START_ALLOWED;
}
// At this point, we would block if the feature is launched and both apps were V+
@@ -1466,8 +1488,11 @@ public class BackgroundActivityStartController {
ActivitySecurityModelFeatureFlags.shouldRestrictActivitySwitch(ar.getUid())
&& ActivitySecurityModelFeatureFlags
.shouldRestrictActivitySwitch(sourceUid);
- return new BackgroundActivityStartController
- .BlockActivityStart(restrictActivitySwitch, true);
+ if (restrictActivitySwitch) {
+ return BlockActivityStart.BLOCK;
+ } else {
+ return BlockActivityStart.LOG_ONLY;
+ }
}
/**
@@ -1673,14 +1698,52 @@ public class BackgroundActivityStartController {
}
}
- static class BlockActivityStart {
+ /**
+ * Activity level allowCrossUidActivitySwitchFromBelow defaults to false.
+ * Package level defaults to true.
+ * We block the launch if dev has explicitly set package level to false, and activity level has
+ * not opted out
+ */
+ private boolean blockCrossUidActivitySwitchFromBelowForActivity(@NonNull ActivityRecord ar) {
+ // We don't need to check package level if activity has opted out.
+ if (ar.mAllowCrossUidActivitySwitchFromBelow) {
+ return false;
+ }
+
+ if (ActivitySecurityModelFeatureFlags.asmRestrictionsEnabledForAll()) {
+ return true;
+ }
+
+ String packageName = ar.packageName;
+ if (packageName == null) {
+ return false;
+ }
+
+ PackageManager pm = mService.mContext.getPackageManager();
+ ApplicationInfo applicationInfo;
+
+ try {
+ applicationInfo = pm.getApplicationInfo(packageName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.wtf(TAG, "Package name: " + packageName + " not found.");
+ return false;
+ }
+
+ return !applicationInfo.allowCrossUidActivitySwitchFromBelow;
+ }
+
+ private static class BlockActivityStart {
+ private static final BlockActivityStart ACTIVITY_START_ALLOWED =
+ new BlockActivityStart(false, false);
+ private static final BlockActivityStart LOG_ONLY = new BlockActivityStart(false, true);
+ private static final BlockActivityStart BLOCK = new BlockActivityStart(true, true);
// We should block if feature flag is enabled
private final boolean mBlockActivityStartIfFlagEnabled;
// Used for logging/toasts. Would we block if target sdk was V and feature was
// enabled?
private final boolean mWouldBlockActivityStartIgnoringFlag;
- BlockActivityStart(boolean shouldBlockActivityStart,
+ private BlockActivityStart(boolean shouldBlockActivityStart,
boolean wouldBlockActivityStartIgnoringFlags) {
this.mBlockActivityStartIfFlagEnabled = shouldBlockActivityStart;
this.mWouldBlockActivityStartIgnoringFlag = wouldBlockActivityStartIgnoringFlags;
diff --git a/services/core/java/com/android/server/wm/ClientLifecycleManager.java b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
index 5b4fb3e6971f..e48e4e84d60d 100644
--- a/services/core/java/com/android/server/wm/ClientLifecycleManager.java
+++ b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
@@ -87,11 +87,7 @@ class ClientLifecycleManager {
void scheduleTransactionItemNow(@NonNull IApplicationThread client,
@NonNull ClientTransactionItem transactionItem) throws RemoteException {
final ClientTransaction clientTransaction = ClientTransaction.obtain(client);
- if (transactionItem.isActivityLifecycleItem()) {
- clientTransaction.setLifecycleStateRequest((ActivityLifecycleItem) transactionItem);
- } else {
- clientTransaction.addCallback(transactionItem);
- }
+ clientTransaction.addTransactionItem(transactionItem);
scheduleTransaction(clientTransaction);
}
@@ -115,11 +111,8 @@ class ClientLifecycleManager {
} else {
// TODO(b/260873529): cleanup after launch.
final ClientTransaction clientTransaction = ClientTransaction.obtain(client);
- if (transactionItem.isActivityLifecycleItem()) {
- clientTransaction.setLifecycleStateRequest((ActivityLifecycleItem) transactionItem);
- } else {
- clientTransaction.addCallback(transactionItem);
- }
+ clientTransaction.addTransactionItem(transactionItem);
+
scheduleTransaction(clientTransaction);
}
}
@@ -160,8 +153,8 @@ class ClientLifecycleManager {
} else {
// TODO(b/260873529): cleanup after launch.
final ClientTransaction clientTransaction = ClientTransaction.obtain(client);
- clientTransaction.addCallback(transactionItem);
- clientTransaction.setLifecycleStateRequest(lifecycleItem);
+ clientTransaction.addTransactionItem(transactionItem);
+ clientTransaction.addTransactionItem(lifecycleItem);
scheduleTransaction(clientTransaction);
}
}
diff --git a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
index 1dc9493eddc6..f11d6ec0828c 100644
--- a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
@@ -16,8 +16,6 @@
package com.android.server.wm;
-import static android.util.DisplayMetrics.DENSITY_DEFAULT;
-
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -30,7 +28,6 @@ import android.util.Slog;
import com.android.server.wm.LaunchParamsController.LaunchParamsModifier;
import com.android.wm.shell.Flags;
-
/**
* The class that defines default launch params for tasks in desktop mode
*/
@@ -44,12 +41,9 @@ public class DesktopModeLaunchParamsModifier implements LaunchParamsModifier {
private static final boolean ENABLE_DESKTOP_WINDOWING = Flags.enableDesktopWindowing();
private static final boolean DESKTOP_MODE_PROTO2_SUPPORTED =
SystemProperties.getBoolean("persist.wm.debug.desktop_mode_2", false);
- // Override default freeform task width when desktop mode is enabled. In dips.
- private static final int DESKTOP_MODE_DEFAULT_WIDTH_DP = SystemProperties.getInt(
- "persist.wm.debug.desktop_mode.default_width", 840);
- // Override default freeform task height when desktop mode is enabled. In dips.
- private static final int DESKTOP_MODE_DEFAULT_HEIGHT_DP = SystemProperties.getInt(
- "persist.wm.debug.desktop_mode.default_height", 630);
+ public static final float DESKTOP_MODE_INITIAL_BOUNDS_SCALE =
+ SystemProperties
+ .getInt("persist.wm.debug.desktop_mode_initial_bounds_scale", 75) / 100f;
private StringBuilder mLogBuilder;
@@ -108,23 +102,29 @@ public class DesktopModeLaunchParamsModifier implements LaunchParamsModifier {
return RESULT_SKIP;
}
- // Update width and height with default desktop mode values
- float density = (float) task.getConfiguration().densityDpi / DENSITY_DEFAULT;
- final int width = (int) (DESKTOP_MODE_DEFAULT_WIDTH_DP * density + 0.5f);
- final int height = (int) (DESKTOP_MODE_DEFAULT_HEIGHT_DP * density + 0.5f);
- outParams.mBounds.right = width;
- outParams.mBounds.bottom = height;
-
- // Center the task in window bounds
- Rect windowBounds = task.getWindowConfiguration().getBounds();
- outParams.mBounds.offset(windowBounds.centerX() - outParams.mBounds.centerX(),
- windowBounds.centerY() - outParams.mBounds.centerY());
+ calculateAndCentreInitialBounds(task, outParams);
appendLog("setting desktop mode task bounds to %s", outParams.mBounds);
return RESULT_DONE;
}
+ /**
+ * Calculates the initial height and width of a task in desktop mode and centers it within the
+ * window bounds.
+ */
+ private void calculateAndCentreInitialBounds(Task task,
+ LaunchParamsController.LaunchParams outParams) {
+ // TODO(b/319819547): Account for app constraints so apps do not become letterboxed
+ final Rect windowBounds = task.getDisplayArea().getBounds();
+ final int width = (int) (windowBounds.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+ final int height = (int) (windowBounds.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+ outParams.mBounds.right = width;
+ outParams.mBounds.bottom = height;
+ outParams.mBounds.offset(windowBounds.centerX() - outParams.mBounds.centerX(),
+ windowBounds.centerY() - outParams.mBounds.centerY());
+ }
+
private void initLogBuilder(Task task, ActivityRecord activity) {
if (DEBUG) {
mLogBuilder = new StringBuilder(
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index e7431723789d..d3acd716aed3 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1127,7 +1127,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
final ActivityRecord activity = w.mActivityRecord;
if (activity != null && activity.isVisibleRequested()) {
- activity.updateLetterboxSurface(w);
+ activity.updateLetterboxSurfaceIfNeeded(w);
final boolean updateAllDrawn = activity.updateDrawnWindowStates(w);
if (updateAllDrawn && !mTmpUpdateAllDrawn.contains(activity)) {
mTmpUpdateAllDrawn.add(activity);
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index a7bbc25d0bb1..5cf9acdbc0d6 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -104,7 +104,6 @@ import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.util.ArraySet;
-import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
import android.view.DisplayInfo;
@@ -2070,8 +2069,7 @@ public class DisplayPolicy {
}
return false;
}
- if (mCachedDecorInsets != null && !mCachedDecorInsets.canPreserve()
- && !mDisplayContent.isSleeping()) {
+ if (mCachedDecorInsets != null && !mCachedDecorInsets.canPreserve() && mScreenOnFully) {
mCachedDecorInsets = null;
}
mDecorInsets.invalidate();
@@ -2136,16 +2134,6 @@ public class DisplayPolicy {
}
mCachedDecorInsets.mPreserveId =
mDisplayContent.mTransitionController.getCollectingTransitionId();
- // The validator will run after the transition is finished. So if the insets are changed
- // during the transition, it can update to the latest state.
- mDisplayContent.mTransitionController.mStateValidators.add(() -> {
- // The insets provider client may defer to change its window until screen is on. So
- // only validate when awake to avoid the cache being always dropped.
- if (!mDisplayContent.isSleeping() && updateDecorInsetsInfo()) {
- Slog.d(TAG, "Insets changed after display switch transition");
- mDisplayContent.sendNewConfiguration();
- }
- });
}
@NavigationBarPosition
@@ -2891,9 +2879,6 @@ public class DisplayPolicy {
if (!CLIENT_TRANSIENT) {
mSystemGestures.dump(pw, prefix);
}
-
- pw.print(prefix); pw.println("Looper state:");
- mHandler.getLooper().dump(new PrintWriterPrinter(pw), prefix + " ");
}
private boolean supportsPointerLocation() {
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index a3e28693cb21..b68e67e291e7 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -25,6 +25,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACT
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.annotation.NonNull;
+import android.app.ActivityManager;
import android.content.ClipData;
import android.content.Context;
import android.hardware.input.InputManagerGlobal;
@@ -43,8 +44,8 @@ import android.view.PointerIcon;
import android.view.SurfaceControl;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
+import android.window.IGlobalDragListener;
import android.window.IUnhandledDragCallback;
-import android.window.IUnhandledDragListener;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wm.WindowManagerInternal.IDragDropCallback;
@@ -81,9 +82,9 @@ class DragDropController {
private WindowManagerService mService;
private final Handler mHandler;
- // The unhandled drag listener for handling cross-window drags that end with no target window
- private IUnhandledDragListener mUnhandledDragListener;
- private final IBinder.DeathRecipient mUnhandledDragListenerDeathRecipient =
+ // The global drag listener for handling cross-window drags
+ private IGlobalDragListener mGlobalDragListener;
+ private final IBinder.DeathRecipient mGlobalDragListenerDeathRecipient =
new IBinder.DeathRecipient() {
@Override
public void binderDied() {
@@ -91,7 +92,7 @@ class DragDropController {
if (hasPendingUnhandledDropCallback()) {
onUnhandledDropCallback(false /* consumedByListeners */);
}
- setUnhandledDragListener(null);
+ setGlobalDragListener(null);
}
}
};
@@ -129,29 +130,22 @@ class DragDropController {
/**
* Sets the listener for unhandled cross-window drags.
*/
- public void setUnhandledDragListener(IUnhandledDragListener listener) {
- if (mUnhandledDragListener != null && mUnhandledDragListener.asBinder() != null) {
- mUnhandledDragListener.asBinder().unlinkToDeath(
- mUnhandledDragListenerDeathRecipient, 0);
+ public void setGlobalDragListener(IGlobalDragListener listener) {
+ if (mGlobalDragListener != null && mGlobalDragListener.asBinder() != null) {
+ mGlobalDragListener.asBinder().unlinkToDeath(
+ mGlobalDragListenerDeathRecipient, 0);
}
- mUnhandledDragListener = listener;
+ mGlobalDragListener = listener;
if (listener != null && listener.asBinder() != null) {
try {
- mUnhandledDragListener.asBinder().linkToDeath(
- mUnhandledDragListenerDeathRecipient, 0);
+ mGlobalDragListener.asBinder().linkToDeath(
+ mGlobalDragListenerDeathRecipient, 0);
} catch (RemoteException e) {
- mUnhandledDragListener = null;
+ mGlobalDragListener = null;
}
}
}
- /**
- * Returns whether there is an unhandled drag listener set.
- */
- boolean hasUnhandledDragListener() {
- return mUnhandledDragListener != null;
- }
-
void sendDragStartedIfNeededLocked(WindowState window) {
mDragState.sendDragStartedIfNeededLocked(window);
}
@@ -351,7 +345,20 @@ class DragDropController {
final boolean relinquishDragSurfaceToDropTarget =
consumed && mDragState.targetInterceptsGlobalDrag(callingWin);
+ final boolean isCrossWindowDrag = !mDragState.mLocalWin.equals(token);
mDragState.endDragLocked(consumed, relinquishDragSurfaceToDropTarget);
+
+ final Task droppedWindowTask = callingWin.getTask();
+ if (com.android.window.flags.Flags.delegateUnhandledDrags()
+ && mGlobalDragListener != null && droppedWindowTask != null && consumed
+ && isCrossWindowDrag) {
+ try {
+ mGlobalDragListener.onCrossWindowDrop(droppedWindowTask.getTaskInfo());
+ } catch (RemoteException e) {
+ Slog.e(TAG_WM, "Failed to call global drag listener for cross-window "
+ + "drop", e);
+ }
+ }
}
} finally {
mCallback.get().postReportDropResult();
@@ -367,19 +374,19 @@ class DragDropController {
final boolean isLocalDrag =
(mDragState.mFlags & (DRAG_FLAG_GLOBAL_SAME_APPLICATION | DRAG_FLAG_GLOBAL)) == 0;
if (!com.android.window.flags.Flags.delegateUnhandledDrags()
- || mUnhandledDragListener == null
+ || mGlobalDragListener == null
|| isLocalDrag) {
// Skip if the flag is disabled, there is no unhandled-drag listener, or if this is a
// purely local drag
if (DEBUG_DRAG) Slog.d(TAG_WM, "Skipping unhandled listener "
- + "(listener=" + mUnhandledDragListener + ", flags=" + mDragState.mFlags + ")");
+ + "(listener=" + mGlobalDragListener + ", flags=" + mDragState.mFlags + ")");
return false;
}
if (DEBUG_DRAG) Slog.d(TAG_WM, "Sending DROP to unhandled listener (" + reason + ")");
try {
// Schedule timeout for the unhandled drag listener to call back
sendTimeoutMessage(MSG_UNHANDLED_DROP_LISTENER_TIMEOUT, null, DRAG_TIMEOUT_MS);
- mUnhandledDragListener.onUnhandledDrop(dropEvent, new IUnhandledDragCallback.Stub() {
+ mGlobalDragListener.onUnhandledDrop(dropEvent, new IUnhandledDragCallback.Stub() {
@Override
public void notifyUnhandledDropComplete(boolean consumedByListener) {
if (DEBUG_DRAG) Slog.d(TAG_WM, "Unhandled listener finished handling DROP");
@@ -390,7 +397,7 @@ class DragDropController {
});
return true;
} catch (RemoteException e) {
- Slog.e(TAG_WM, "Failed to call unhandled drag listener", e);
+ Slog.e(TAG_WM, "Failed to call global drag listener for unhandled drop", e);
return false;
}
}
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index e6ef90bd33d2..0978cb49e863 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_EMBEDDED_WINDOWS;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -34,6 +35,9 @@ import android.view.InputApplicationHandle;
import android.view.InputChannel;
import android.window.InputTransferToken;
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.server.input.InputManagerService;
+
/**
* Keeps track of embedded windows.
*
@@ -52,9 +56,13 @@ class EmbeddedWindowController {
private final Object mGlobalLock;
private final ActivityTaskManagerService mAtmService;
- EmbeddedWindowController(ActivityTaskManagerService atmService) {
+ private final InputManagerService mInputManagerService;
+
+ EmbeddedWindowController(ActivityTaskManagerService atmService,
+ InputManagerService inputManagerService) {
mAtmService = atmService;
mGlobalLock = atmService.getGlobalLock();
+ mInputManagerService = inputManagerService;
}
/**
@@ -135,6 +143,61 @@ class EmbeddedWindowController {
return mWindowsByWindowToken.get(windowToken);
}
+ private boolean isValidTouchGestureParams(WindowState hostWindowState,
+ EmbeddedWindow embeddedWindow) {
+ if (embeddedWindow == null) {
+ ProtoLog.w(WM_DEBUG_EMBEDDED_WINDOWS,
+ "Attempt to transfer touch gesture with non-existent embedded window");
+ return false;
+ }
+ final WindowState wsAssociatedWithEmbedded = embeddedWindow.getWindowState();
+ if (wsAssociatedWithEmbedded == null) {
+ ProtoLog.w(WM_DEBUG_EMBEDDED_WINDOWS,
+ "Attempt to transfer touch gesture using embedded window with no associated "
+ + "host");
+ return false;
+ }
+ if (wsAssociatedWithEmbedded.mClient.asBinder() != hostWindowState.mClient.asBinder()) {
+ ProtoLog.w(WM_DEBUG_EMBEDDED_WINDOWS,
+ "Attempt to transfer touch gesture with host window not associated with "
+ + "embedded window");
+ return false;
+ }
+
+ if (embeddedWindow.getInputChannelToken() == null) {
+ ProtoLog.w(WM_DEBUG_EMBEDDED_WINDOWS,
+ "Attempt to transfer touch gesture using embedded window that has no input "
+ + "channel");
+ return false;
+ }
+ if (hostWindowState.mInputChannelToken == null) {
+ ProtoLog.w(WM_DEBUG_EMBEDDED_WINDOWS,
+ "Attempt to transfer touch gesture using a host window with no input channel");
+ return false;
+ }
+ return true;
+ }
+
+ boolean transferToHost(@NonNull InputTransferToken embeddedWindowToken,
+ @NonNull WindowState transferToHostWindowState) {
+ EmbeddedWindow ew = getByInputTransferToken(embeddedWindowToken);
+ if (!isValidTouchGestureParams(transferToHostWindowState, ew)) {
+ return false;
+ }
+ return mInputManagerService.transferTouchGesture(ew.getInputChannelToken(),
+ transferToHostWindowState.mInputChannelToken);
+ }
+
+ boolean transferToEmbedded(WindowState hostWindowState,
+ @NonNull InputTransferToken transferToToken) {
+ final EmbeddedWindowController.EmbeddedWindow ew = getByInputTransferToken(transferToToken);
+ if (!isValidTouchGestureParams(hostWindowState, ew)) {
+ return false;
+ }
+ return mInputManagerService.transferTouchGesture(hostWindowState.mInputChannelToken,
+ ew.getInputChannelToken());
+ }
+
static class EmbeddedWindow implements InputTarget {
final IBinder mClient;
@Nullable final WindowState mHostWindowState;
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index e66321ae8219..362d4efa0825 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -49,7 +49,7 @@ import java.util.function.Supplier;
*/
public class Letterbox {
- private static final Rect EMPTY_RECT = new Rect();
+ static final Rect EMPTY_RECT = new Rect();
private static final Point ZERO_POINT = new Point(0, 0);
private final Supplier<SurfaceControl.Builder> mSurfaceControlFactory;
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index edf9da1e0bf5..5d613cf45643 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -52,7 +52,6 @@ import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED;
@@ -924,21 +923,21 @@ final class LetterboxUiController {
return mLetterbox == null || mLetterbox.notIntersectsOrFullyContains(rect);
}
- void updateLetterboxSurface(WindowState winHint) {
- updateLetterboxSurface(winHint, mActivityRecord.getSyncTransaction());
+ void updateLetterboxSurfaceIfNeeded(WindowState winHint) {
+ updateLetterboxSurfaceIfNeeded(winHint, mActivityRecord.getSyncTransaction());
}
- void updateLetterboxSurface(WindowState winHint, Transaction t) {
+ void updateLetterboxSurfaceIfNeeded(WindowState winHint, Transaction t) {
if (shouldNotLayoutLetterbox(winHint)) {
return;
}
- layoutLetterbox(winHint);
+ layoutLetterboxIfNeeded(winHint);
if (mLetterbox != null && mLetterbox.needsApplySurfaceChanges()) {
mLetterbox.applySurfaceChanges(t);
}
}
- void layoutLetterbox(WindowState w) {
+ void layoutLetterboxIfNeeded(WindowState w) {
if (shouldNotLayoutLetterbox(w)) {
return;
}
@@ -1369,23 +1368,25 @@ final class LetterboxUiController {
*
* <p>Conditions that needs to be met:
* <ul>
- * <li>Activity is portrait-only.
- * <li>Fullscreen window in landscape device orientation.
+ * <li>Windowing mode is fullscreen.
* <li>Horizontal Reachability is enabled.
- * <li>Activity fills parent vertically.
+ * <li>First top opaque activity fills parent vertically, but not horizontally.
* </ul>
*/
private boolean isHorizontalReachabilityEnabled(Configuration parentConfiguration) {
// Use screen resolved bounds which uses resolved bounds or size compat bounds
// as activity bounds can sometimes be empty
+ final Rect opaqueActivityBounds = hasInheritedLetterboxBehavior()
+ ? mFirstOpaqueActivityBeneath.getScreenResolvedBounds()
+ : mActivityRecord.getScreenResolvedBounds();
return mLetterboxConfiguration.getIsHorizontalReachabilityEnabled()
&& parentConfiguration.windowConfiguration.getWindowingMode()
== WINDOWING_MODE_FULLSCREEN
- && (parentConfiguration.orientation == ORIENTATION_LANDSCAPE
- && mActivityRecord.getOrientationForReachability() == ORIENTATION_PORTRAIT)
// Check whether the activity fills the parent vertically.
&& parentConfiguration.windowConfiguration.getAppBounds().height()
- <= mActivityRecord.getScreenResolvedBounds().height();
+ <= opaqueActivityBounds.height()
+ && parentConfiguration.windowConfiguration.getAppBounds().width()
+ > opaqueActivityBounds.width();
}
@VisibleForTesting
@@ -1402,23 +1403,25 @@ final class LetterboxUiController {
*
* <p>Conditions that needs to be met:
* <ul>
- * <li>Activity is landscape-only.
- * <li>Fullscreen window in portrait device orientation.
+ * <li>Windowing mode is fullscreen.
* <li>Vertical Reachability is enabled.
- * <li>Activity fills parent horizontally.
+ * <li>First top opaque activity fills parent horizontally but not vertically.
* </ul>
*/
private boolean isVerticalReachabilityEnabled(Configuration parentConfiguration) {
// Use screen resolved bounds which uses resolved bounds or size compat bounds
// as activity bounds can sometimes be empty
+ final Rect opaqueActivityBounds = hasInheritedLetterboxBehavior()
+ ? mFirstOpaqueActivityBeneath.getScreenResolvedBounds()
+ : mActivityRecord.getScreenResolvedBounds();
return mLetterboxConfiguration.getIsVerticalReachabilityEnabled()
&& parentConfiguration.windowConfiguration.getWindowingMode()
== WINDOWING_MODE_FULLSCREEN
- && (parentConfiguration.orientation == ORIENTATION_PORTRAIT
- && mActivityRecord.getOrientationForReachability() == ORIENTATION_LANDSCAPE)
// Check whether the activity fills the parent horizontally.
- && parentConfiguration.windowConfiguration.getBounds().width()
- == mActivityRecord.getScreenResolvedBounds().width();
+ && parentConfiguration.windowConfiguration.getAppBounds().width()
+ <= opaqueActivityBounds.width()
+ && parentConfiguration.windowConfiguration.getAppBounds().height()
+ > opaqueActivityBounds.height();
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index e027eb63f1d5..dd146420c748 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.app.ActivityManager.FLAG_AND_UNLOCKED;
import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
import static android.app.ActivityManager.RECENT_WITH_EXCLUDED;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
@@ -69,6 +68,7 @@ import android.os.SystemProperties;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArraySet;
+import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -89,9 +89,9 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
-import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* Class for managing the recent tasks list. The list is ordered by most recent (index 0) to the
@@ -167,8 +167,9 @@ class RecentTasks {
/**
* Mapping of user id -> whether recent tasks have been loaded for that user.
+ * The AtomicBoolean per user will be locked when reading persisted task from storage.
*/
- private final SparseBooleanArray mUsersWithRecentsLoaded = new SparseBooleanArray(
+ private final SparseArray<AtomicBoolean> mUsersWithRecentsLoaded = new SparseArray<>(
DEFAULT_INITIAL_CAPACITY);
/**
@@ -481,29 +482,49 @@ class RecentTasks {
/**
* Loads the persistent recentTasks for {@code userId} into this list from persistent storage.
- * Does nothing if they are already loaded.
- *
- * @param userId the user Id
+ * Does nothing if they are already loaded. This may perform IO operation, so the caller should
+ * not hold a lock.
*/
- void loadUserRecentsLocked(int userId) {
- if (mUsersWithRecentsLoaded.get(userId)) {
- // User already loaded, return early
- return;
+ void loadRecentTasksIfNeeded(int userId) {
+ AtomicBoolean userLoaded;
+ synchronized (mService.mGlobalLock) {
+ userLoaded = mUsersWithRecentsLoaded.get(userId);
+ if (userLoaded == null) {
+ mUsersWithRecentsLoaded.append(userId, userLoaded = new AtomicBoolean());
+ }
}
+ synchronized (userLoaded) {
+ if (userLoaded.get()) {
+ // The recent tasks of the user are already loaded.
+ return;
+ }
+ // Read task files from storage.
+ final SparseBooleanArray persistedTaskIds =
+ mTaskPersister.readPersistedTaskIdsFromFileForUser(userId);
+ final TaskPersister.RecentTaskFiles taskFiles = TaskPersister.loadTasksForUser(userId);
+ synchronized (mService.mGlobalLock) {
+ restoreRecentTasksLocked(userId, persistedTaskIds, taskFiles);
+ }
+ userLoaded.set(true);
+ }
+ }
- // Load the task ids if not loaded.
- loadPersistedTaskIdsForUserLocked(userId);
-
- // Check if any tasks are added before recents is loaded
- final SparseBooleanArray preaddedTasks = new SparseBooleanArray();
- for (final Task task : mTasks) {
+ /** Restores recent tasks from raw data (the files are already read into memory). */
+ private void restoreRecentTasksLocked(int userId, SparseBooleanArray persistedTaskIds,
+ TaskPersister.RecentTaskFiles taskFiles) {
+ mTaskPersister.setPersistedTaskIds(userId, persistedTaskIds);
+ mPersistedTaskIds.put(userId, persistedTaskIds.clone());
+ // Check if any tasks are added before recents is loaded.
+ final IntArray existedTaskIds = new IntArray();
+ for (int i = mTasks.size() - 1; i >= 0; i--) {
+ final Task task = mTasks.get(i);
if (task.mUserId == userId && shouldPersistTaskLocked(task)) {
- preaddedTasks.put(task.mTaskId, true);
+ existedTaskIds.add(task.mTaskId);
}
}
-
- Slog.i(TAG, "Loading recents for user " + userId + " into memory.");
- List<Task> tasks = mTaskPersister.restoreTasksForUserLocked(userId, preaddedTasks);
+ Slog.i(TAG, "Restoring recents for user " + userId);
+ final ArrayList<Task> tasks = mTaskPersister.restoreTasksForUserLocked(userId, taskFiles,
+ existedTaskIds);
// Tasks are ordered from most recent to least recent. Update the last active time to be
// in sync with task recency when device reboots, so the most recent task has the
@@ -516,37 +537,34 @@ class RecentTasks {
mTasks.addAll(tasks);
cleanupLocked(userId);
- mUsersWithRecentsLoaded.put(userId, true);
// If we have tasks added before loading recents, we need to update persistent task IDs.
- if (preaddedTasks.size() > 0) {
+ if (existedTaskIds.size() > 0) {
syncPersistentTaskIdsLocked();
}
}
- private void loadPersistedTaskIdsForUserLocked(int userId) {
- // An empty instead of a null set here means that no persistent taskIds were present
- // on file when we loaded them.
- if (mPersistedTaskIds.get(userId) == null) {
- mPersistedTaskIds.put(userId, mTaskPersister.loadPersistedTaskIdsForUser(userId));
- Slog.i(TAG, "Loaded persisted task ids for user " + userId);
- }
+ private boolean isRecentTasksLoaded(int userId) {
+ final AtomicBoolean userLoaded = mUsersWithRecentsLoaded.get(userId);
+ return userLoaded != null && userLoaded.get();
}
/**
* @return whether the {@param taskId} is currently in use for the given user.
*/
boolean containsTaskId(int taskId, int userId) {
- loadPersistedTaskIdsForUserLocked(userId);
- return mPersistedTaskIds.get(userId).get(taskId);
+ final SparseBooleanArray taskIds = mPersistedTaskIds.get(userId);
+ return taskIds != null && taskIds.get(taskId);
}
- /**
- * @return all the task ids for the user with the given {@param userId}.
- */
- SparseBooleanArray getTaskIdsForUser(int userId) {
- loadPersistedTaskIdsForUserLocked(userId);
- return mPersistedTaskIds.get(userId);
+ /** Returns all the task ids for the user from {@link #usersWithRecentsLoadedLocked}. */
+ SparseBooleanArray getTaskIdsForLoadedUser(int loadedUserId) {
+ final SparseBooleanArray taskIds = mPersistedTaskIds.get(loadedUserId);
+ if (taskIds == null) {
+ Slog.wtf(TAG, "Loaded user without loaded tasks, userId=" + loadedUserId);
+ return new SparseBooleanArray();
+ }
+ return taskIds;
}
/**
@@ -565,7 +583,7 @@ class RecentTasks {
private void syncPersistentTaskIdsLocked() {
for (int i = mPersistedTaskIds.size() - 1; i >= 0; i--) {
int userId = mPersistedTaskIds.keyAt(i);
- if (mUsersWithRecentsLoaded.get(userId)) {
+ if (isRecentTasksLoaded(userId)) {
// Recents are loaded only after task ids are loaded. Therefore, the set of taskids
// referenced here should not be null.
mPersistedTaskIds.valueAt(i).clear();
@@ -621,7 +639,7 @@ class RecentTasks {
int len = 0;
for (int i = 0; i < usersWithRecentsLoaded.length; i++) {
int userId = mUsersWithRecentsLoaded.keyAt(i);
- if (mUsersWithRecentsLoaded.valueAt(i)) {
+ if (mUsersWithRecentsLoaded.valueAt(i).get()) {
usersWithRecentsLoaded[len++] = userId;
}
}
@@ -639,7 +657,7 @@ class RecentTasks {
* @param userId the id of the user
*/
void unloadUserDataFromMemoryLocked(int userId) {
- if (mUsersWithRecentsLoaded.get(userId)) {
+ if (isRecentTasksLoaded(userId)) {
Slog.i(TAG, "Unloading recents for user " + userId + " from memory.");
mUsersWithRecentsLoaded.delete(userId);
removeTasksForUserLocked(userId);
@@ -922,11 +940,6 @@ class RecentTasks {
return mService.mAmInternal.getCurrentProfileIds();
}
- @VisibleForTesting
- boolean isUserRunning(int userId, int flags) {
- return mService.mAmInternal.isUserRunning(userId, flags);
- }
-
/**
* @return the list of recent tasks for presentation.
*/
@@ -942,13 +955,6 @@ class RecentTasks {
private ArrayList<ActivityManager.RecentTaskInfo> getRecentTasksImpl(int maxNum, int flags,
boolean getTasksAllowed, int userId, int callingUid) {
final boolean withExcluded = (flags & RECENT_WITH_EXCLUDED) != 0;
-
- if (!isUserRunning(userId, FLAG_AND_UNLOCKED)) {
- Slog.i(TAG, "user " + userId + " is still locked. Cannot load recents");
- return new ArrayList<>();
- }
- loadUserRecentsLocked(userId);
-
final Set<Integer> includedUsers = getProfileIds(userId);
includedUsers.add(Integer.valueOf(userId));
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index a98b9f7cd1d1..3ef6eeb2ecf3 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -44,7 +44,6 @@ import android.view.SurfaceControl.Transaction;
import android.view.WindowManager;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.ProtoLogImpl;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.FastPrintWriter;
import com.android.server.wm.SurfaceAnimator.AnimationType;
@@ -210,7 +209,7 @@ class RemoteAnimationController implements DeathRecipient {
Slog.e(TAG, "Failed to start remote animation", e);
onAnimationFinished();
}
- if (ProtoLogImpl.isEnabled(WM_DEBUG_REMOTE_ANIMATIONS)) {
+ if (ProtoLog.isEnabled(WM_DEBUG_REMOTE_ANIMATIONS)) {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation(): Notify animation start:");
writeStartDebugStatement();
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 609ad1e76370..b562ccfb3d2b 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -612,10 +612,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
void refreshSecureSurfaceState() {
- forAllWindows((w) -> {
- if (w.mHasSurface) {
- w.setSecureLocked(w.isSecureLocked());
- }
+ forAllWindows(w -> {
+ w.setSecureLocked(w.isSecureLocked());
}, true /* traverseTopToBottom */);
}
@@ -1454,7 +1452,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
return false;
}
- if (enableHomeDelay() && !mService.mAmInternal.getThemeOverlayReadiness()) {
+ if (enableHomeDelay() && !mService.mAmInternal.isThemeOverlayReady(userId)) {
Slog.d(TAG, "ThemeHomeDelay: Home launch was deferred.");
return false;
}
@@ -2483,6 +2481,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
if (displayShouldSleep == display.isSleeping()) {
continue;
}
+ final boolean wasSleeping = display.isSleeping();
display.setIsSleeping(displayShouldSleep);
if (display.mTransitionController.isShellTransitionsEnabled()
@@ -2508,9 +2507,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// Use NONE if keyguard is not showing.
int transit = TRANSIT_NONE;
Task startTask = null;
- if (!display.getDisplayPolicy().isAwake()) {
- // Note that currently this only happens on default display because non-default
- // display is always awake.
+ if (wasSleeping) {
transit = TRANSIT_WAKE;
} else if (display.isKeyguardOccluded()) {
// The display was awake so this is resuming activity for occluding keyguard.
diff --git a/services/core/java/com/android/server/wm/SensitiveContentPackages.java b/services/core/java/com/android/server/wm/SensitiveContentPackages.java
index a7d6903bbe30..9ab11b8059a7 100644
--- a/services/core/java/com/android/server/wm/SensitiveContentPackages.java
+++ b/services/core/java/com/android/server/wm/SensitiveContentPackages.java
@@ -16,9 +16,16 @@
package com.android.server.wm;
+import static android.permission.flags.Flags.sensitiveNotificationAppProtection;
+import static android.view.flags.Flags.sensitiveContentAppProtection;
+
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.IBinder;
import android.util.ArraySet;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.PrintWriter;
import java.util.Objects;
@@ -29,12 +36,26 @@ import java.util.Objects;
public class SensitiveContentPackages {
private final ArraySet<PackageInfo> mProtectedPackages = new ArraySet<>();
- /** Returns {@code true} if package/uid pair should be blocked from screen capture */
- public boolean shouldBlockScreenCaptureForApp(String pkg, int uid) {
+ /**
+ * Returns {@code true} if package/uid/window combination should be blocked
+ * from screen capture.
+ */
+ public boolean shouldBlockScreenCaptureForApp(String pkg, int uid, IBinder windowToken) {
+ if (!(sensitiveContentAppProtection() || sensitiveNotificationAppProtection())) {
+ return false;
+ }
+
for (int i = 0; i < mProtectedPackages.size(); i++) {
PackageInfo info = mProtectedPackages.valueAt(i);
if (info != null && info.mPkg.equals(pkg) && info.mUid == uid) {
- return true;
+ // sensitiveContentAppProtection blocks specific window where sensitive content
+ // is rendered, whereas sensitiveNotificationAppProtection blocks the package
+ // if the package has a sensitive notification.
+ if ((sensitiveContentAppProtection() && windowToken == info.getWindowToken())
+ || (sensitiveNotificationAppProtection() && info.getWindowToken() == null)
+ ) {
+ return true;
+ }
}
}
return false;
@@ -56,37 +77,67 @@ public class SensitiveContentPackages {
}
/**
+ * Clears apps added to collection of apps in which screen capture should be disabled.
+ *
+ * @param packageInfos set of {@link PackageInfo} whose windows should be unblocked
+ * from capture.
+ * @return {@code true} if packages set is modified, {@code false} otherwise.
+ */
+ public boolean removeBlockScreenCaptureForApps(@NonNull ArraySet<PackageInfo> packageInfos) {
+ return mProtectedPackages.removeAll(packageInfos);
+ }
+
+ /**
* Clears the set of package/uid pairs that should be blocked from screen capture
*
* @return {@code true} if packages set is modified, {@code false} otherwise.
*/
public boolean clearBlockedApps() {
if (mProtectedPackages.isEmpty()) {
- // set was already empty
return false;
}
mProtectedPackages.clear();
return true;
}
+ /**
+ * @return the size of protected packages.
+ */
+ @VisibleForTesting
+ public int size() {
+ return mProtectedPackages.size();
+ }
+
void dump(PrintWriter pw) {
final String innerPrefix = " ";
pw.println("SensitiveContentPackages:");
pw.println(innerPrefix + "Packages that should block screen capture ("
+ mProtectedPackages.size() + "):");
for (PackageInfo info : mProtectedPackages) {
- pw.println(innerPrefix + " package=" + info.mPkg + " uid=" + info.mUid);
+ pw.println(innerPrefix + " package=" + info.mPkg + " uid=" + info.mUid
+ + " windowToken=" + info.mWindowToken);
}
}
- /** Helper class that represents a package/uid pair */
+ /**
+ * Helper class that represents a package, uid, and window token combination, window token
+ * is set to block screen capture at window level.
+ */
public static class PackageInfo {
- private String mPkg;
- private int mUid;
+ private final String mPkg;
+ private final int mUid;
+
+ @Nullable
+ private final IBinder mWindowToken;
public PackageInfo(String pkg, int uid) {
+ this(pkg, uid, null);
+ }
+
+ public PackageInfo(String pkg, int uid, IBinder windowToken) {
this.mPkg = pkg;
this.mUid = uid;
+ this.mWindowToken = windowToken;
}
@Override
@@ -94,12 +145,30 @@ public class SensitiveContentPackages {
if (this == o) return true;
if (!(o instanceof PackageInfo)) return false;
PackageInfo that = (PackageInfo) o;
- return mUid == that.mUid && Objects.equals(mPkg, that.mPkg);
+ return mUid == that.mUid && Objects.equals(mPkg, that.mPkg)
+ && Objects.equals(mWindowToken, that.mWindowToken);
}
@Override
public int hashCode() {
- return Objects.hash(mPkg, mUid);
+ return Objects.hash(mPkg, mUid, mWindowToken);
+ }
+
+ public IBinder getWindowToken() {
+ return mWindowToken;
+ }
+
+ public int getUid() {
+ return mUid;
+ }
+
+ public String getPkg() {
+ return mPkg;
+ }
+
+ @Override
+ public String toString() {
+ return "package=" + mPkg + " uid=" + mUid + " windowToken=" + mWindowToken;
}
}
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 3c8c55e278e3..908cbd340236 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -110,9 +110,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
private final String mStringName;
SurfaceSession mSurfaceSession;
private final ArrayList<WindowState> mAddedWindows = new ArrayList<>();
- // Set of visible application overlay window surfaces connected to this session.
- private final ArraySet<WindowSurfaceController> mAppOverlaySurfaces = new ArraySet<>();
- // Set of visible alert window surfaces connected to this session.
+ /** Set of visible alert/app-overlay window surfaces connected to this session. */
private final ArraySet<WindowSurfaceController> mAlertWindowSurfaces = new ArraySet<>();
private final DragDropController mDragDropController;
final boolean mCanAddInternalSystemWindow;
@@ -796,46 +794,45 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
}
boolean changed;
-
- if (!mCanAddInternalSystemWindow && !mCanCreateSystemApplicationOverlay) {
- // We want to track non-system apps adding alert windows so we can post an
- // on-going notification for the user to control their visibility.
- if (visible) {
- changed = mAlertWindowSurfaces.add(surfaceController);
- MetricsLoggerWrapper.logAppOverlayEnter(mUid, mPackageName, changed, type, true);
- } else {
- changed = mAlertWindowSurfaces.remove(surfaceController);
- MetricsLoggerWrapper.logAppOverlayExit(mUid, mPackageName, changed, type, true);
+ // Track non-system apps adding overlay/alert windows, so a notification can post for the
+ // user to control their visibility.
+ final boolean noSystemOverlayPermission =
+ !mCanAddInternalSystemWindow && !mCanCreateSystemApplicationOverlay;
+ if (visible) {
+ changed = mAlertWindowSurfaces.add(surfaceController);
+ if (type == TYPE_APPLICATION_OVERLAY) {
+ MetricsLoggerWrapper.logAppOverlayEnter(mUid, mPackageName, changed, type,
+ false /* set false to only log for TYPE_APPLICATION_OVERLAY */);
+ } else if (noSystemOverlayPermission) {
+ MetricsLoggerWrapper.logAppOverlayEnter(mUid, mPackageName, changed, type,
+ true /* only log for non-TYPE_APPLICATION_OVERLAY */);
}
-
- if (changed) {
- if (mAlertWindowSurfaces.isEmpty()) {
- cancelAlertWindowNotification();
- } else if (mAlertWindowNotification == null){
- mAlertWindowNotification = new AlertWindowNotification(mService, mPackageName);
- if (mShowingAlertWindowNotificationAllowed) {
- mAlertWindowNotification.post();
- }
- }
+ } else {
+ changed = mAlertWindowSurfaces.remove(surfaceController);
+ if (type == TYPE_APPLICATION_OVERLAY) {
+ MetricsLoggerWrapper.logAppOverlayExit(mUid, mPackageName, changed, type,
+ false /* set false to only log for TYPE_APPLICATION_OVERLAY */);
+ } else if (noSystemOverlayPermission) {
+ MetricsLoggerWrapper.logAppOverlayExit(mUid, mPackageName, changed, type,
+ true /* only log for non-TYPE_APPLICATION_OVERLAY */);
}
}
- if (type != TYPE_APPLICATION_OVERLAY) {
- return;
- }
-
- if (visible) {
- changed = mAppOverlaySurfaces.add(surfaceController);
- MetricsLoggerWrapper.logAppOverlayEnter(mUid, mPackageName, changed, type, false);
- } else {
- changed = mAppOverlaySurfaces.remove(surfaceController);
- MetricsLoggerWrapper.logAppOverlayExit(mUid, mPackageName, changed, type, false);
+ if (changed && noSystemOverlayPermission) {
+ if (mAlertWindowSurfaces.isEmpty()) {
+ cancelAlertWindowNotification();
+ } else if (mAlertWindowNotification == null) {
+ mAlertWindowNotification = new AlertWindowNotification(mService, mPackageName);
+ if (mShowingAlertWindowNotificationAllowed) {
+ mAlertWindowNotification.post();
+ }
+ }
}
- if (changed) {
- // Notify activity manager of changes to app overlay windows so it can adjust the
- // importance score for the process.
- setHasOverlayUi(!mAppOverlaySurfaces.isEmpty());
+ if (changed && mPid != WindowManagerService.MY_PID) {
+ // Notify activity manager that the process contains overlay/alert windows, so it can
+ // adjust the importance score for the process.
+ setHasOverlayUi(!mAlertWindowSurfaces.isEmpty());
}
}
@@ -870,12 +867,12 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
mSurfaceSession = null;
mAddedWindows.clear();
mAlertWindowSurfaces.clear();
- mAppOverlaySurfaces.clear();
setHasOverlayUi(false);
cancelAlertWindowNotification();
}
- private void setHasOverlayUi(boolean hasOverlayUi) {
+ @VisibleForTesting
+ void setHasOverlayUi(boolean hasOverlayUi) {
mService.mH.obtainMessage(H.SET_HAS_OVERLAY_UI, mPid, hasOverlayUi ? 1 : 0).sendToTarget();
}
@@ -890,7 +887,6 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("numWindow="); pw.print(mAddedWindows.size());
pw.print(" mCanAddInternalSystemWindow="); pw.print(mCanAddInternalSystemWindow);
- pw.print(" mAppOverlaySurfaces="); pw.print(mAppOverlaySurfaces);
pw.print(" mAlertWindowSurfaces="); pw.print(mAlertWindowSurfaces);
pw.print(" mClientDead="); pw.print(mClientDead);
pw.print(" mSurfaceSession="); pw.println(mSurfaceSession);
@@ -970,40 +966,6 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
}
@Override
- public boolean transferEmbeddedTouchFocusToHost(IWindow embeddedWindow) {
- if (embeddedWindow == null) {
- return false;
- }
-
- final long identity = Binder.clearCallingIdentity();
- boolean didTransfer = false;
- try {
- didTransfer = mService.transferEmbeddedTouchFocusToHost(embeddedWindow);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- return didTransfer;
- }
-
- @Override
- public boolean transferHostTouchGestureToEmbedded(IWindow hostWindow,
- InputTransferToken inputTransferToken) {
- if (hostWindow == null) {
- return false;
- }
-
- final long identity = Binder.clearCallingIdentity();
- boolean didTransfer;
- try {
- didTransfer = mService.transferHostTouchGestureToEmbedded(this, hostWindow,
- inputTransferToken);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- return didTransfer;
- }
-
- @Override
public boolean moveFocusToAdjacentWindow(IWindow fromWindow, @FocusDirection int direction) {
final long identity = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/wm/SnapshotCache.java b/services/core/java/com/android/server/wm/SnapshotCache.java
index 401b2604c28f..86804360f6f4 100644
--- a/services/core/java/com/android/server/wm/SnapshotCache.java
+++ b/services/core/java/com/android/server/wm/SnapshotCache.java
@@ -19,6 +19,8 @@ import android.annotation.Nullable;
import android.util.ArrayMap;
import android.window.TaskSnapshot;
+import com.android.internal.annotations.GuardedBy;
+
import java.io.PrintWriter;
/**
@@ -26,25 +28,31 @@ import java.io.PrintWriter;
* @param <TYPE> The basic type, either Task or ActivityRecord
*/
abstract class SnapshotCache<TYPE extends WindowContainer> {
- protected final WindowManagerService mService;
+ protected final Object mLock = new Object();
+
protected final String mName;
+
+ @GuardedBy("mLock")
protected final ArrayMap<ActivityRecord, Integer> mAppIdMap = new ArrayMap<>();
+
+ @GuardedBy("mLock")
protected final ArrayMap<Integer, CacheEntry> mRunningCache = new ArrayMap<>();
- SnapshotCache(WindowManagerService service, String name) {
- mService = service;
+ SnapshotCache(String name) {
mName = name;
}
abstract void putSnapshot(TYPE window, TaskSnapshot snapshot);
void clearRunningCache() {
- mRunningCache.clear();
+ synchronized (mLock) {
+ mRunningCache.clear();
+ }
}
@Nullable
final TaskSnapshot getSnapshot(Integer id) {
- synchronized (mService.mGlobalLock) {
+ synchronized (mLock) {
// Try the running cache.
final CacheEntry entry = mRunningCache.get(id);
if (entry != null) {
@@ -56,17 +64,21 @@ abstract class SnapshotCache<TYPE extends WindowContainer> {
/** Called when an app token has been removed. */
void onAppRemoved(ActivityRecord activity) {
- final Integer id = mAppIdMap.get(activity);
- if (id != null) {
- removeRunningEntry(id);
+ synchronized (mLock) {
+ final Integer id = mAppIdMap.get(activity);
+ if (id != null) {
+ removeRunningEntry(id);
+ }
}
}
/** Called when an app window token's process died. */
void onAppDied(ActivityRecord activity) {
- final Integer id = mAppIdMap.get(activity);
- if (id != null) {
- removeRunningEntry(id);
+ synchronized (mLock) {
+ final Integer id = mAppIdMap.get(activity);
+ if (id != null) {
+ removeRunningEntry(id);
+ }
}
}
@@ -75,10 +87,12 @@ abstract class SnapshotCache<TYPE extends WindowContainer> {
}
void removeRunningEntry(Integer id) {
- final CacheEntry entry = mRunningCache.get(id);
- if (entry != null) {
- mAppIdMap.remove(entry.topApp);
- mRunningCache.remove(id);
+ synchronized (mLock) {
+ final CacheEntry entry = mRunningCache.get(id);
+ if (entry != null) {
+ mAppIdMap.remove(entry.topApp);
+ mRunningCache.remove(id);
+ }
}
}
@@ -86,11 +100,14 @@ abstract class SnapshotCache<TYPE extends WindowContainer> {
final String doublePrefix = prefix + " ";
final String triplePrefix = doublePrefix + " ";
pw.println(prefix + "SnapshotCache " + mName);
- for (int i = mRunningCache.size() - 1; i >= 0; i--) {
- final CacheEntry entry = mRunningCache.valueAt(i);
- pw.println(doublePrefix + "Entry token=" + mRunningCache.keyAt(i));
- pw.println(triplePrefix + "topApp=" + entry.topApp);
- pw.println(triplePrefix + "snapshot=" + entry.snapshot);
+
+ synchronized (mLock) {
+ for (int i = mRunningCache.size() - 1; i >= 0; i--) {
+ final CacheEntry entry = mRunningCache.valueAt(i);
+ pw.println(doublePrefix + "Entry token=" + mRunningCache.keyAt(i));
+ pw.println(triplePrefix + "topApp=" + entry.topApp);
+ pw.println(triplePrefix + "snapshot=" + entry.snapshot);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index c3de4d5acc21..d67684c7038e 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -32,7 +32,6 @@ import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.ProtoLogImpl;
import com.android.internal.protolog.common.ProtoLog;
import java.io.PrintWriter;
@@ -193,7 +192,7 @@ public class SurfaceAnimator {
return;
}
mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);
- if (ProtoLogImpl.isEnabled(WM_DEBUG_ANIM)) {
+ if (ProtoLog.isEnabled(WM_DEBUG_ANIM)) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
mAnimation.dump(pw, "");
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 838ce86515cd..85d81c4db2ca 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -104,6 +104,7 @@ import android.window.TaskFragmentOrganizerToken;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.util.ToBooleanFunction;
import com.android.server.am.HostingRecord;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.window.flags.Flags;
@@ -1590,7 +1591,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
mAtmService.getLifecycleManager().scheduleTransactionItem(
appThread, activityResultItem);
} else {
- transaction.addCallback(activityResultItem);
+ transaction.addTransactionItem(activityResultItem);
}
}
}
@@ -1602,7 +1603,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
mAtmService.getLifecycleManager().scheduleTransactionItem(
appThread, newIntentItem);
} else {
- transaction.addCallback(newIntentItem);
+ transaction.addTransactionItem(newIntentItem);
}
}
@@ -1624,7 +1625,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
mAtmService.getLifecycleManager().scheduleTransactionItem(
appThread, resumeActivityItem);
} else {
- transaction.setLifecycleStateRequest(resumeActivityItem);
+ transaction.addTransactionItem(resumeActivityItem);
mAtmService.getLifecycleManager().scheduleTransaction(transaction);
}
@@ -3025,11 +3026,17 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return false;
}
- // boost if there's an Activity window that has FLAG_DIM_BEHIND flag.
- return forAllWindows(
+ ToBooleanFunction<WindowState> getDimBehindWindow =
(w) -> (w.mAttrs.flags & FLAG_DIM_BEHIND) != 0 && w.mActivityRecord != null
&& w.mActivityRecord.isEmbedded() && (w.mActivityRecord.isVisibleRequested()
- || w.mActivityRecord.isVisible()), true);
+ || w.mActivityRecord.isVisible());
+ if (adjacentTf.forAllWindows(getDimBehindWindow, true)) {
+ // early return if the adjacent Tf has a dimming window.
+ return false;
+ }
+
+ // boost if there's an Activity window that has FLAG_DIM_BEHIND flag.
+ return forAllWindows(getDimBehindWindow, true);
}
@Override
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 8d054db8994a..24b533a23af6 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -1197,16 +1197,13 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
}
}
- // TODO(b/204399167): change to push the embedded state to the client side
@Override
public boolean isActivityEmbedded(IBinder activityToken) {
synchronized (mGlobalLock) {
final ActivityRecord activity = ActivityRecord.forTokenLocked(activityToken);
- if (activity == null) {
- return false;
- }
- final TaskFragment taskFragment = activity.getOrganizedTaskFragment();
- return taskFragment != null && taskFragment.isEmbeddedWithBoundsOverride();
+ return activity != null
+ ? activity.isEmbeddedInHostContainer()
+ : false;
}
}
diff --git a/services/core/java/com/android/server/wm/TaskPersister.java b/services/core/java/com/android/server/wm/TaskPersister.java
index 5ec0119725cb..d89dc0b8e81c 100644
--- a/services/core/java/com/android/server/wm/TaskPersister.java
+++ b/services/core/java/com/android/server/wm/TaskPersister.java
@@ -27,6 +27,7 @@ import android.os.FileUtils;
import android.os.SystemClock;
import android.util.ArraySet;
import android.util.AtomicFile;
+import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -43,20 +44,20 @@ import org.xmlpull.v1.XmlPullParser;
import java.io.BufferedReader;
import java.io.BufferedWriter;
+import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
-import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
-import java.util.List;
/**
* Persister that saves recent tasks into disk.
@@ -129,11 +130,9 @@ public class TaskPersister implements PersisterQueue.Listener {
ImageWriteQueueItem.class);
}
+ /** Reads task ids from file. This should not be called in lock. */
@NonNull
- SparseBooleanArray loadPersistedTaskIdsForUser(int userId) {
- if (mTaskIdsInFile.get(userId) != null) {
- return mTaskIdsInFile.get(userId).clone();
- }
+ SparseBooleanArray readPersistedTaskIdsFromFileForUser(int userId) {
final SparseBooleanArray persistedTaskIds = new SparseBooleanArray();
synchronized (mIoLock) {
BufferedReader reader = null;
@@ -154,11 +153,10 @@ public class TaskPersister implements PersisterQueue.Listener {
IoUtils.closeQuietly(reader);
}
}
- mTaskIdsInFile.put(userId, persistedTaskIds);
- return persistedTaskIds.clone();
+ Slog.i(TAG, "Loaded persisted task ids for user " + userId);
+ return persistedTaskIds;
}
-
@VisibleForTesting
void writePersistedTaskIdsForUser(@NonNull SparseBooleanArray taskIds, int userId) {
if (userId < 0) {
@@ -183,6 +181,10 @@ public class TaskPersister implements PersisterQueue.Listener {
}
}
+ void setPersistedTaskIds(int userId, @NonNull SparseBooleanArray taskIds) {
+ mTaskIdsInFile.put(userId, taskIds);
+ }
+
void unloadUserDataFromMemory(int userId) {
mTaskIdsInFile.delete(userId);
}
@@ -241,7 +243,7 @@ public class TaskPersister implements PersisterQueue.Listener {
return item != null ? item.mImage : null;
}
- private String fileToString(File file) {
+ private static String fileToString(File file) {
final String newline = System.lineSeparator();
try {
BufferedReader reader = new BufferedReader(new FileReader(file));
@@ -272,44 +274,64 @@ public class TaskPersister implements PersisterQueue.Listener {
return null;
}
- List<Task> restoreTasksForUserLocked(final int userId, SparseBooleanArray preaddedTasks) {
- final ArrayList<Task> tasks = new ArrayList<Task>();
- ArraySet<Integer> recoveredTaskIds = new ArraySet<Integer>();
-
- File userTasksDir = getUserTasksDir(userId);
-
- File[] recentFiles = userTasksDir.listFiles();
+ /** Loads task files from disk. This should not be called in lock. */
+ static RecentTaskFiles loadTasksForUser(int userId) {
+ final ArrayList<RecentTaskFile> taskFiles = new ArrayList<>();
+ final File userTasksDir = getUserTasksDir(userId);
+ final File[] recentFiles = userTasksDir.listFiles();
if (recentFiles == null) {
- Slog.e(TAG, "restoreTasksForUserLocked: Unable to list files from " + userTasksDir);
- return tasks;
+ Slog.i(TAG, "loadTasksForUser: Unable to list files from " + userTasksDir
+ + " exists=" + userTasksDir.exists());
+ return new RecentTaskFiles(new File[0], taskFiles);
}
-
- for (int taskNdx = 0; taskNdx < recentFiles.length; ++taskNdx) {
- File taskFile = recentFiles[taskNdx];
- if (DEBUG) {
- Slog.d(TAG, "restoreTasksForUserLocked: userId=" + userId
- + ", taskFile=" + taskFile.getName());
- }
-
+ for (File taskFile : recentFiles) {
if (!taskFile.getName().endsWith(TASK_FILENAME_SUFFIX)) {
continue;
}
+ final int taskId;
try {
- final int taskId = Integer.parseInt(taskFile.getName().substring(
+ taskId = Integer.parseInt(taskFile.getName().substring(
0 /* beginIndex */,
taskFile.getName().length() - TASK_FILENAME_SUFFIX.length()));
- if (preaddedTasks.get(taskId, false)) {
- Slog.w(TAG, "Task #" + taskId +
- " has already been created so we don't restore again");
- continue;
- }
} catch (NumberFormatException e) {
Slog.w(TAG, "Unexpected task file name", e);
continue;
}
+ try {
+ taskFiles.add(new RecentTaskFile(taskId, taskFile));
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed to read file: " + fileToString(taskFile), e);
+ taskFile.delete();
+ }
+ }
+ return new RecentTaskFiles(recentFiles, taskFiles);
+ }
+
+ /** Restores tasks from raw bytes (no read storage operation). */
+ ArrayList<Task> restoreTasksForUserLocked(int userId, RecentTaskFiles recentTaskFiles,
+ IntArray existedTaskIds) {
+ final ArrayList<Task> tasks = new ArrayList<>();
+ final ArrayList<RecentTaskFile> taskFiles = recentTaskFiles.mLoadedFiles;
+ if (taskFiles.isEmpty()) {
+ return tasks;
+ }
+
+ final ArraySet<Integer> recoveredTaskIds = new ArraySet<>();
+ for (int taskNdx = 0; taskNdx < taskFiles.size(); ++taskNdx) {
+ final RecentTaskFile recentTask = taskFiles.get(taskNdx);
+ if (existedTaskIds.contains(recentTask.mTaskId)) {
+ Slog.w(TAG, "Task #" + recentTask.mTaskId
+ + " has already been created, so skip restoring");
+ continue;
+ }
+ final File taskFile = recentTask.mFile;
+ if (DEBUG) {
+ Slog.d(TAG, "restoreTasksForUserLocked: userId=" + userId
+ + ", taskFile=" + taskFile.getName());
+ }
boolean deleteFile = false;
- try (InputStream is = new FileInputStream(taskFile)) {
+ try (InputStream is = recentTask.mXmlContent) {
final TypedXmlPullParser in = Xml.resolvePullParser(is);
int event;
@@ -345,7 +367,7 @@ public class TaskPersister implements PersisterQueue.Listener {
} else if (userId != task.mUserId) {
// Should not happen.
Slog.wtf(TAG, "Task with userId " + task.mUserId + " found in "
- + userTasksDir.getAbsolutePath());
+ + taskFile.getAbsolutePath());
} else {
// Looks fine.
mTaskSupervisor.setNextTaskIdForUser(taskId, userId);
@@ -377,7 +399,7 @@ public class TaskPersister implements PersisterQueue.Listener {
}
if (!DEBUG) {
- removeObsoleteFiles(recoveredTaskIds, userTasksDir.listFiles());
+ removeObsoleteFiles(recoveredTaskIds, recentTaskFiles.mUserTaskFiles);
}
// Fix up task affiliation from taskIds
@@ -456,7 +478,7 @@ public class TaskPersister implements PersisterQueue.Listener {
SparseArray<SparseBooleanArray> changedTaskIdsPerUser = new SparseArray<>();
synchronized (mService.mGlobalLock) {
for (int userId : mRecentTasks.usersWithRecentsLoadedLocked()) {
- SparseBooleanArray taskIdsToSave = mRecentTasks.getTaskIdsForUser(userId);
+ SparseBooleanArray taskIdsToSave = mRecentTasks.getTaskIdsForLoadedUser(userId);
SparseBooleanArray persistedIdsInFile = mTaskIdsInFile.get(userId);
if (persistedIdsInFile != null && persistedIdsInFile.equals(taskIdsToSave)) {
continue;
@@ -512,6 +534,30 @@ public class TaskPersister implements PersisterQueue.Listener {
return parentDir.isDirectory() || parentDir.mkdir();
}
+ private static class RecentTaskFile {
+ final int mTaskId;
+ final File mFile;
+ final ByteArrayInputStream mXmlContent;
+
+ RecentTaskFile(int taskId, File file) throws IOException {
+ mTaskId = taskId;
+ mFile = file;
+ mXmlContent = new ByteArrayInputStream(Files.readAllBytes(file.toPath()));
+ }
+ }
+
+ static class RecentTaskFiles {
+ /** All files under the user task directory. */
+ final File[] mUserTaskFiles;
+ /** The successfully loaded files. */
+ final ArrayList<RecentTaskFile> mLoadedFiles;
+
+ RecentTaskFiles(File[] userFiles, ArrayList<RecentTaskFile> loadedFiles) {
+ mUserTaskFiles = userFiles;
+ mLoadedFiles = loadedFiles;
+ }
+ }
+
private static class TaskWriteQueueItem implements PersisterQueue.WriteQueueItem {
private final ActivityTaskManagerService mService;
private final Task mTask;
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
index 55a3decdca08..6f548ab01d74 100644
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -198,14 +198,14 @@ class TaskPositioningController {
// resizing/scrolling are not sent to the app. 'win' is the main window
// of the app, it may not have focus since there might be other windows
// on top (eg. a dialog window).
- WindowState transferFocusFromWin = win;
+ WindowState transferTouchFromWin = win;
if (displayContent.mCurrentFocus != null && displayContent.mCurrentFocus != win
&& displayContent.mCurrentFocus.mActivityRecord == win.mActivityRecord) {
- transferFocusFromWin = displayContent.mCurrentFocus;
+ transferTouchFromWin = displayContent.mCurrentFocus;
}
- if (!mService.mInputManager.transferTouchFocus(
- transferFocusFromWin.mInputChannel, mTaskPositioner.mClientChannel,
- false /* isDragDrop */)) {
+ if (!mService.mInputManager.transferTouchGesture(
+ transferTouchFromWin.mInputChannel.getToken(),
+ mTaskPositioner.mClientChannel.getToken())) {
Slog.e(TAG_WM, "startPositioningLocked: Unable to transfer touch focus");
cleanUpTaskPositioner();
return false;
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotCache.java b/services/core/java/com/android/server/wm/TaskSnapshotCache.java
index 33486ccb995f..b69ac1bb2795 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotCache.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotCache.java
@@ -28,19 +28,21 @@ class TaskSnapshotCache extends SnapshotCache<Task> {
private final AppSnapshotLoader mLoader;
- TaskSnapshotCache(WindowManagerService service, AppSnapshotLoader loader) {
- super(service, "Task");
+ TaskSnapshotCache(AppSnapshotLoader loader) {
+ super("Task");
mLoader = loader;
}
void putSnapshot(Task task, TaskSnapshot snapshot) {
- final CacheEntry entry = mRunningCache.get(task.mTaskId);
- if (entry != null) {
- mAppIdMap.remove(entry.topApp);
+ synchronized (mLock) {
+ final CacheEntry entry = mRunningCache.get(task.mTaskId);
+ if (entry != null) {
+ mAppIdMap.remove(entry.topApp);
+ }
+ final ActivityRecord top = task.getTopMostActivity();
+ mAppIdMap.put(top, task.mTaskId);
+ mRunningCache.put(task.mTaskId, new CacheEntry(snapshot, top));
}
- final ActivityRecord top = task.getTopMostActivity();
- mAppIdMap.put(top, task.mTaskId);
- mRunningCache.put(task.mTaskId, new CacheEntry(snapshot, top));
}
/**
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index d8e18e47fa89..4218f8f88a07 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -68,7 +68,7 @@ class TaskSnapshotController extends AbsAppSnapshotController<Task, TaskSnapshot
Environment::getDataSystemCeDirectory);
mPersister = new TaskSnapshotPersister(persistQueue, mPersistInfoProvider);
- initialize(new TaskSnapshotCache(service, new AppSnapshotLoader(mPersistInfoProvider)));
+ initialize(new TaskSnapshotCache(new AppSnapshotLoader(mPersistInfoProvider)));
final boolean snapshotEnabled =
!service.mContext
.getResources()
@@ -270,6 +270,11 @@ class TaskSnapshotController extends AbsAppSnapshotController<Task, TaskSnapshot
return source.getTaskDescription();
}
+ @Override
+ protected Rect getLetterboxInsets(ActivityRecord topActivity) {
+ return topActivity.getLetterboxInsets();
+ }
+
void getClosingTasksInner(Task task, ArraySet<Task> outClosingTasks) {
// Since RecentsAnimation will handle task snapshot while switching apps with the
// best capture timing (e.g. IME window capture),
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 5d9c42d4c3a8..7edc3a2b9786 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -691,12 +691,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
recordDisplay(wc.getDisplayContent());
if (info.mShowWallpaper) {
// Collect the wallpaper token (for isWallpaper(wc)) so it is part of the sync set.
- final List<WindowState> wallpapers =
- wc.getDisplayContent().mWallpaperController.getAllTopWallpapers();
- for (int i = wallpapers.size() - 1; i >= 0; i--) {
- WindowState wallpaper = wallpapers.get(i);
- collect(wallpaper.mToken);
- }
+ wc.mDisplayContent.mWallpaperController.collectTopWallpapers(this);
}
}
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 70775530d0e2..503f925bf557 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -267,7 +267,8 @@ class TransitionController {
mSyncEngine.addOnIdleListener(this::tryStartCollectFromQueue);
}
- private void detachPlayer() {
+ @VisibleForTesting
+ void detachPlayer() {
if (mTransitionPlayer == null) return;
// Immediately set to null so that nothing inadvertently starts/queues.
mTransitionPlayer = null;
diff --git a/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java b/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java
index 817901f96ad4..fa2d9bf21dee 100644
--- a/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java
+++ b/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java
@@ -322,15 +322,17 @@ public class TrustedPresentationListenerController {
var listener = trustedPresentationInfo.mListener;
boolean lastState = trustedPresentationInfo.mLastComputedTrustedPresentationState;
boolean newState =
- (alpha >= trustedPresentationInfo.mThresholds.minAlpha) && (fractionRendered
- >= trustedPresentationInfo.mThresholds.minFractionRendered);
+ (alpha >= trustedPresentationInfo.mThresholds.getMinAlpha())
+ && (fractionRendered >= trustedPresentationInfo.mThresholds
+ .getMinFractionRendered());
trustedPresentationInfo.mLastComputedTrustedPresentationState = newState;
ProtoLog.v(WM_DEBUG_TPL,
"lastState=%s newState=%s alpha=%f minAlpha=%f fractionRendered=%f "
+ "minFractionRendered=%f",
- lastState, newState, alpha, trustedPresentationInfo.mThresholds.minAlpha,
- fractionRendered, trustedPresentationInfo.mThresholds.minFractionRendered);
+ lastState, newState, alpha, trustedPresentationInfo.mThresholds.getMinAlpha(),
+ fractionRendered, trustedPresentationInfo.mThresholds
+ .getMinFractionRendered());
if (lastState && !newState) {
// We were in the trusted presentation state, but now we left it,
@@ -350,13 +352,15 @@ public class TrustedPresentationListenerController {
trustedPresentationInfo.mEnteredTrustedPresentationStateTime = currTimeMs;
mHandler.postDelayed(() -> {
computeTpl(mLastWindowHandles);
- }, (long) (trustedPresentationInfo.mThresholds.stabilityRequirementMs * 1.5));
+ }, (long) (trustedPresentationInfo.mThresholds
+ .getStabilityRequirementMillis() * 1.5));
}
// Has the timer elapsed, but we are still in the state? Emit a callback if needed
if (!trustedPresentationInfo.mLastReportedTrustedPresentationState && newState && (
currTimeMs - trustedPresentationInfo.mEnteredTrustedPresentationStateTime
- > trustedPresentationInfo.mThresholds.stabilityRequirementMs)) {
+ > trustedPresentationInfo.mThresholds
+ .getStabilityRequirementMillis())) {
trustedPresentationInfo.mLastReportedTrustedPresentationState = true;
addListenerUpdate(listenerUpdates, listener,
trustedPresentationInfo.mId, /*presentationState*/ true);
@@ -413,15 +417,6 @@ public class TrustedPresentationListenerController {
mThresholds = thresholds;
mId = id;
mListener = listener;
- checkValid(thresholds);
- }
-
- private void checkValid(TrustedPresentationThresholds thresholds) {
- if (thresholds.minAlpha <= 0 || thresholds.minFractionRendered <= 0
- || thresholds.stabilityRequirementMs < 1) {
- throw new IllegalArgumentException(
- "TrustedPresentationThresholds values are invalid");
- }
}
}
}
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 6949a874b533..594043d380c9 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -23,7 +23,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WALLPAPER;
@@ -55,12 +54,10 @@ import android.view.Display;
import android.view.DisplayInfo;
import android.view.SurfaceControl;
import android.view.WindowManager;
-import android.view.animation.Animation;
import android.window.ScreenCapture;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.ProtoLogImpl;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ToBooleanFunction;
import com.android.server.wallpaper.WallpaperCropper.WallpaperCropUtils;
@@ -80,6 +77,7 @@ class WallpaperController {
private WallpaperCropUtils mWallpaperCropUtils = null;
private DisplayContent mDisplayContent;
+ // Larger index has higher z-order.
private final ArrayList<WallpaperWindowToken> mWallpaperTokens = new ArrayList<>();
// If non-null, this is the currently visible window that is associated
@@ -126,18 +124,6 @@ class WallpaperController {
*/
private volatile boolean mIsWallpaperNotifiedOnDisplaySwitch;
- private final Consumer<WindowState> mFindWallpapers = w -> {
- if (w.mAttrs.type == TYPE_WALLPAPER) {
- WallpaperWindowToken token = w.mToken.asWallpaperToken();
- if (token.canShowWhenLocked() && !mFindResults.hasTopShowWhenLockedWallpaper()) {
- mFindResults.setTopShowWhenLockedWallpaper(w);
- } else if (!token.canShowWhenLocked()
- && !mFindResults.hasTopHideWhenLockedWallpaper()) {
- mFindResults.setTopHideWhenLockedWallpaper(w);
- }
- }
- };
-
private final ToBooleanFunction<WindowState> mFindWallpaperTargetFunction = w -> {
final boolean useShellTransition = w.mTransitionController.isShellTransitionsEnabled();
if (!useShellTransition) {
@@ -321,16 +307,6 @@ class WallpaperController {
return false;
}
- /**
- * Starts {@param a} on all wallpaper windows.
- */
- void startWallpaperAnimation(Animation a) {
- for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
- final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
- token.startAnimation(a);
- }
- }
-
boolean isWallpaperTargetAnimating() {
return mWallpaperTarget != null && mWallpaperTarget.isAnimating(TRANSITION | PARENTS)
&& (mWallpaperTarget.mActivityRecord == null
@@ -359,7 +335,7 @@ class WallpaperController {
for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) {
final WallpaperWindowToken token = mWallpaperTokens.get(i);
token.setVisibility(false);
- if (ProtoLogImpl.isEnabled(WM_DEBUG_WALLPAPER) && token.isVisible()) {
+ if (ProtoLog.isEnabled(WM_DEBUG_WALLPAPER) && token.isVisible()) {
ProtoLog.d(WM_DEBUG_WALLPAPER,
"Hiding wallpaper %s from %s target=%s prev=%s callers=%s",
token, winGoingAway, mWallpaperTarget, mPrevWallpaperTarget,
@@ -621,7 +597,8 @@ class WallpaperController {
if (Float.compare(window.mWallpaperZoomOut, zoom) != 0) {
window.mWallpaperZoomOut = zoom;
computeLastWallpaperZoomOut();
- for (WallpaperWindowToken token : mWallpaperTokens) {
+ for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) {
+ final WallpaperWindowToken token = mWallpaperTokens.get(i);
token.updateWallpaperOffset(false);
}
}
@@ -744,7 +721,7 @@ class WallpaperController {
mFindResults.setUseTopWallpaperAsTarget(true);
}
- mDisplayContent.forAllWindows(mFindWallpapers, true /* traverseTopToBottom */);
+ findWallpapers();
mDisplayContent.forAllWindows(mFindWallpaperTargetFunction, true /* traverseTopToBottom */);
if (mFindResults.mNeedsShowWhenLockedWallpaper) {
// Keep wallpaper visible if the show-when-locked activities doesn't fill screen.
@@ -757,15 +734,29 @@ class WallpaperController {
}
}
- List<WindowState> getAllTopWallpapers() {
- ArrayList<WindowState> wallpapers = new ArrayList<>(2);
+ private void findWallpapers() {
+ for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) {
+ final WallpaperWindowToken token = mWallpaperTokens.get(i);
+ final boolean canShowWhenLocked = token.canShowWhenLocked();
+ for (int j = token.getChildCount() - 1; j >= 0; j--) {
+ final WindowState w = token.getChildAt(j);
+ if (!w.mIsWallpaper) continue;
+ if (canShowWhenLocked && !mFindResults.hasTopShowWhenLockedWallpaper()) {
+ mFindResults.setTopShowWhenLockedWallpaper(w);
+ } else if (!canShowWhenLocked && !mFindResults.hasTopHideWhenLockedWallpaper()) {
+ mFindResults.setTopHideWhenLockedWallpaper(w);
+ }
+ }
+ }
+ }
+
+ void collectTopWallpapers(Transition transition) {
if (mFindResults.hasTopShowWhenLockedWallpaper()) {
- wallpapers.add(mFindResults.mTopWallpaper.mTopShowWhenLockedWallpaper);
+ transition.collect(mFindResults.mTopWallpaper.mTopShowWhenLockedWallpaper);
}
if (mFindResults.hasTopHideWhenLockedWallpaper()) {
- wallpapers.add(mFindResults.mTopWallpaper.mTopHideWhenLockedWallpaper);
+ transition.collect(mFindResults.mTopWallpaper.mTopHideWhenLockedWallpaper);
}
- return wallpapers;
}
private boolean isFullscreen(WindowManager.LayoutParams attrs) {
@@ -1016,6 +1007,12 @@ class WallpaperController {
mWallpaperTokens.remove(token);
}
+ void onWallpaperTokenReordered() {
+ if (mWallpaperTokens.size() > 1) {
+ mWallpaperTokens.sort(null /* by WindowContainer#compareTo */);
+ }
+ }
+
@VisibleForTesting
boolean canScreenshotWallpaper() {
return canScreenshotWallpaper(getTopVisibleWallpaper());
@@ -1160,7 +1157,8 @@ class WallpaperController {
pw.print(prefix); pw.print("mPrevWallpaperTarget="); pw.println(mPrevWallpaperTarget);
}
- for (WallpaperWindowToken t : mWallpaperTokens) {
+ for (int i = mWallpaperTokens.size() - 1; i >= 0; i--) {
+ final WallpaperWindowToken t = mWallpaperTokens.get(i);
pw.print(prefix); pw.println("token " + t + ":");
pw.print(prefix); pw.print(" canShowWhenLocked="); pw.println(t.canShowWhenLocked());
dumpValue(pw, prefix, "mWallpaperX", t.mWallpaperX);
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index 1bcd882b5d64..dc500a2748cf 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -30,7 +30,6 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.SparseArray;
-import android.view.animation.Animation;
import com.android.internal.protolog.common.ProtoLog;
@@ -90,16 +89,14 @@ class WallpaperWindowToken extends WindowToken {
return;
}
mShowWhenLocked = showWhenLocked;
- // Move the window token to the front (private) or back (showWhenLocked). This is
- // possible
- // because the DisplayArea underneath TaskDisplayArea only contains TYPE_WALLPAPER
- // windows.
+ // Move the window token to the front (private) or back (showWhenLocked). This is possible
+ // because the DisplayArea underneath TaskDisplayArea only contains TYPE_WALLPAPER windows.
final int position = showWhenLocked ? POSITION_BOTTOM : POSITION_TOP;
- // Note: Moving all the way to the front or back breaks ordering based on addition
- // times.
- // We should never have more than one non-animating token of each type.
+ // Note: Moving all the way to the front or back breaks ordering based on addition times.
+ // There should never have more than one non-animating token of each type.
getParent().positionChildAt(position, this /* child */, false /*includingParents */);
+ mDisplayContent.mWallpaperController.onWallpaperTokenReordered();
}
boolean canShowWhenLocked() {
@@ -139,16 +136,6 @@ class WallpaperWindowToken extends WindowToken {
}
}
- /**
- * Starts {@param anim} on all children.
- */
- void startAnimation(Animation anim) {
- for (int ndx = mChildren.size() - 1; ndx >= 0; ndx--) {
- final WindowState windowState = mChildren.get(ndx);
- windowState.startAnimation(anim);
- }
- }
-
void updateWallpaperWindows(boolean visible) {
if (mVisibleRequested != visible) {
ProtoLog.d(WM_DEBUG_WALLPAPER, "Wallpaper token %s visible=%b",
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 61fde5e31e8f..fd0289edc84b 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -113,7 +113,6 @@ import android.window.WindowContainerToken;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
-import com.android.internal.protolog.ProtoLogImpl;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ToBooleanFunction;
import com.android.server.wm.SurfaceAnimator.Animatable;
@@ -3410,7 +3409,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
// ActivityOption#makeCustomAnimation or WindowManager#overridePendingTransition.
a.restrictDuration(MAX_APP_TRANSITION_DURATION);
}
- if (ProtoLogImpl.isEnabled(WM_DEBUG_ANIM)) {
+ if (ProtoLog.isEnabled(WM_DEBUG_ANIM)) {
ProtoLog.i(WM_DEBUG_ANIM, "Loaded animation %s for %s, duration: %d, stack=%s",
a, this, ((a != null) ? a.getDuration() : 0), Debug.getCallers(20));
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index ae4c3b9a510a..4698b6b2925c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -25,6 +25,7 @@ import android.annotation.UserIdInt;
import android.content.ClipData;
import android.content.Context;
import android.graphics.Matrix;
+import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.display.DisplayManagerInternal;
@@ -158,7 +159,9 @@ public abstract class WindowManagerInternal {
public interface WindowsForAccessibilityCallback {
/**
- * Called when the windows for accessibility changed.
+ * Called when the windows for accessibility changed. This is called if
+ * {@link com.android.server.accessibility.Flags.FLAG_COMPUTE_WINDOW_CHANGES_ON_A11Y} is
+ * false.
*
* @param forceSend Send the windows for accessibility even if they haven't changed.
* @param topFocusedDisplayId The display Id which has the top focused window.
@@ -167,6 +170,23 @@ public abstract class WindowManagerInternal {
*/
void onWindowsForAccessibilityChanged(boolean forceSend, int topFocusedDisplayId,
IBinder topFocusedWindowToken, @NonNull List<WindowInfo> windows);
+
+ /**
+ * Called when the windows for accessibility changed. This is called if
+ * {@link com.android.server.accessibility.Flags.FLAG_COMPUTE_WINDOW_CHANGES_ON_A11Y} is
+ * true.
+ * TODO(b/322444245): Remove screenSize parameter by getting it from
+ * DisplayManager#getDisplay(int).getRealSize() on the a11y side.
+ *
+ * @param forceSend Send the windows for accessibility even if they haven't changed.
+ * @param topFocusedDisplayId The display Id which has the top focused window.
+ * @param topFocusedWindowToken The window token of top focused window.
+ * @param screenSize The size of the display that the change happened.
+ * @param windows The windows for accessibility.
+ */
+ void onAccessibilityWindowsChanged(boolean forceSend, int topFocusedDisplayId,
+ @NonNull IBinder topFocusedWindowToken, @NonNull Point screenSize,
+ @NonNull List<AccessibilityWindowsPopulator.AccessibilityWindow> windows);
}
/**
@@ -315,8 +335,7 @@ public abstract class WindowManagerInternal {
InputChannel source) {
return state.register(display)
.thenApply(unused ->
- service.transferTouchFocus(source, state.getInputChannel(),
- true /* isDragDrop */));
+ service.startDragAndDrop(source, state.getInputChannel()));
}
/**
@@ -409,13 +428,12 @@ public abstract class WindowManagerInternal {
public abstract void setMagnificationSpec(int displayId, MagnificationSpec spec);
/**
- * Set by the accessibility framework to indicate whether the magnifiable regions of the display
- * should be shown.
+ * Set by the accessibility framework to indicate whether fullscreen magnification is activated.
*
* @param displayId The logical display id.
- * @param show {@code true} to show magnifiable region bounds, {@code false} to hide
+ * @param activated The activation of fullscreen magnification
*/
- public abstract void setForceShowMagnifiableBounds(int displayId, boolean show);
+ public abstract void setFullscreenMagnificationActivated(int displayId, boolean activated);
/**
* Obtains the magnification regions.
@@ -1043,6 +1061,15 @@ public abstract class WindowManagerInternal {
/**
* Clears apps added to collection of apps in which screen capture should be disabled.
*
+ * @param packageInfos set of {@link PackageInfo} whose windows should be unblocked
+ * from capture.
+ */
+ public abstract void removeBlockScreenCaptureForApps(
+ @NonNull ArraySet<PackageInfo> packageInfos);
+
+ /**
+ * Clears all apps added to collection of apps in which screen capture should be disabled.
+ *
* <p> This clears and resets any existing set or added applications from
* * {@link #addBlockScreenCaptureForApps(ArraySet)}
*/
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 631ebcd2b6e9..08d43ae6f4c9 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -306,11 +306,11 @@ import android.view.displayhash.VerifiedDisplayHash;
import android.view.inputmethod.ImeTracker;
import android.window.AddToSurfaceSyncGroupResult;
import android.window.ClientWindowFrames;
+import android.window.IGlobalDragListener;
import android.window.IScreenRecordingCallback;
import android.window.ISurfaceSyncGroupCompletedListener;
import android.window.ITaskFpsCallback;
import android.window.ITrustedPresentationListener;
-import android.window.IUnhandledDragListener;
import android.window.InputTransferToken;
import android.window.ScreenCapture;
import android.window.SystemPerformanceHinter;
@@ -327,8 +327,8 @@ import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IKeyguardLockedStateListener;
import com.android.internal.policy.IShortcutService;
import com.android.internal.policy.KeyInterceptionInfo;
+import com.android.internal.protolog.LegacyProtoLogImpl;
import com.android.internal.protolog.ProtoLogGroup;
-import com.android.internal.protolog.ProtoLogImpl;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastPrintWriter;
@@ -1344,7 +1344,7 @@ public class WindowManagerService extends IWindowManager.Stub
LocalServices.addService(WindowManagerInternal.class, new LocalService());
LocalServices.addService(
ImeTargetVisibilityPolicy.class, new ImeTargetVisibilityPolicyImpl());
- mEmbeddedWindowController = new EmbeddedWindowController(mAtmService);
+ mEmbeddedWindowController = new EmbeddedWindowController(mAtmService, inputManager);
mDisplayAreaPolicyProvider = DisplayAreaPolicy.Provider.fromResources(
mContext.getResources());
@@ -6701,7 +6701,11 @@ public class WindowManagerService extends IWindowManager.Stub
private void dumpLogStatus(PrintWriter pw) {
pw.println("WINDOW MANAGER LOGGING (dumpsys window logging)");
- pw.println(ProtoLogImpl.getSingleInstance().getStatus());
+ if (android.tracing.Flags.perfettoProtolog()) {
+ pw.println("Deprecated legacy command. Use Perfetto commands instead.");
+ return;
+ }
+ ((LegacyProtoLogImpl) ProtoLog.getSingleInstance()).getStatus();
}
private void dumpSessionsLocked(PrintWriter pw) {
@@ -6750,11 +6754,6 @@ public class WindowManagerService extends IWindowManager.Stub
private void dumpWindowsLocked(PrintWriter pw, boolean dumpAll,
ArrayList<WindowState> windows) {
pw.println("WINDOW MANAGER WINDOWS (dumpsys window windows)");
- dumpWindowsNoHeaderLocked(pw, dumpAll, windows);
- }
-
- private void dumpWindowsNoHeaderLocked(PrintWriter pw, boolean dumpAll,
- ArrayList<WindowState> windows) {
mRoot.dumpWindowsNoHeader(pw, dumpAll, windows);
if (!mHidingNonSystemOverlayWindows.isEmpty()) {
@@ -6989,9 +6988,15 @@ public class WindowManagerService extends IWindowManager.Stub
if (reason != null) {
pw.println(" Reason: " + reason);
}
+ pw.println();
+ final ArrayList<WindowState> relatedWindows = new ArrayList<>();
for (int i = mRoot.getChildCount() - 1; i >= 0; i--) {
final DisplayContent dc = mRoot.getChildAt(i);
final int displayId = dc.getDisplayId();
+ final WindowState currentFocus = dc.mCurrentFocus;
+ final ActivityRecord focusedApp = dc.mFocusedApp;
+ pw.println(" Display #" + displayId + " currentFocus=" + currentFocus
+ + " focusedApp=" + focusedApp);
if (!dc.mWinAddedSinceNullFocus.isEmpty()) {
pw.println(" Windows added in display #" + displayId + " since null focus: "
+ dc.mWinAddedSinceNullFocus);
@@ -7000,12 +7005,25 @@ public class WindowManagerService extends IWindowManager.Stub
pw.println(" Windows removed in display #" + displayId + " since null focus: "
+ dc.mWinRemovedSinceNullFocus);
}
+ pw.println(" Tasks in top down Z order:");
+ dc.forAllTaskDisplayAreas(tda -> {
+ tda.dump(pw, " ", false /* dumpAll */);
+ });
+ dc.getInputMonitor().dump(pw, " ");
+ pw.println();
+ dc.forAllWindows(w -> {
+ if ((currentFocus != null && Objects.equals(w.mAttrs.packageName,
+ currentFocus.mAttrs.packageName)) || (focusedApp != null
+ && Objects.equals(w.mAttrs.packageName, focusedApp.packageName))) {
+ relatedWindows.add(w);
+ }
+ }, true /* traverseTopToBottom */);
}
+ if (windowState != null && !relatedWindows.contains(windowState)) {
+ relatedWindows.add(windowState);
+ }
+ mRoot.dumpWindowsNoHeader(pw, true /* dumpAll */, relatedWindows);
pw.println();
- dumpWindowsNoHeaderLocked(pw, true, null);
- pw.println();
- pw.println("Last ANR continued");
- mRoot.dumpDisplayContents(pw);
pw.close();
mLastANRState = sw.toString();
@@ -7844,10 +7862,11 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
- public void setForceShowMagnifiableBounds(int displayId, boolean show) {
+ public void setFullscreenMagnificationActivated(int displayId, boolean activated) {
synchronized (mGlobalLock) {
if (mAccessibilityController.hasCallbacks()) {
- mAccessibilityController.setForceShowMagnifiableBounds(displayId, show);
+ mAccessibilityController
+ .setFullscreenMagnificationActivated(displayId, activated);
} else {
throw new IllegalStateException("Magnification callbacks not set!");
}
@@ -8632,6 +8651,17 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
+ public void removeBlockScreenCaptureForApps(ArraySet<PackageInfo> packageInfos) {
+ synchronized (mGlobalLock) {
+ boolean modified =
+ mSensitiveContentPackages.removeBlockScreenCaptureForApps(packageInfos);
+ if (modified) {
+ WindowManagerService.this.refreshScreenCaptureDisabled();
+ }
+ }
+ }
+
+ @Override
public void clearBlockedApps() {
synchronized (mGlobalLock) {
boolean modified = mSensitiveContentPackages.clearBlockedApps();
@@ -9044,73 +9074,33 @@ public class WindowManagerService extends IWindowManager.Stub
null /* region */, clientToken);
}
- boolean transferEmbeddedTouchFocusToHost(IWindow embeddedWindow) {
- final IBinder windowBinder = embeddedWindow.asBinder();
- final IBinder hostInputChannel, embeddedInputChannel;
- synchronized (mGlobalLock) {
- final EmbeddedWindowController.EmbeddedWindow ew =
- mEmbeddedWindowController.getByWindowToken(windowBinder);
- if (ew == null) {
- Slog.w(TAG, "Attempt to transfer touch focus from non-existent embedded window");
- return false;
- }
- final WindowState hostWindowState = ew.getWindowState();
- if (hostWindowState == null) {
- Slog.w(TAG, "Attempt to transfer touch focus from embedded window with no" +
- " associated host");
- return false;
- }
- embeddedInputChannel = ew.getInputChannelToken();
- if (embeddedInputChannel == null) {
- Slog.w(TAG, "Attempt to transfer touch focus from embedded window with no input" +
- " channel");
- return false;
- }
- hostInputChannel = hostWindowState.mInputChannelToken;
- if (hostInputChannel == null) {
- Slog.w(TAG, "Attempt to transfer touch focus to a host window with no" +
- " input channel");
- return false;
- }
- return mInputManager.transferTouchFocus(embeddedInputChannel, hostInputChannel);
- }
- }
-
- boolean transferHostTouchGestureToEmbedded(Session session, IWindow hostWindow,
- InputTransferToken inputTransferToken) {
- final IBinder hostInputChannel, embeddedInputChannel;
- synchronized (mGlobalLock) {
- final WindowState hostWindowState = windowForClientLocked(session, hostWindow, false);
- if (hostWindowState == null) {
- Slog.w(TAG, "Attempt to transfer touch gesture with invalid host window");
- return false;
- }
+ @Override
+ public boolean transferTouchGesture(@NonNull InputTransferToken transferFromToken,
+ @NonNull InputTransferToken transferToToken) {
+ Objects.requireNonNull(transferFromToken);
+ Objects.requireNonNull(transferToToken);
- final EmbeddedWindowController.EmbeddedWindow ew =
- mEmbeddedWindowController.getByInputTransferToken(inputTransferToken);
- if (ew == null || ew.mHostWindowState == null) {
- Slog.w(TAG, "Attempt to transfer touch gesture to non-existent embedded window");
- return false;
- }
- if (ew.mHostWindowState.mClient.asBinder() != hostWindow.asBinder()) {
- Slog.w(TAG, "Attempt to transfer touch gesture to embedded window not associated"
- + " with host window");
- return false;
- }
- embeddedInputChannel = ew.getInputChannelToken();
- if (embeddedInputChannel == null) {
- Slog.w(TAG, "Attempt to transfer touch focus from embedded window with no input"
- + " channel");
- return false;
- }
- hostInputChannel = hostWindowState.mInputChannelToken;
- if (hostInputChannel == null) {
- Slog.w(TAG,
- "Attempt to transfer touch focus to a host window with no input channel");
- return false;
+ final long identity = Binder.clearCallingIdentity();
+ boolean didTransfer;
+ try {
+ synchronized (mGlobalLock) {
+ // If the transferToToken exists in the input to window map, it means the request
+ // is to transfer from embedded to host. Otherwise, the transferToToken
+ // represents an embedded window so transfer from host to embedded.
+ WindowState windowStateTo = mInputToWindowMap.get(transferToToken.mToken);
+ if (windowStateTo != null) {
+ didTransfer = mEmbeddedWindowController.transferToHost(transferFromToken,
+ windowStateTo);
+ } else {
+ WindowState windowStateFrom = mInputToWindowMap.get(transferFromToken.mToken);
+ didTransfer = mEmbeddedWindowController.transferToEmbedded(windowStateFrom,
+ transferToToken);
+ }
}
- return mInputManager.transferTouchFocus(hostInputChannel, embeddedInputChannel);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
+ return didTransfer;
}
private void updateInputChannel(IBinder channelToken, int callingUid, int callingPid,
@@ -10034,14 +10024,13 @@ public class WindowManagerService extends IWindowManager.Stub
}
/**
- * Sets the listener to be called back when a cross-window drag and drop operation is unhandled
- * (ie. not handled by any window which can handle the drag).
+ * Sets the listener to be called back when a cross-window drag and drop operation happens.
*/
@Override
- public void setUnhandledDragListener(IUnhandledDragListener listener) throws RemoteException {
+ public void setGlobalDragListener(IGlobalDragListener listener) throws RemoteException {
mAtmService.enforceTaskPermission("setUnhandledDragListener");
synchronized (mGlobalLock) {
- mDragDropController.setUnhandledDragListener(listener);
+ mDragDropController.setGlobalDragListener(listener);
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 8fad9509af44..0b29f9688acd 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -48,7 +48,9 @@ import android.view.IWindowManager;
import android.view.ViewDebug;
import com.android.internal.os.ByteTransferPipe;
-import com.android.internal.protolog.ProtoLogImpl;
+import com.android.internal.protolog.LegacyProtoLogImpl;
+import com.android.internal.protolog.common.IProtoLog;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.server.IoThread;
import com.android.server.wm.LetterboxConfiguration.LetterboxBackgroundType;
import com.android.server.wm.LetterboxConfiguration.LetterboxHorizontalReachabilityPosition;
@@ -107,11 +109,19 @@ public class WindowManagerShellCommand extends ShellCommand {
// trace files can be written.
return mInternal.mWindowTracing.onShellCommand(this);
case "logging":
- int result = ProtoLogImpl.getSingleInstance().onShellCommand(this);
- if (result != 0) {
- pw.println("Not handled, please use "
- + "`adb shell dumpsys activity service SystemUIService WMShell` "
- + "if you are looking for ProtoLog in WMShell");
+ IProtoLog instance = ProtoLog.getSingleInstance();
+ int result = 0;
+ if (instance instanceof LegacyProtoLogImpl) {
+ result = ((LegacyProtoLogImpl) instance).onShellCommand(this);
+ if (result != 0) {
+ pw.println("Not handled, please use "
+ + "`adb shell dumpsys activity service SystemUIService "
+ + "WMShell` if you are looking for ProtoLog in WMShell");
+ }
+ } else {
+ result = -1;
+ pw.println("Command not supported. "
+ + "Only supported when using legacy ProtoLog.");
}
return result;
case "user-rotation":
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index a8de9198943c..d6fc01aeadd2 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -2254,6 +2254,11 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
ownerTask.addChild(taskFragment, position);
taskFragment.setWindowingMode(creationParams.getWindowingMode());
if (!creationParams.getInitialRelativeBounds().isEmpty()) {
+ // The surface operations for the task fragment should sync with the transition.
+ // This avoid using pending transaction before collectExistenceChange is called.
+ if (transition != null) {
+ addToSyncSet(transition.getSyncId(), taskFragment);
+ }
// Set relative bounds instead of using setBounds. This will avoid unnecessary update in
// case the parent has resized since the last time parent info is sent to the organizer.
taskFragment.setRelativeEmbeddedBounds(creationParams.getInitialRelativeBounds());
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 6acf1f3f84af..ee16a37d6baf 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -28,7 +28,6 @@ import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.server.am.ProcessList.INVALID_ADJ;
-import static com.android.server.grammaticalinflection.GrammaticalInflectionUtils.checkSystemGrammaticalGenderPermission;
import static com.android.server.wm.ActivityRecord.State.DESTROYED;
import static com.android.server.wm.ActivityRecord.State.DESTROYING;
import static com.android.server.wm.ActivityRecord.State.PAUSED;
@@ -299,7 +298,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
*/
private volatile int mActivityStateFlags = ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER;
- private boolean mCanUseSystemGrammaticalGender;
+ private final boolean mCanUseSystemGrammaticalGender;
public WindowProcessController(@NonNull ActivityTaskManagerService atm,
@NonNull ApplicationInfo info, String name, int uid, int userId, Object owner,
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index f56e50e2e9fd..a7a28c282ff9 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -245,7 +245,6 @@ import android.window.OnBackInvokedCallbackInfo;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.KeyInterceptionInfo;
-import com.android.internal.protolog.ProtoLogImpl;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.ToBooleanFunction;
@@ -1332,7 +1331,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
updateSourceFrame(windowFrames.mFrame);
if (mActivityRecord != null && !mIsChildWindow) {
- mActivityRecord.layoutLetterbox(this);
+ mActivityRecord.layoutLetterboxIfNeeded(this);
}
mSurfacePlacementNeeded = true;
mHaveFrame = true;
@@ -1897,11 +1896,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return true;
}
- if (com.android.server.notification.Flags.sensitiveNotificationAppProtection()) {
- if (mWmService.mSensitiveContentPackages
- .shouldBlockScreenCaptureForApp(getOwningPackage(), getOwningUid())) {
- return true;
- }
+ // block screen capture to protect sensitive notifications or content on the screen.
+ if (mWmService.mSensitiveContentPackages.shouldBlockScreenCaptureForApp(
+ getOwningPackage(), getOwningUid(), getWindowToken())) {
+ return true;
}
return !DevicePolicyCache.getInstance().isScreenCaptureAllowed(mShowUserId);
@@ -2603,7 +2601,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
/**
* Move the touch gesture from the currently touched window on this display to this window.
+ *
+ * @deprecated Use {@link
+ * com.android.server.input.InputManagerInternal#transferTouchGesture(IBinder, IBinder)}.
*/
+ @Deprecated
public boolean transferTouch() {
return mWmService.mInputManager.transferTouch(mInputChannelToken, getDisplayId());
}
@@ -2835,10 +2837,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// For child windows we want to use the pid for the parent window in case the the child
// window was added from another process.
final WindowState parentWindow = getParentWindow();
- final int pid = parentWindow != null ? parentWindow.mSession.mPid : mSession.mPid;
- final Configuration processConfig =
- mWmService.mAtmService.getGlobalConfigurationForPid(pid);
- return processConfig;
+ final Session session = parentWindow != null ? parentWindow.mSession : mSession;
+ return session.mPid == MY_PID ? mWmService.mRoot.getConfiguration()
+ : session.mProcess.getConfiguration();
}
private Configuration getLastReportedConfiguration() {
@@ -4679,7 +4680,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
void onExitAnimationDone() {
- if (ProtoLogImpl.isEnabled(WM_DEBUG_ANIM)) {
+ if (ProtoLog.isEnabled(WM_DEBUG_ANIM)) {
final AnimationAdapter animationAdapter = mSurfaceAnimator.getAnimation();
StringWriter sw = new StringWriter();
if (animationAdapter != null) {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 6428591d8b8d..7f7c2493cd68 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -60,7 +60,6 @@ import android.view.WindowManager.LayoutParams;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
-import com.android.internal.protolog.ProtoLogImpl;
import com.android.internal.protolog.common.ProtoLog;
import com.android.server.policy.WindowManagerPolicy;
@@ -584,7 +583,7 @@ class WindowStateAnimator {
mWin.mAttrs, attr, TRANSIT_OLD_NONE);
}
}
- if (ProtoLogImpl.isEnabled(WM_DEBUG_ANIM)) {
+ if (ProtoLog.isEnabled(WM_DEBUG_ANIM)) {
ProtoLog.v(WM_DEBUG_ANIM, "applyAnimation: win=%s"
+ " anim=%d attr=0x%x a=%s transit=%d type=%d isEntrance=%b Callers %s",
this, anim, attr, a, transit, mAttrType, isEntrance, Debug.getCallers(20));
diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java
index 416d0427a0d6..424d50434010 100644
--- a/services/core/java/com/android/server/wm/WindowTracing.java
+++ b/services/core/java/com/android/server/wm/WindowTracing.java
@@ -35,7 +35,9 @@ import android.util.Log;
import android.util.proto.ProtoOutputStream;
import android.view.Choreographer;
-import com.android.internal.protolog.ProtoLogImpl;
+import com.android.internal.protolog.LegacyProtoLogImpl;
+import com.android.internal.protolog.common.IProtoLog;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.TraceBuffer;
import java.io.File;
@@ -77,6 +79,8 @@ class WindowTracing {
private volatile boolean mEnabledLockFree;
private boolean mScheduled;
+ private final IProtoLog mProtoLog;
+
static WindowTracing createDefaultAndStartLooper(WindowManagerService service,
Choreographer choreographer) {
File file = new File(TRACE_FILENAME);
@@ -96,6 +100,7 @@ class WindowTracing {
mTraceFile = file;
mBuffer = new TraceBuffer(bufferCapacity);
setLogLevel(WindowTraceLogLevel.TRIM, null /* pw */);
+ mProtoLog = ProtoLog.getSingleInstance();
}
void startTrace(@Nullable PrintWriter pw) {
@@ -104,7 +109,6 @@ class WindowTracing {
return;
}
synchronized (mEnabledLock) {
- ProtoLogImpl.getSingleInstance().startProtoLog(pw);
logAndPrintln(pw, "Start tracing to " + mTraceFile + ".");
mBuffer.resetBuffer();
mEnabled = mEnabledLockFree = true;
@@ -132,7 +136,6 @@ class WindowTracing {
writeTraceToFileLocked();
logAndPrintln(pw, "Trace written to " + mTraceFile + ".");
}
- ProtoLogImpl.getSingleInstance().stopProtoLog(pw, true);
}
/**
@@ -152,11 +155,15 @@ class WindowTracing {
logAndPrintln(pw, "Stop tracing to " + mTraceFile + ". Waiting for traces to flush.");
writeTraceToFileLocked();
logAndPrintln(pw, "Trace written to " + mTraceFile + ".");
- ProtoLogImpl.getSingleInstance().stopProtoLog(pw, true);
+ if (!android.tracing.Flags.perfettoProtolog()) {
+ ((LegacyProtoLogImpl) mProtoLog).stopProtoLog(pw, true);
+ }
logAndPrintln(pw, "Start tracing to " + mTraceFile + ".");
mBuffer.resetBuffer();
mEnabled = mEnabledLockFree = true;
- ProtoLogImpl.getSingleInstance().startProtoLog(pw);
+ if (!android.tracing.Flags.perfettoProtolog()) {
+ ((LegacyProtoLogImpl) mProtoLog).startProtoLog(pw);
+ }
}
}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index cbbcd965f4af..c778398342dc 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -1509,15 +1509,14 @@ bool NativeInputManager::filterInputEvent(const InputEvent& inputEvent, uint32_t
ScopedLocalRef<jobject> inputEventObj(env);
switch (inputEvent.getType()) {
case InputEventType::KEY:
- inputEventObj.reset(
- android_view_KeyEvent_fromNative(env,
- static_cast<const KeyEvent&>(inputEvent)));
+ inputEventObj =
+ android_view_KeyEvent_obtainAsCopy(env,
+ static_cast<const KeyEvent&>(inputEvent));
break;
case InputEventType::MOTION:
- inputEventObj.reset(
- android_view_MotionEvent_obtainAsCopy(env,
- static_cast<const MotionEvent&>(
- inputEvent)));
+ inputEventObj = android_view_MotionEvent_obtainAsCopy(env,
+ static_cast<const MotionEvent&>(
+ inputEvent));
break;
default:
return true; // dispatch the event normally
@@ -1559,7 +1558,7 @@ void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent& keyEvent,
const nsecs_t when = keyEvent.getEventTime();
JNIEnv* env = jniEnv();
- ScopedLocalRef<jobject> keyEventObj(env, android_view_KeyEvent_fromNative(env, keyEvent));
+ ScopedLocalRef<jobject> keyEventObj = android_view_KeyEvent_obtainAsCopy(env, keyEvent);
if (!keyEventObj.get()) {
ALOGE("Failed to obtain key event object for interceptKeyBeforeQueueing.");
return;
@@ -1639,7 +1638,7 @@ nsecs_t NativeInputManager::interceptKeyBeforeDispatching(const sp<IBinder>& tok
// Token may be null
ScopedLocalRef<jobject> tokenObj(env, javaObjectForIBinder(env, token));
- ScopedLocalRef<jobject> keyEventObj(env, android_view_KeyEvent_fromNative(env, keyEvent));
+ ScopedLocalRef<jobject> keyEventObj = android_view_KeyEvent_obtainAsCopy(env, keyEvent);
if (!keyEventObj.get()) {
ALOGE("Failed to obtain key event object for interceptKeyBeforeDispatching.");
return 0;
@@ -1670,7 +1669,7 @@ std::optional<KeyEvent> NativeInputManager::dispatchUnhandledKey(const sp<IBinde
// Note: tokenObj may be null.
ScopedLocalRef<jobject> tokenObj(env, javaObjectForIBinder(env, token));
- ScopedLocalRef<jobject> keyEventObj(env, android_view_KeyEvent_fromNative(env, keyEvent));
+ ScopedLocalRef<jobject> keyEventObj = android_view_KeyEvent_obtainAsCopy(env, keyEvent);
if (!keyEventObj.get()) {
ALOGE("Failed to obtain key event object for dispatchUnhandledKey.");
return {};
@@ -1689,7 +1688,8 @@ std::optional<KeyEvent> NativeInputManager::dispatchUnhandledKey(const sp<IBinde
return {};
}
- const KeyEvent fallbackEvent = android_view_KeyEvent_toNative(env, fallbackKeyEventObj.get());
+ const KeyEvent fallbackEvent =
+ android_view_KeyEvent_obtainAsCopy(env, fallbackKeyEventObj.get());
android_view_KeyEvent_recycle(env, fallbackKeyEventObj.get());
return fallbackEvent;
}
@@ -2070,7 +2070,7 @@ static jint nativeInjectInputEvent(JNIEnv* env, jobject nativeImplObj, jobject i
InputEventInjectionSync mode = static_cast<InputEventInjectionSync>(syncMode);
if (env->IsInstanceOf(inputEventObj, gKeyEventClassInfo.clazz)) {
- const KeyEvent keyEvent = android_view_KeyEvent_toNative(env, inputEventObj);
+ const KeyEvent keyEvent = android_view_KeyEvent_obtainAsCopy(env, inputEventObj);
const InputEventInjectionResult result =
im->getInputManager()->getDispatcher().injectInputEvent(&keyEvent, targetUid, mode,
std::chrono::milliseconds(
@@ -2101,7 +2101,7 @@ static jobject nativeVerifyInputEvent(JNIEnv* env, jobject nativeImplObj, jobjec
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
if (env->IsInstanceOf(inputEventObj, gKeyEventClassInfo.clazz)) {
- const KeyEvent keyEvent = android_view_KeyEvent_toNative(env, inputEventObj);
+ const KeyEvent keyEvent = android_view_KeyEvent_obtainAsCopy(env, inputEventObj);
std::unique_ptr<VerifiedInputEvent> verifiedEvent =
im->getInputManager()->getDispatcher().verifyInputEvent(keyEvent);
if (verifiedEvent == nullptr) {
@@ -2186,9 +2186,9 @@ static void nativeSetSystemUiLightsOut(JNIEnv* env, jobject nativeImplObj, jbool
im->setSystemUiLightsOut(lightsOut);
}
-static jboolean nativeTransferTouchFocus(JNIEnv* env, jobject nativeImplObj,
- jobject fromChannelTokenObj, jobject toChannelTokenObj,
- jboolean isDragDrop) {
+static jboolean nativeTransferTouchGesture(JNIEnv* env, jobject nativeImplObj,
+ jobject fromChannelTokenObj, jobject toChannelTokenObj,
+ jboolean isDragDrop) {
if (fromChannelTokenObj == nullptr || toChannelTokenObj == nullptr) {
return JNI_FALSE;
}
@@ -2197,21 +2197,22 @@ static jboolean nativeTransferTouchFocus(JNIEnv* env, jobject nativeImplObj,
sp<IBinder> toChannelToken = ibinderForJavaObject(env, toChannelTokenObj);
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
- if (im->getInputManager()->getDispatcher().transferTouchFocus(fromChannelToken, toChannelToken,
- isDragDrop)) {
+ if (im->getInputManager()->getDispatcher().transferTouchGesture(fromChannelToken,
+ toChannelToken, isDragDrop)) {
return JNI_TRUE;
} else {
return JNI_FALSE;
}
}
-static jboolean nativeTransferTouch(JNIEnv* env, jobject nativeImplObj, jobject destChannelTokenObj,
- jint displayId) {
+static jboolean nativeTransferTouchOnDisplay(JNIEnv* env, jobject nativeImplObj,
+ jobject destChannelTokenObj, jint displayId) {
sp<IBinder> destChannelToken = ibinderForJavaObject(env, destChannelTokenObj);
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
- if (im->getInputManager()->getDispatcher().transferTouch(destChannelToken,
- static_cast<int32_t>(displayId))) {
+ if (im->getInputManager()->getDispatcher().transferTouchOnDisplay(destChannelToken,
+ static_cast<int32_t>(
+ displayId))) {
return JNI_TRUE;
} else {
return JNI_FALSE;
@@ -2875,9 +2876,9 @@ static const JNINativeMethod gInputManagerMethods[] = {
{"requestPointerCapture", "(Landroid/os/IBinder;Z)V", (void*)nativeRequestPointerCapture},
{"setInputDispatchMode", "(ZZ)V", (void*)nativeSetInputDispatchMode},
{"setSystemUiLightsOut", "(Z)V", (void*)nativeSetSystemUiLightsOut},
- {"transferTouchFocus", "(Landroid/os/IBinder;Landroid/os/IBinder;Z)Z",
- (void*)nativeTransferTouchFocus},
- {"transferTouch", "(Landroid/os/IBinder;I)Z", (void*)nativeTransferTouch},
+ {"transferTouchGesture", "(Landroid/os/IBinder;Landroid/os/IBinder;Z)Z",
+ (void*)nativeTransferTouchGesture},
+ {"transferTouch", "(Landroid/os/IBinder;I)Z", (void*)nativeTransferTouchOnDisplay},
{"getMousePointerSpeed", "()I", (void*)nativeGetMousePointerSpeed},
{"setPointerSpeed", "(I)V", (void*)nativeSetPointerSpeed},
{"setMousePointerAccelerationEnabled", "(IZ)V",
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 a46916553abc..b38a2f9558e9 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -117,6 +117,9 @@
<xs:element type="sensorDetails" name="proxSensor">
<xs:annotation name="final"/>
</xs:element>
+ <xs:element type="sensorDetails" name="tempSensor">
+ <xs:annotation name="final"/>
+ </xs:element>
<!-- Length of the ambient light horizon used to calculate the long & short term
estimates of ambient light in milliseconds.-->
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 79ea274e2fca..b329db4a2076 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -133,6 +133,7 @@ package com.android.server.display.config {
method public final java.math.BigDecimal getScreenBrightnessRampSlowIncreaseIdle();
method public final com.android.server.display.config.SensorDetails getScreenOffBrightnessSensor();
method public final com.android.server.display.config.IntegerArray getScreenOffBrightnessSensorValueToLux();
+ method public final com.android.server.display.config.SensorDetails getTempSensor();
method @NonNull public final com.android.server.display.config.ThermalThrottling getThermalThrottling();
method public final com.android.server.display.config.UsiVersion getUsiVersion();
method public final void setAmbientBrightnessChangeThresholds(@NonNull com.android.server.display.config.Thresholds);
@@ -167,6 +168,7 @@ package com.android.server.display.config {
method public final void setScreenBrightnessRampSlowIncreaseIdle(java.math.BigDecimal);
method public final void setScreenOffBrightnessSensor(com.android.server.display.config.SensorDetails);
method public final void setScreenOffBrightnessSensorValueToLux(com.android.server.display.config.IntegerArray);
+ method public final void setTempSensor(com.android.server.display.config.SensorDetails);
method public final void setThermalThrottling(@NonNull com.android.server.display.config.ThermalThrottling);
method public final void setUsiVersion(com.android.server.display.config.UsiVersion);
}
diff --git a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
index be4b9e1fc7b1..173cb36a1a34 100644
--- a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
@@ -104,7 +104,6 @@ public final class CreateRequestSession extends RequestSession<CreateCredentialR
flattenedPrimaryProviders.add(cn.flattenToString());
}
- final boolean isShowAllOptionsRequested = false;
mPendingIntent = mCredentialManagerUi.createPendingIntent(
RequestInfo.newCreateRequestInfo(
mRequestId, mClientRequest,
@@ -112,8 +111,8 @@ public final class CreateRequestSession extends RequestSession<CreateCredentialR
PermissionUtils.hasPermission(mContext, mClientAppInfo.getPackageName(),
Manifest.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS),
/*defaultProviderId=*/flattenedPrimaryProviders,
- isShowAllOptionsRequested),
- providerDataList, /*isRequestForAllOptions=*/ isShowAllOptionsRequested);
+ /*isShowAllOptionsRequested=*/ false),
+ providerDataList);
mClientCallback.onPendingIntent(mPendingIntent);
} catch (RemoteException e) {
mRequestSessionMetric.collectUiReturnedFinalPhase(/*uiReturned=*/ false);
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
index 534c842f6b07..9d45cfb2fb7e 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
@@ -18,6 +18,7 @@ package com.android.server.credentials;
import static android.credentials.selection.Constants.EXTRA_FINAL_RESPONSE_RECEIVER;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
@@ -152,16 +153,34 @@ public class CredentialManagerUi {
/**
* Creates a {@link PendingIntent} to be used to invoke the credential manager selector UI,
- * by the calling app process.
+ * by the calling app process. The bottom-sheet navigates to the default page when the intent
+ * is invoked.
*
* @param requestInfo the information about the request
* @param providerDataList the list of provider data from remote providers
- * @param isRequestForAllOptions whether the bottom sheet should directly navigate to the
- * all options page
*/
public PendingIntent createPendingIntent(
- RequestInfo requestInfo, ArrayList<ProviderData> providerDataList,
- boolean isRequestForAllOptions) {
+ RequestInfo requestInfo, ArrayList<ProviderData> providerDataList) {
+ return createPendingIntent(requestInfo, providerDataList, /*forAutofill=*/ false);
+ }
+
+ /**
+ * Creates a {@link PendingIntent} to be used to invoke the credential manager selector UI,
+ * by the calling app process. This intent is invoked from the Autofill flow, when the user
+ * requests to bring up the 'All Options' page of the credential bottom-sheet. When the user
+ * clicks on the pinned entry, the intent will bring up the 'All Options' page of the
+ * bottom-sheet. The provider data list is processed by the credential autofill service for
+ * each autofill id and passed in as an auth extra.
+ *
+ * @param requestInfo the information about the request
+ */
+ public PendingIntent createPendingIntentForAutofill(RequestInfo requestInfo) {
+ return createPendingIntent(requestInfo, /*providerDataList=*/ null, /*forAutofill=*/ true);
+ }
+
+ private PendingIntent createPendingIntent(
+ RequestInfo requestInfo, @Nullable ArrayList<ProviderData> providerDataList,
+ boolean forAutofill) {
List<CredentialProviderInfo> allProviders =
CredentialProviderInfoFactory.getCredentialProviderServices(
mContext,
@@ -176,11 +195,17 @@ public class CredentialManagerUi {
.map(disabledProvider -> new DisabledProviderData(
disabledProvider.getComponentName().flattenToString())).toList();
- Intent intent = IntentFactory.createCredentialSelectorIntent(mContext, requestInfo,
- providerDataList,
- new ArrayList<>(disabledProviderDataList), mResultReceiver,
- isRequestForAllOptions)
- .setAction(UUID.randomUUID().toString());
+ Intent intent;
+ if (forAutofill) {
+ intent = IntentFactory.createCredentialSelectorIntentForAutofill(
+ mContext, requestInfo, new ArrayList<>(disabledProviderDataList),
+ mResultReceiver);
+ } else {
+ intent = IntentFactory.createCredentialSelectorIntent(
+ mContext, requestInfo, providerDataList,
+ new ArrayList<>(disabledProviderDataList), mResultReceiver);
+ }
+ intent.setAction(UUID.randomUUID().toString());
//TODO: Create unique pending intent using request code and cancel any pre-existing pending
// intents
return PendingIntent.getActivityAsUser(
diff --git a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
index adb1b72cf3ee..723c52f37574 100644
--- a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
@@ -115,15 +115,12 @@ public class GetCandidateRequestSession extends RequestSession<GetCredentialRequ
}
cancelExistingPendingIntent();
- final boolean isShowAllOptionsRequested = true;
- mPendingIntent = mCredentialManagerUi.createPendingIntent(
+ mPendingIntent = mCredentialManagerUi.createPendingIntentForAutofill(
RequestInfo.newGetRequestInfo(
mRequestId, mClientRequest, mClientAppInfo.getPackageName(),
PermissionUtils.hasPermission(mContext, mClientAppInfo.getPackageName(),
Manifest.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS),
- isShowAllOptionsRequested),
- /*providerDataList=*/ null,
- /*isRequestForAllOptions=*/ isShowAllOptionsRequested);
+ /*isShowAllOptionsRequested=*/ true));
List<GetCredentialProviderData> candidateProviderDataList = new ArrayList<>();
for (ProviderData providerData : providerDataList) {
@@ -168,6 +165,9 @@ public class GetCandidateRequestSession extends RequestSession<GetCredentialRequ
mRequestSessionMetric.collectFrameworkException(exception);
if (finalResponseReceiver != null) {
Bundle resultData = new Bundle();
+ resultData.putStringArray(
+ CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION,
+ new String[] {exception, message});
finalResponseReceiver.send(Constants.FAILURE_CREDMAN_SELECTOR, resultData);
} else {
respondToClientWithErrorAndFinish(exception, message);
diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
index a279337698f2..6513ae1af369 100644
--- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
@@ -103,7 +103,6 @@ public class GetRequestSession extends RequestSession<GetCredentialRequest,
Binder.withCleanCallingIdentity(() -> {
try {
cancelExistingPendingIntent();
- final boolean isShowAllOptionsRequested = false;
mPendingIntent = mCredentialManagerUi.createPendingIntent(
RequestInfo.newGetRequestInfo(
mRequestId, mClientRequest, mClientAppInfo.getPackageName(),
@@ -111,9 +110,8 @@ public class GetRequestSession extends RequestSession<GetCredentialRequest,
mClientAppInfo.getPackageName(),
Manifest.permission
.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS),
- isShowAllOptionsRequested),
- providerDataList,
- /*isRequestForAllOptions=*/ isShowAllOptionsRequested);
+ /*isShowAllOptionsRequested=*/ false),
+ providerDataList);
mClientCallback.onPendingIntent(mPendingIntent);
} catch (RemoteException e) {
mRequestSessionMetric.collectUiReturnedFinalPhase(/*uiReturned=*/ false);
diff --git a/services/credentials/java/com/android/server/credentials/MetricUtilities.java b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
index 23aa3742175a..96ef2ed61f1a 100644
--- a/services/credentials/java/com/android/server/credentials/MetricUtilities.java
+++ b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
@@ -531,7 +531,7 @@ public class MetricUtilities {
int index = 0;
for (CandidateBrowsingPhaseMetric metric : browsingPhaseMetrics) {
browsedClickedEntries[index] = metric.getEntryEnum();
- browsedProviderUid[index] = metric.getProviderUid();
+ browsedProviderUid[index] = DEFAULT_INT_32;
index++;
}
FrameworkStatsLog.write(FrameworkStatsLog.CREDENTIAL_MANAGER_FINALNOUID_REPORTED,
diff --git a/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java b/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java
index 6b313fda1dd2..6e8f7c8d7722 100644
--- a/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java
@@ -187,14 +187,13 @@ public class PrepareGetRequestSession extends GetRequestSession {
}
}
if (!providerDataList.isEmpty()) {
- final boolean isShowAllOptionsRequested = false;
return mCredentialManagerUi.createPendingIntent(
RequestInfo.newGetRequestInfo(
mRequestId, mClientRequest, mClientAppInfo.getPackageName(),
PermissionUtils.hasPermission(mContext, mClientAppInfo.getPackageName(),
Manifest.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS),
- isShowAllOptionsRequested),
- providerDataList, /*isRequestForAllOptions=*/ isShowAllOptionsRequested);
+ /*isShowAllOptionsRequested=*/ false),
+ providerDataList);
} else {
return null;
}
diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
index ca23d62601bb..fa63bc899cb5 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
@@ -30,9 +30,7 @@ import android.credentials.selection.AuthenticationEntry;
import android.credentials.selection.Entry;
import android.credentials.selection.GetCredentialProviderData;
import android.credentials.selection.ProviderPendingIntentResponse;
-import android.os.Bundle;
import android.os.ICancellationSignal;
-import android.service.autofill.Flags;
import android.service.credentials.Action;
import android.service.credentials.BeginGetCredentialOption;
import android.service.credentials.BeginGetCredentialRequest;
@@ -44,7 +42,6 @@ import android.service.credentials.GetCredentialRequest;
import android.service.credentials.RemoteEntry;
import android.util.Pair;
import android.util.Slog;
-import android.view.autofill.AutofillId;
import java.util.ArrayList;
import java.util.HashMap;
@@ -77,10 +74,6 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
@NonNull
private final Map<String, CredentialOption> mBeginGetOptionToCredentialOptionMap;
- @NonNull
- private final Map<String, AutofillId> mCredentialEntryKeyToAutofilLIdMap;
-
-
/** The complete request to be used in the second round. */
private final android.credentials.GetCredentialRequest mCompleteRequest;
private final CallingAppInfo mCallingAppInfo;
@@ -249,7 +242,6 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
mBeginGetOptionToCredentialOptionMap = new HashMap<>(beginGetOptionToCredentialOptionMap);
mProviderResponseDataHandler = new ProviderResponseDataHandler(
ComponentName.unflattenFromString(hybridService));
- mCredentialEntryKeyToAutofilLIdMap = new HashMap<>();
}
/** Called when the provider response has been updated by an external source. */
@@ -303,7 +295,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
invokeCallbackOnInternalInvalidState();
return;
}
- onCredentialEntrySelected(providerPendingIntentResponse, entryKey);
+ onCredentialEntrySelected(providerPendingIntentResponse);
break;
case ACTION_ENTRY_KEY:
Action actionEntry = mProviderResponseDataHandler.getActionEntry(entryKey);
@@ -312,7 +304,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
invokeCallbackOnInternalInvalidState();
return;
}
- onActionEntrySelected(providerPendingIntentResponse, entryKey);
+ onActionEntrySelected(providerPendingIntentResponse);
break;
case AUTHENTICATION_ACTION_ENTRY_KEY:
Action authenticationEntry = mProviderResponseDataHandler
@@ -342,7 +334,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
break;
case REMOTE_ENTRY_KEY:
if (mProviderResponseDataHandler.getRemoteEntry(entryKey) != null) {
- onRemoteEntrySelected(providerPendingIntentResponse, entryKey);
+ onRemoteEntrySelected(providerPendingIntentResponse);
} else {
Slog.i(TAG, "Unexpected remote entry key");
invokeCallbackOnInternalInvalidState();
@@ -381,7 +373,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
return null;
}
- private Intent setUpFillInIntentWithFinalRequest(@NonNull String id, String entryKey) {
+ private Intent setUpFillInIntentWithFinalRequest(@NonNull String id) {
// TODO: Determine if we should skip this entry if entry id is not set, or is set
// but does not resolve to a valid option. For now, not skipping it because
// it may be possible that the provider adds their own extras and expects to receive
@@ -392,13 +384,6 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
Slog.w(TAG, "Id from Credential Entry does not resolve to a valid option");
return intent;
}
- AutofillId autofillId = credentialOption
- .getCandidateQueryData()
- .getParcelable(CredentialProviderService.EXTRA_AUTOFILL_ID, AutofillId.class);
- if (autofillId != null && Flags.autofillCredmanIntegration()) {
- intent.putExtra(CredentialProviderService.EXTRA_AUTOFILL_ID, autofillId);
- mCredentialEntryKeyToAutofilLIdMap.put(entryKey, autofillId);
- }
return intent.putExtra(
CredentialProviderService.EXTRA_GET_CREDENTIAL_REQUEST,
new GetCredentialRequest(
@@ -414,13 +399,12 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
}
private void onRemoteEntrySelected(
- ProviderPendingIntentResponse providerPendingIntentResponse, String entryKey) {
- onCredentialEntrySelected(providerPendingIntentResponse, entryKey);
+ ProviderPendingIntentResponse providerPendingIntentResponse) {
+ onCredentialEntrySelected(providerPendingIntentResponse);
}
private void onCredentialEntrySelected(
- ProviderPendingIntentResponse providerPendingIntentResponse,
- String entryKey) {
+ ProviderPendingIntentResponse providerPendingIntentResponse) {
if (providerPendingIntentResponse == null) {
invokeCallbackOnInternalInvalidState();
return;
@@ -437,18 +421,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
GetCredentialResponse getCredentialResponse = PendingIntentResultHandler
.extractGetCredentialResponse(
providerPendingIntentResponse.getResultData());
- if (getCredentialResponse != null && getCredentialResponse.getCredential() != null) {
- Bundle credentialData = getCredentialResponse.getCredential().getData();
- AutofillId autofillId = mCredentialEntryKeyToAutofilLIdMap.get(entryKey);
- if (Flags.autofillCredmanIntegration()
- && entryKey != null && autofillId != null && credentialData != null
- ) {
- Slog.d(TAG, "Adding autofillId to credential response: " + autofillId);
- credentialData.putParcelable(
- CredentialProviderService.EXTRA_AUTOFILL_ID,
- mCredentialEntryKeyToAutofilLIdMap.get(entryKey)
- );
- }
+ if (getCredentialResponse != null) {
mCallbacks.onFinalResponseReceived(mComponentName,
getCredentialResponse);
return;
@@ -532,9 +505,9 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
/** Returns true if either an exception or a response is found. */
private void onActionEntrySelected(ProviderPendingIntentResponse
- providerPendingIntentResponse, String entryKey) {
+ providerPendingIntentResponse) {
Slog.i(TAG, "onActionEntrySelected");
- onCredentialEntrySelected(providerPendingIntentResponse, entryKey);
+ onCredentialEntrySelected(providerPendingIntentResponse);
}
@@ -632,7 +605,7 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential
Entry entry = new Entry(CREDENTIAL_ENTRY_KEY,
id, credentialEntry.getSlice(),
setUpFillInIntentWithFinalRequest(credentialEntry
- .getBeginGetCredentialOptionId(), id));
+ .getBeginGetCredentialOptionId()));
mUiCredentialEntries.put(id, new Pair<>(credentialEntry, entry));
mCredentialEntryTypes.add(credentialEntry.getType());
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index 17638fcaba68..dc8cec91001b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -21,8 +21,6 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
import static android.app.admin.WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST;
import static android.app.admin.WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_DENYLIST;
-import static android.app.admin.flags.Flags.dumpsysPolicyEngineMigrationEnabled;
-import static android.app.admin.flags.Flags.policyEngineMigrationV2Enabled;
import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
import static com.android.server.devicepolicy.DevicePolicyManagerService.LOG_TAG;
@@ -41,6 +39,7 @@ import android.app.admin.PackagePolicy;
import android.app.admin.PasswordPolicy;
import android.app.admin.PreferentialNetworkServiceConfig;
import android.app.admin.WifiSsidPolicy;
+import android.app.admin.flags.Flags;
import android.graphics.Color;
import android.net.wifi.WifiSsid;
import android.os.Bundle;
@@ -1297,7 +1296,7 @@ class ActiveAdmin {
pw.print("encryptionRequested=");
pw.println(encryptionRequested);
- if (!dumpsysPolicyEngineMigrationEnabled()) {
+ if (!Flags.dumpsysPolicyEngineMigrationEnabled()) {
pw.print("disableCamera=");
pw.println(disableCamera);
@@ -1316,7 +1315,8 @@ class ActiveAdmin {
UserRestrictionsUtils.dumpRestrictions(pw, " ", userRestrictions);
}
- if (!policyEngineMigrationV2Enabled() || !dumpsysPolicyEngineMigrationEnabled()) {
+ if (!Flags.policyEngineMigrationV2Enabled()
+ || !Flags.dumpsysPolicyEngineMigrationEnabled()) {
pw.print("mUsbDataSignaling=");
pw.println(mUsbDataSignalingEnabled);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
index e7855bc85061..c4e2dc802104 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
@@ -15,6 +15,10 @@
*/
package com.android.server.devicepolicy;
+import static android.app.admin.DevicePolicyManager.CONTENT_PROTECTION_DISABLED;
+import static android.app.admin.DevicePolicyManager.ContentProtectionPolicy;
+
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.admin.DevicePolicyCache;
import android.app.admin.DevicePolicyManager;
@@ -70,10 +74,14 @@ public class DevicePolicyCacheImpl extends DevicePolicyCache {
/** Maps to {@code ActiveAdmin.mAdminCanGrantSensorsPermissions}. */
private final AtomicBoolean mCanGrantSensorsPermissions = new AtomicBoolean(false);
+ @GuardedBy("mLock")
+ private final SparseIntArray mContentProtectionPolicy = new SparseIntArray();
+
public void onUserRemoved(int userHandle) {
synchronized (mLock) {
mPasswordQuality.delete(userHandle);
mPermissionPolicy.delete(userHandle);
+ mContentProtectionPolicy.delete(userHandle);
}
}
@@ -143,6 +151,24 @@ public class DevicePolicyCacheImpl extends DevicePolicyCache {
}
@Override
+ public @ContentProtectionPolicy int getContentProtectionPolicy(@UserIdInt int userId) {
+ synchronized (mLock) {
+ return mContentProtectionPolicy.get(userId, CONTENT_PROTECTION_DISABLED);
+ }
+ }
+
+ /** Update the content protection policy for the given user. */
+ public void setContentProtectionPolicy(@UserIdInt int userId, @Nullable Integer value) {
+ synchronized (mLock) {
+ if (value == null) {
+ mContentProtectionPolicy.delete(userId);
+ } else {
+ mContentProtectionPolicy.put(userId, value);
+ }
+ }
+ }
+
+ @Override
public boolean canAdminGrantSensorsPermissions() {
return mCanGrantSensorsPermissions.get();
}
@@ -178,6 +204,7 @@ public class DevicePolicyCacheImpl extends DevicePolicyCache {
pw.println("Screen capture disallowed users: " + mScreenCaptureDisallowedUsers);
pw.println("Password quality: " + mPasswordQuality);
pw.println("Permission policy: " + mPermissionPolicy);
+ pw.println("Content protection policy: " + mContentProtectionPolicy);
pw.println("Admin can grant sensors permission: " + mCanGrantSensorsPermissions.get());
pw.print("Shortcuts overrides: ");
pw.println(mLauncherShortcutOverrides);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index 105dc880c9a7..12f44074a4ad 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -24,7 +24,6 @@ import static android.app.admin.PolicyUpdateResult.RESULT_FAILURE_HARDWARE_LIMIT
import static android.app.admin.PolicyUpdateResult.RESULT_FAILURE_STORAGE_LIMIT_REACHED;
import static android.app.admin.PolicyUpdateResult.RESULT_POLICY_CLEARED;
import static android.app.admin.PolicyUpdateResult.RESULT_POLICY_SET;
-import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled;
import static android.content.pm.UserProperties.INHERIT_DEVICE_POLICY_FROM_PARENT;
import android.Manifest;
@@ -42,6 +41,7 @@ import android.app.admin.PolicyUpdateReceiver;
import android.app.admin.PolicyValue;
import android.app.admin.TargetUser;
import android.app.admin.UserRestrictionPolicyKey;
+import android.app.admin.flags.Flags;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -102,6 +102,9 @@ final class DevicePolicyEngine {
DevicePolicyIdentifiers.getIdentifierForUserRestriction(
UserManager.DISALLOW_CELLULAR_2G);
+ //TODO(b/295504706) : Speak to security team to decide what to set Policy_Size_Limit
+ private static final int DEFAULT_POLICY_SIZE_LIMIT = -1;
+
private final Context mContext;
private final UserManager mUserManager;
@@ -122,10 +125,11 @@ final class DevicePolicyEngine {
* Map containing the current set of admins in each user with active policies.
*/
private final SparseArray<Set<EnforcingAdmin>> mEnforcingAdmins;
+
private final SparseArray<HashMap<EnforcingAdmin, Integer>> mAdminPolicySize;
- //TODO(b/295504706) : Speak to security team to decide what to set Policy_Size_Limit
- private static final int POLICY_SIZE_LIMIT = 99999;
+ private int mPolicySizeLimit = DEFAULT_POLICY_SIZE_LIMIT;
+
private final DeviceAdminServiceController mDeviceAdminServiceController;
DevicePolicyEngine(
@@ -221,7 +225,7 @@ final class DevicePolicyEngine {
synchronized (mLock) {
PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId);
- if (devicePolicySizeTrackingEnabled()) {
+ if (Flags.devicePolicySizeTrackingEnabled() && false) {
if (!handleAdminPolicySizeLimit(localPolicyState, enforcingAdmin, value,
policyDefinition, userId)) {
return;
@@ -346,7 +350,7 @@ final class DevicePolicyEngine {
}
PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId);
- if (devicePolicySizeTrackingEnabled()) {
+ if (Flags.devicePolicySizeTrackingEnabled() && false) {
decreasePolicySizeForAdmin(localPolicyState, enforcingAdmin);
}
@@ -492,7 +496,7 @@ final class DevicePolicyEngine {
synchronized (mLock) {
PolicyState<V> globalPolicyState = getGlobalPolicyStateLocked(policyDefinition);
- if (devicePolicySizeTrackingEnabled()) {
+ if (Flags.devicePolicySizeTrackingEnabled() && false) {
if (!handleAdminPolicySizeLimit(globalPolicyState, enforcingAdmin, value,
policyDefinition, UserHandle.USER_ALL)) {
return;
@@ -564,7 +568,7 @@ final class DevicePolicyEngine {
synchronized (mLock) {
PolicyState<V> policyState = getGlobalPolicyStateLocked(policyDefinition);
- if (devicePolicySizeTrackingEnabled()) {
+ if (Flags.devicePolicySizeTrackingEnabled() && false) {
decreasePolicySizeForAdmin(policyState, enforcingAdmin);
}
@@ -1594,7 +1598,9 @@ final class DevicePolicyEngine {
existingPolicySize = sizeOf(policyState.getPoliciesSetByAdmins().get(admin));
}
int policySize = sizeOf(value);
- if (currentAdminPoliciesSize + policySize - existingPolicySize < POLICY_SIZE_LIMIT) {
+ // Policy size limit is disabled if mPolicySizeLimit is -1.
+ if (mPolicySizeLimit == -1
+ || currentAdminPoliciesSize + policySize - existingPolicySize < mPolicySizeLimit) {
increasePolicySizeForAdmin(
admin, /* policySizeDiff = */ policySize - existingPolicySize);
return true;
@@ -1642,6 +1648,26 @@ final class DevicePolicyEngine {
}
}
+ /**
+ * Updates the max allowed size limit for policies per admin. Setting it to -1, disables
+ * the limitation.
+ */
+ void setMaxPolicyStorageLimit(int storageLimit) {
+ if (storageLimit < DEFAULT_POLICY_SIZE_LIMIT && storageLimit != -1) {
+ throw new IllegalArgumentException("Can't set a size limit less than the minimum "
+ + "allowed size.");
+ }
+ mPolicySizeLimit = storageLimit;
+ }
+
+ /**
+ * Returns the max allowed size limit for policies per admin. -1 means the limitation is
+ * disabled.
+ */
+ int getMaxPolicyStorageLimit() {
+ return mPolicySizeLimit;
+ }
+
public void dump(IndentingPrintWriter pw) {
synchronized (mLock) {
pw.println("Local Policies: ");
@@ -1761,6 +1787,7 @@ final class DevicePolicyEngine {
private static final String TAG_ENFORCING_ADMIN_AND_SIZE = "enforcing-admin-and-size";
private static final String TAG_ENFORCING_ADMIN = "enforcing-admin";
private static final String TAG_POLICY_SUM_SIZE = "policy-sum-size";
+ private static final String TAG_MAX_POLICY_SIZE_LIMIT = "max-policy-size-limit";
private static final String ATTR_USER_ID = "user-id";
private static final String ATTR_POLICY_SUM_SIZE = "size";
@@ -1805,6 +1832,7 @@ final class DevicePolicyEngine {
writeGlobalPoliciesInner(serializer);
writeEnforcingAdminsInner(serializer);
writeEnforcingAdminSizeInner(serializer);
+ writeMaxPolicySizeInner(serializer);
}
private void writeLocalPoliciesInner(TypedXmlSerializer serializer) throws IOException {
@@ -1864,7 +1892,7 @@ final class DevicePolicyEngine {
private void writeEnforcingAdminSizeInner(TypedXmlSerializer serializer)
throws IOException {
- if (devicePolicySizeTrackingEnabled()) {
+ if (Flags.devicePolicySizeTrackingEnabled() && false) {
if (mAdminPolicySize != null) {
for (int i = 0; i < mAdminPolicySize.size(); i++) {
int userId = mAdminPolicySize.keyAt(i);
@@ -1886,6 +1914,17 @@ final class DevicePolicyEngine {
}
}
+ private void writeMaxPolicySizeInner(TypedXmlSerializer serializer)
+ throws IOException {
+ if (!Flags.devicePolicySizeTrackingEnabled() || true) {
+ return;
+ }
+ serializer.startTag(/* namespace= */ null, TAG_MAX_POLICY_SIZE_LIMIT);
+ serializer.attributeInt(
+ /* namespace= */ null, ATTR_POLICY_SUM_SIZE, mPolicySizeLimit);
+ serializer.endTag(/* namespace= */ null, TAG_MAX_POLICY_SIZE_LIMIT);
+ }
+
void readFromFileLocked() {
if (!mFile.exists()) {
Log.d(TAG, "" + mFile + " doesn't exist");
@@ -1926,6 +1965,9 @@ final class DevicePolicyEngine {
case TAG_ENFORCING_ADMIN_AND_SIZE:
readEnforcingAdminAndSizeInner(parser);
break;
+ case TAG_MAX_POLICY_SIZE_LIMIT:
+ readMaxPolicySizeInner(parser);
+ break;
default:
Slogf.wtf(TAG, "Unknown tag " + tag);
}
@@ -2036,5 +2078,13 @@ final class DevicePolicyEngine {
}
mAdminPolicySize.get(admin.getUserId()).put(admin, size);
}
+
+ private void readMaxPolicySizeInner(TypedXmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ if (!Flags.devicePolicySizeTrackingEnabled() || true) {
+ return;
+ }
+ mPolicySizeLimit = parser.getAttributeInt(/* namespace= */ null, ATTR_POLICY_SUM_SIZE);
+ }
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index b7a2271d8803..0f97f4a7cdc0 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -28,10 +28,13 @@ import static android.Manifest.permission.MANAGE_DEVICE_POLICY_AIRPLANE_MODE;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_APPS_CONTROL;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_APP_RESTRICTIONS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIO_OUTPUT;
+import static android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_AUTOFILL;
+import static android.Manifest.permission.MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_BLUETOOTH;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_CALLS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_CAMERA;
+import static android.Manifest.permission.MANAGE_DEVICE_POLICY_CAMERA_TOGGLE;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_CERTIFICATES;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_CONTENT_PROTECTION;
@@ -50,6 +53,7 @@ import static android.Manifest.permission.MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_LOCK_TASK;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_MICROPHONE;
+import static android.Manifest.permission.MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_MOBILE_NETWORK;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_MODIFY_USERS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_MTE;
@@ -60,6 +64,7 @@ import static android.Manifest.permission.MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_PRINTING;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_PROFILES;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_PROFILE_INTERACTION;
+import static android.Manifest.permission.MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_RESET_PASSWORD;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_RESTRICT_PRIVATE_DNS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS;
@@ -73,6 +78,7 @@ import static android.Manifest.permission.MANAGE_DEVICE_POLICY_STATUS_BAR;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SYSTEM_UPDATES;
+import static android.Manifest.permission.MANAGE_DEVICE_POLICY_THEFT_DETECTION;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_THREAD_NETWORK;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_TIME;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING;
@@ -85,6 +91,7 @@ import static android.Manifest.permission.MANAGE_DEVICE_POLICY_WINDOWS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_WIPE_DATA;
import static android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS;
import static android.Manifest.permission.MASTER_CLEAR;
+import static android.Manifest.permission.NOTIFY_PENDING_SYSTEM_UPDATE;
import static android.Manifest.permission.QUERY_ADMIN_POLICY;
import static android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY;
import static android.Manifest.permission.SET_TIME;
@@ -101,6 +108,7 @@ import static android.app.AppOpsManager.OPSTR_SYSTEM_EXEMPT_FROM_POWER_RESTRICTI
import static android.app.AppOpsManager.OPSTR_SYSTEM_EXEMPT_FROM_SUSPENSION;
import static android.app.admin.DeviceAdminInfo.HEADLESS_DEVICE_OWNER_MODE_AFFILIATED;
import static android.app.admin.DeviceAdminInfo.HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER;
+import static android.app.admin.DeviceAdminInfo.HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED;
import static android.app.admin.DeviceAdminInfo.USES_POLICY_FORCE_LOCK;
import static android.app.admin.DeviceAdminInfo.USES_POLICY_WIPE_DATA;
import static android.app.admin.DeviceAdminReceiver.ACTION_COMPLIANCE_ACKNOWLEDGEMENT_REQUIRED;
@@ -231,13 +239,6 @@ import static android.app.admin.ProvisioningException.ERROR_REMOVE_NON_REQUIRED_
import static android.app.admin.ProvisioningException.ERROR_SETTING_PROFILE_OWNER_FAILED;
import static android.app.admin.ProvisioningException.ERROR_SET_DEVICE_OWNER_FAILED;
import static android.app.admin.ProvisioningException.ERROR_STARTING_PROFILE_FAILED;
-import static android.app.admin.flags.Flags.backupServiceSecurityLogEventEnabled;
-import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled;
-import static android.app.admin.flags.Flags.dumpsysPolicyEngineMigrationEnabled;
-import static android.app.admin.flags.Flags.headlessDeviceOwnerSingleUserEnabled;
-import static android.app.admin.flags.Flags.policyEngineMigrationV2Enabled;
-import static android.app.admin.flags.Flags.assistContentUserRestrictionEnabled;
-import static android.app.admin.flags.Flags.securityLogV2Enabled;
import static android.content.Intent.ACTION_MANAGED_PROFILE_AVAILABLE;
import static android.content.Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -263,6 +264,7 @@ import static android.security.keystore.AttestationUtils.USE_INDIVIDUAL_ATTESTAT
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_ENTRY_POINT_ADB;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_HIGH;
import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
@@ -324,6 +326,7 @@ import android.app.admin.DevicePolicyStringResource;
import android.app.admin.DeviceStateCache;
import android.app.admin.FactoryResetProtectionPolicy;
import android.app.admin.FullyManagedDeviceProvisioningParams;
+import android.app.admin.IAuditLogEventsCallback;
import android.app.admin.IDevicePolicyManager;
import android.app.admin.IntegerPolicyValue;
import android.app.admin.IntentFilterPolicyKey;
@@ -350,6 +353,7 @@ import android.app.admin.SystemUpdatePolicy;
import android.app.admin.UnsafeStateException;
import android.app.admin.UserRestrictionPolicyKey;
import android.app.admin.WifiSsidPolicy;
+import android.app.admin.flags.Flags;
import android.app.backup.IBackupManager;
import android.app.compat.CompatChanges;
import android.app.role.OnRoleHoldersChangedListener;
@@ -503,7 +507,6 @@ import com.android.internal.widget.PasswordValidationError;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
import com.android.net.module.util.ProxyUtils;
-import com.android.net.thread.flags.Flags;
import com.android.server.AlarmManagerInternal;
import com.android.server.LocalManagerRegistry;
import com.android.server.LocalServices;
@@ -2056,7 +2059,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
mLockPatternUtils = injector.newLockPatternUtils();
mLockSettingsInternal = injector.getLockSettingsInternal();
// TODO: why does SecurityLogMonitor need to be created even when mHasFeature == false?
- mSecurityLogMonitor = new SecurityLogMonitor(this);
+ mSecurityLogMonitor = new SecurityLogMonitor(this, mHandler);
mHasFeature = mInjector.hasFeature();
mIsWatch = mInjector.getPackageManager()
@@ -2714,8 +2717,20 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
private void maybeStartSecurityLogMonitorOnActivityManagerReady() {
- synchronized (getLockObject()) {
- if (mInjector.securityLogIsLoggingEnabled()) {
+ if (!mInjector.securityLogIsLoggingEnabled()) {
+ return;
+ }
+
+ if (Flags.securityLogV2Enabled()) {
+ boolean auditLoggingEnabled = Boolean.TRUE.equals(
+ mDevicePolicyEngine.getResolvedPolicy(
+ PolicyDefinition.AUDIT_LOGGING, UserHandle.USER_ALL));
+ boolean securityLoggingEnabled = Boolean.TRUE.equals(
+ mDevicePolicyEngine.getResolvedPolicy(
+ PolicyDefinition.SECURITY_LOGGING, UserHandle.USER_ALL));
+ setLoggingConfiguration(securityLoggingEnabled, auditLoggingEnabled);
+ } else {
+ synchronized (getLockObject()) {
mSecurityLogMonitor.start(getSecurityLoggingEnabledUser());
mInjector.runCryptoSelfTest();
maybePauseDeviceWideLoggingLocked();
@@ -3396,7 +3411,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@GuardedBy("getLockObject()")
private void maybeMigrateSecurityLoggingPolicyLocked() {
- if (!securityLogV2Enabled() || mOwners.isSecurityLoggingMigrated()) {
+ if (!Flags.securityLogV2Enabled() || mOwners.isSecurityLoggingMigrated()) {
return;
}
@@ -3500,7 +3515,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
revertTransferOwnershipIfNecessaryLocked();
- if (!policyEngineMigrationV2Enabled()) {
+ if (!Flags.policyEngineMigrationV2Enabled()) {
updateUsbDataSignal(mContext, isUsbDataSignalingEnabledInternalLocked());
}
}
@@ -3618,6 +3633,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
userId == UserHandle.USER_SYSTEM ? UserHandle.USER_ALL : userId);
updatePermissionPolicyCache(userId);
updateAdminCanGrantSensorsPermissionCache(userId);
+ updateContentProtectionPolicyCache(userId);
final List<PreferentialNetworkServiceConfig> preferentialNetworkServiceConfigs;
synchronized (getLockObject()) {
@@ -9524,7 +9540,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
private int getHeadlessDeviceOwnerMode() {
synchronized (getLockObject()) {
- return getDeviceOwnerAdminLocked().info.getHeadlessDeviceOwnerMode();
+ ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+ if (deviceOwner == null) {
+ return HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED;
+ }
+ return deviceOwner.info.getHeadlessDeviceOwnerMode();
}
}
@@ -11125,7 +11145,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
pw.println();
mStatLogger.dump(pw);
pw.println();
- if (dumpsysPolicyEngineMigrationEnabled()) {
+ if (Flags.dumpsysPolicyEngineMigrationEnabled()) {
mDevicePolicyEngine.dump(pw);
pw.println();
}
@@ -12042,8 +12062,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
if (packageList != null) {
- for (String pkg : packageList) {
- PolicySizeVerifier.enforceMaxPackageNameLength(pkg);
+ if (!Flags.devicePolicySizeTrackingEnabled()) {
+ for (String pkg : packageList) {
+ PolicySizeVerifier.enforceMaxPackageNameLength(pkg);
+ }
}
List<InputMethodInfo> enabledImes = mInjector.binderWithCleanCallingIdentity(() ->
@@ -12279,18 +12301,19 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
final CallerIdentity caller = getCallerIdentity(admin);
- if (headlessDeviceOwnerSingleUserEnabled()) {
- // Block this method if the device is in headless main user mode
- Preconditions.checkCallAuthorization(
- getHeadlessDeviceOwnerMode() != HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER,
- "createAndManageUser was called while in headless single user mode");
- }
// Only allow the system user to use this method
Preconditions.checkCallAuthorization(caller.getUserHandle().isSystem(),
"createAndManageUser was called from non-system user");
Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_CREATE_AND_MANAGE_USER);
+ if (Flags.headlessDeviceOwnerSingleUserEnabled()) {
+ // Block this method if the device is in headless main user mode
+ Preconditions.checkCallAuthorization(
+ getHeadlessDeviceOwnerMode() != HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER,
+ "createAndManageUser was called while in headless single user mode");
+ }
+
final boolean ephemeral = (flags & DevicePolicyManager.MAKE_USER_EPHEMERAL) != 0;
final boolean demo = (flags & DevicePolicyManager.MAKE_USER_DEMO) != 0
&& UserManager.isDeviceInDemoMode(mContext);
@@ -13409,12 +13432,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
UserManager.DISALLOW_SMS, new String[]{MANAGE_DEVICE_POLICY_SMS});
USER_RESTRICTION_PERMISSIONS.put(
UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS, new String[]{MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS});
- if (Flags.threadUserRestrictionEnabled()) {
+ if (com.android.net.thread.platform.flags.Flags.threadUserRestrictionEnabled()) {
USER_RESTRICTION_PERMISSIONS.put(
UserManager.DISALLOW_THREAD_NETWORK,
new String[]{MANAGE_DEVICE_POLICY_THREAD_NETWORK});
}
- if (assistContentUserRestrictionEnabled()) {
+ if (Flags.assistContentUserRestrictionEnabled()) {
USER_RESTRICTION_PERMISSIONS.put(
UserManager.DISALLOW_ASSIST_CONTENT,
new String[]{MANAGE_DEVICE_POLICY_ASSIST_CONTENT});
@@ -13748,7 +13771,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return;
}
- if (!devicePolicySizeTrackingEnabled()) {
+ if (!Flags.devicePolicySizeTrackingEnabled()) {
PolicySizeVerifier.enforceMaxStringLength(accountType, "account type");
}
@@ -14362,8 +14385,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
public void setLockTaskPackages(ComponentName who, String callerPackageName, String[] packages)
throws SecurityException {
Objects.requireNonNull(packages, "packages is null");
- for (String pkg : packages) {
- PolicySizeVerifier.enforceMaxPackageNameLength(pkg);
+ if (!Flags.devicePolicySizeTrackingEnabled()) {
+ for (String pkg : packages) {
+ PolicySizeVerifier.enforceMaxPackageNameLength(pkg);
+ }
}
CallerIdentity caller = getCallerIdentity(who, callerPackageName);
@@ -15767,7 +15792,22 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public void enforceSecurityLoggingPolicy(boolean enabled) {
- enforceLoggingPolicy(enabled);
+ if (!Flags.securityLogV2Enabled()) {
+ return;
+ }
+ Boolean auditLoggingEnabled = mDevicePolicyEngine.getResolvedPolicy(
+ PolicyDefinition.AUDIT_LOGGING, UserHandle.USER_ALL);
+ enforceLoggingPolicy(enabled, Boolean.TRUE.equals(auditLoggingEnabled));
+ }
+
+ @Override
+ public void enforceAuditLoggingPolicy(boolean enabled) {
+ if (!Flags.securityLogV2Enabled()) {
+ return;
+ }
+ Boolean securityLoggingEnabled = mDevicePolicyEngine.getResolvedPolicy(
+ PolicyDefinition.SECURITY_LOGGING, UserHandle.USER_ALL);
+ enforceLoggingPolicy(Boolean.TRUE.equals(securityLoggingEnabled), enabled);
}
private List<EnforcingUser> getEnforcingUsers(Set<EnforcingAdmin> admins) {
@@ -15787,17 +15827,23 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
- private void enforceLoggingPolicy(boolean securityLoggingEnabled) {
- Slogf.i(LOG_TAG, "Enforcing security logging, securityLoggingEnabled: %b",
- securityLoggingEnabled);
- SecurityLog.setLoggingEnabledProperty(securityLoggingEnabled);
- if (securityLoggingEnabled) {
- mSecurityLogMonitor.start(getSecurityLoggingEnabledUser());
+ private void enforceLoggingPolicy(
+ boolean securityLogEnabled, boolean auditLogEnabled) {
+ Slogf.i(LOG_TAG, "Enforcing logging policy, security: %b audit: %b",
+ securityLogEnabled, auditLogEnabled);
+ mInjector.securityLogSetLoggingEnabledProperty(securityLogEnabled || auditLogEnabled);
+ setLoggingConfiguration(securityLogEnabled, auditLogEnabled);
+ }
+
+ private void setLoggingConfiguration(
+ boolean securityLoggingEnabled, boolean auditLoggingEnabled) {
+ final int loggingEnabledUser = getSecurityLoggingEnabledUser();
+ mSecurityLogMonitor.setLoggingParams(
+ loggingEnabledUser, securityLoggingEnabled, auditLoggingEnabled);
+ if (securityLoggingEnabled || auditLoggingEnabled) {
synchronized (getLockObject()) {
maybePauseDeviceWideLoggingLocked();
}
- } else {
- mSecurityLogMonitor.stop();
}
}
@@ -16243,7 +16289,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public void notifyPendingSystemUpdate(@Nullable SystemUpdateInfo info) {
Preconditions.checkCallAuthorization(
- hasCallingOrSelfPermission(permission.NOTIFY_PENDING_SYSTEM_UPDATE),
+ hasCallingOrSelfPermission(NOTIFY_PENDING_SYSTEM_UPDATE),
"Only the system update service can broadcast update information");
mInjector.binderWithCleanCallingIdentity(() -> {
@@ -16284,12 +16330,21 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
// Send broadcasts to corresponding profile owners if any.
for (final int userId : runningUserIds) {
+ final ComponentName profileOwnerPackage;
synchronized (getLockObject()) {
- final ComponentName profileOwnerPackage =
- mOwners.getProfileOwnerComponent(userId);
- if (profileOwnerPackage != null) {
- intent.setComponent(profileOwnerPackage);
- mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
+ profileOwnerPackage = mOwners.getProfileOwnerComponent(userId);
+ }
+ if (profileOwnerPackage != null) {
+ intent.setComponent(profileOwnerPackage);
+ mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
+ }
+
+ if (Flags.permissionMigrationForZeroTrustImplEnabled()) {
+ final UserHandle user = UserHandle.of(userId);
+ final String roleHolderPackage = getRoleHolderPackageNameOnUser(
+ RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT, userId);
+ if (roleHolderPackage != null) {
+ broadcastExplicitIntentToPackage(intent, roleHolderPackage, user);
}
}
}
@@ -16297,13 +16352,19 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
@Override
- public SystemUpdateInfo getPendingSystemUpdate(ComponentName admin) {
- Objects.requireNonNull(admin, "ComponentName is null");
-
- final CallerIdentity caller = getCallerIdentity(admin);
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller) || isProfileOwner(caller));
+ public SystemUpdateInfo getPendingSystemUpdate(ComponentName admin, String callerPackage) {
+ if (Flags.permissionMigrationForZeroTrustImplEnabled()) {
+ CallerIdentity caller = getCallerIdentity(admin, callerPackage);
+ enforcePermissions(new String[] {NOTIFY_PENDING_SYSTEM_UPDATE,
+ MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES}, caller.getPackageName(),
+ caller.getUserId());
+ } else {
+ Objects.requireNonNull(admin, "ComponentName is null");
+ final CallerIdentity caller = getCallerIdentity(admin);
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller));
+ }
return mOwners.getSystemUpdateInfo();
}
@@ -16749,7 +16810,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return STATUS_HEADLESS_SYSTEM_USER_MODE_NOT_SUPPORTED;
}
- if (headlessDeviceOwnerSingleUserEnabled() && isHeadlessModeSingleUser) {
+ if (Flags.headlessDeviceOwnerSingleUserEnabled() && isHeadlessModeSingleUser) {
ensureSetUpUser = mUserManagerInternal.getMainUserId();
if (ensureSetUpUser == UserHandle.USER_NULL) {
return STATUS_HEADLESS_ONLY_SYSTEM_USER;
@@ -17656,7 +17717,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
final CallerIdentity caller = getCallerIdentity(who, packageName);
- if (securityLogV2Enabled()) {
+ if (Flags.securityLogV2Enabled()) {
EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin(
who,
MANAGE_DEVICE_POLICY_SECURITY_LOGGING,
@@ -17716,7 +17777,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return mInjector.securityLogGetLoggingEnabledProperty();
}
- if (securityLogV2Enabled()) {
+ if (Flags.securityLogV2Enabled()) {
final EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
admin,
MANAGE_DEVICE_POLICY_SECURITY_LOGGING,
@@ -17814,7 +17875,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final CallerIdentity caller = getCallerIdentity(admin, packageName);
- if (securityLogV2Enabled()) {
+ if (Flags.securityLogV2Enabled()) {
EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
admin,
MANAGE_DEVICE_POLICY_SECURITY_LOGGING,
@@ -17863,6 +17924,82 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
@Override
+ public void setAuditLogEnabled(String callingPackage, boolean enabled) {
+ if (!mHasFeature) {
+ return;
+ }
+ final CallerIdentity caller = getCallerIdentity(callingPackage);
+
+ if (!Flags.securityLogV2Enabled()) {
+ throw new UnsupportedOperationException("Audit log not enabled");
+ }
+
+ EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin(
+ null /* admin */,
+ MANAGE_DEVICE_POLICY_AUDIT_LOGGING,
+ caller.getPackageName(),
+ caller.getUserId());
+ if (enabled) {
+ mDevicePolicyEngine.setGlobalPolicy(
+ PolicyDefinition.AUDIT_LOGGING,
+ admin,
+ new BooleanPolicyValue(true));
+ } else {
+ mDevicePolicyEngine.removeGlobalPolicy(
+ PolicyDefinition.AUDIT_LOGGING,
+ admin);
+ mSecurityLogMonitor.setAuditLogEventsCallback(caller.getUid(), null /* callback */);
+ }
+ }
+
+ @Override
+ public boolean isAuditLogEnabled(String callingPackage) {
+ if (!mHasFeature) {
+ return false;
+ }
+
+ if (!Flags.securityLogV2Enabled()) {
+ throw new UnsupportedOperationException("Audit log not enabled");
+ }
+
+ final CallerIdentity caller = getCallerIdentity(callingPackage);
+ EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin(
+ null /* admin */,
+ MANAGE_DEVICE_POLICY_AUDIT_LOGGING,
+ caller.getPackageName(),
+ caller.getUserId());
+
+ Boolean policy = mDevicePolicyEngine.getGlobalPolicySetByAdmin(
+ PolicyDefinition.AUDIT_LOGGING, admin);
+
+ return Boolean.TRUE.equals(policy);
+ }
+
+ @Override
+ public void setAuditLogEventsCallback(String callingPackage, IAuditLogEventsCallback callback) {
+ if (!mHasFeature) {
+ return;
+ }
+
+ final CallerIdentity caller = getCallerIdentity(callingPackage);
+ EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin(
+ null /* admin */,
+ MANAGE_DEVICE_POLICY_AUDIT_LOGGING,
+ caller.getPackageName(),
+ caller.getUserId());
+
+ Boolean policy = mDevicePolicyEngine.getGlobalPolicySetByAdmin(
+ PolicyDefinition.AUDIT_LOGGING, admin);
+
+ if (!Boolean.TRUE.equals(policy)) {
+ throw new IllegalStateException(
+ "Managing app has to enable audit log before setting events callback");
+ }
+
+ mSecurityLogMonitor.setAuditLogEventsCallback(caller.getUid(), callback);
+ }
+
+ @Override
public long forceSecurityLogs() {
Preconditions.checkCallAuthorization(isAdb(getCallerIdentity())
|| hasCallingOrSelfPermission(permission.FORCE_DEVICE_POLICY_MANAGER_LOGS),
@@ -18087,7 +18224,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
toggleBackupServiceActive(caller.getUserId(), enabled);
- if (backupServiceSecurityLogEventEnabled()) {
+ if (Flags.backupServiceSecurityLogEventEnabled()) {
if (SecurityLog.isLoggingEnabled()) {
SecurityLog.writeEvent(SecurityLog.TAG_BACKUP_SERVICE_TOGGLED,
caller.getPackageName(), caller.getUserId(), enabled ? 1 : 0);
@@ -20807,14 +20944,18 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
final CallerIdentity caller = getCallerIdentity(callerPackage);
- Preconditions.checkCallAuthorization(
- isDefaultDeviceOwner(caller) || isProfileOwner(caller)
- || isCallerDelegate(caller, DELEGATION_CERT_INSTALL));
+ if (Flags.permissionMigrationForZeroTrustImplEnabled()) {
+ enforcePermission(MANAGE_DEVICE_POLICY_CERTIFICATES, caller.getPackageName());
+ } else {
+ Preconditions.checkCallAuthorization(
+ isDefaultDeviceOwner(caller) || isProfileOwner(caller)
+ || isCallerDelegate(caller, DELEGATION_CERT_INSTALL));
+ }
synchronized (getLockObject()) {
final ActiveAdmin requiredAdmin = getDeviceOrProfileOwnerAdminLocked(
caller.getUserId());
- final String esid = requiredAdmin.mEnrollmentSpecificId;
+ final String esid = requiredAdmin != null ? requiredAdmin.mEnrollmentSpecificId : null;
return esid != null ? esid : "";
}
}
@@ -21408,7 +21549,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
setTimeAndTimezone(provisioningParams.getTimeZone(), provisioningParams.getLocalTime());
setLocale(provisioningParams.getLocale());
- int deviceOwnerUserId = headlessDeviceOwnerSingleUserEnabled()
+ int deviceOwnerUserId = Flags.headlessDeviceOwnerSingleUserEnabled()
&& getHeadlessDeviceOwnerMode() == HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER
? mUserManagerInternal.getMainUserId()
: UserHandle.USER_SYSTEM;
@@ -21785,7 +21926,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
Objects.requireNonNull(packageName, "Admin package name must be provided");
final CallerIdentity caller = getCallerIdentity(packageName);
- if (!policyEngineMigrationV2Enabled()) {
+ if (!Flags.policyEngineMigrationV2Enabled()) {
Preconditions.checkCallAuthorization(
isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
"USB data signaling can only be controlled by a device owner or "
@@ -21795,7 +21936,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
synchronized (getLockObject()) {
- if (policyEngineMigrationV2Enabled()) {
+ if (Flags.policyEngineMigrationV2Enabled()) {
EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
/* admin= */ null, MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING,
caller.getPackageName(),
@@ -21835,7 +21976,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public boolean isUsbDataSignalingEnabled(String packageName) {
final CallerIdentity caller = getCallerIdentity(packageName);
- if (policyEngineMigrationV2Enabled()) {
+ if (Flags.policyEngineMigrationV2Enabled()) {
Boolean enabled = mDevicePolicyEngine.getResolvedPolicy(
PolicyDefinition.USB_DATA_SIGNALING,
caller.getUserId());
@@ -21952,6 +22093,20 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
@Override
+ public boolean isTheftDetectionTriggered(String callerPackageName) {
+ final CallerIdentity caller = getCallerIdentity(callerPackageName);
+ if (!android.app.admin.flags.Flags.deviceTheftImplEnabled()) {
+ return false;
+ }
+ enforcePermission(MANAGE_DEVICE_POLICY_THEFT_DETECTION, caller.getPackageName(),
+ caller.getUserId());
+
+ return mInjector.binderWithCleanCallingIdentity(() ->
+ 0 != (mLockPatternUtils.getStrongAuthForUser(caller.getUserId())
+ & SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST));
+ }
+
+ @Override
public void setWifiSsidPolicy(String callerPackageName, WifiSsidPolicy policy) {
CallerIdentity caller;
@@ -22392,14 +22547,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
});
}
- // Permission that will need to be created in V.
- private static final String MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL =
- "manage_device_policy_block_uninstall";
- private static final String MANAGE_DEVICE_POLICY_CAMERA_TOGGLE =
- "manage_device_policy_camera_toggle";
- private static final String MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE =
- "manage_device_policy_microphone_toggle";
-
// DPC types
private static final int NOT_A_DPC = -1;
private static final int DEFAULT_DEVICE_OWNER = 0;
@@ -22485,7 +22632,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
MANAGE_DEVICE_POLICY_WINDOWS,
MANAGE_DEVICE_POLICY_WIPE_DATA,
SET_TIME,
- SET_TIME_ZONE
+ SET_TIME_ZONE,
+ MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES
);
private static final List<String> FINANCED_DEVICE_OWNER_PERMISSIONS = List.of(
MANAGE_DEVICE_POLICY_ACROSS_USERS,
@@ -22549,7 +22697,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS,
MANAGE_DEVICE_POLICY_TIME,
MANAGE_DEVICE_POLICY_VPN,
- MANAGE_DEVICE_POLICY_WIPE_DATA
+ MANAGE_DEVICE_POLICY_WIPE_DATA,
+ MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES
);
/**
@@ -23386,6 +23535,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
+ private void updateContentProtectionPolicyCache(@UserIdInt int userId) {
+ mPolicyCache.setContentProtectionPolicy(
+ userId,
+ mDevicePolicyEngine.getResolvedPolicy(PolicyDefinition.CONTENT_PROTECTION, userId));
+ }
+
@Override
public ManagedSubscriptionsPolicy getManagedSubscriptionsPolicy() {
synchronized (getLockObject()) {
@@ -24075,5 +24230,30 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
return adminOwnedSubscriptions;
});
+
+ }
+
+ @Override
+ public void setMaxPolicyStorageLimit(String callerPackageName, int storageLimit) {
+ if (!Flags.devicePolicySizeTrackingEnabled() || true) {
+ return;
+ }
+ CallerIdentity caller = getCallerIdentity(callerPackageName);
+ enforcePermission(MANAGE_PROFILE_AND_DEVICE_OWNERS, caller.getPackageName(),
+ caller.getUserId());
+
+ mDevicePolicyEngine.setMaxPolicyStorageLimit(storageLimit);
+ }
+
+ @Override
+ public int getMaxPolicyStorageLimit(String callerPackageName) {
+ if (!Flags.devicePolicySizeTrackingEnabled() || true) {
+ return -1;
+ }
+ CallerIdentity caller = getCallerIdentity(callerPackageName);
+ enforcePermission(MANAGE_PROFILE_AND_DEVICE_OWNERS, caller.getPackageName(),
+ caller.getUserId());
+
+ return mDevicePolicyEngine.getMaxPolicyStorageLimit();
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
index d9fef10ee41b..9d73ed0070c8 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
@@ -16,11 +16,11 @@
package com.android.server.devicepolicy;
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT;
-import static android.app.admin.flags.Flags.securityLogV2Enabled;
import android.annotation.Nullable;
import android.app.admin.SystemUpdateInfo;
import android.app.admin.SystemUpdatePolicy;
+import android.app.admin.flags.Flags;
import android.content.ComponentName;
import android.os.UserHandle;
import android.util.ArrayMap;
@@ -400,7 +400,7 @@ class OwnersData {
out.startTag(null, TAG_POLICY_ENGINE_MIGRATION);
out.attributeBoolean(null, ATTR_MIGRATED_TO_POLICY_ENGINE, mMigratedToPolicyEngine);
- if (securityLogV2Enabled()) {
+ if (Flags.securityLogV2Enabled()) {
out.attributeBoolean(null, ATTR_SECURITY_LOG_MIGRATED, mSecurityLoggingMigrated);
}
out.endTag(null, TAG_POLICY_ENGINE_MIGRATION);
@@ -463,7 +463,7 @@ class OwnersData {
case TAG_POLICY_ENGINE_MIGRATION:
mMigratedToPolicyEngine = parser.getAttributeBoolean(
null, ATTR_MIGRATED_TO_POLICY_ENGINE, false);
- mSecurityLoggingMigrated = securityLogV2Enabled()
+ mSecurityLoggingMigrated = Flags.securityLogV2Enabled()
&& parser.getAttributeBoolean(null, ATTR_SECURITY_LOG_MIGRATED, false);
break;
default:
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
index e8c5658ca941..8cb511e8727c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
@@ -17,11 +17,11 @@
package com.android.server.devicepolicy;
import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
-import static android.app.admin.flags.Flags.defaultSmsPersonalAppSuspensionFixEnabled;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.admin.flags.Flags;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -206,7 +206,7 @@ public final class PersonalAppsSuspensionHelper {
private String getDefaultSmsPackage() {
//TODO(b/319449037): Unflag the following change.
- if (defaultSmsPersonalAppSuspensionFixEnabled()) {
+ if (Flags.defaultSmsPersonalAppSuspensionFixEnabled()) {
return SmsApplication.getDefaultSmsApplicationAsUser(
mContext, /*updateIfNeeded=*/ false, mContext.getUser())
.getPackageName();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
index 3474db3c7c1f..71facab99fce 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
@@ -141,6 +141,13 @@ final class PolicyDefinition<V> {
PolicyEnforcerCallbacks::enforceSecurityLogging,
new BooleanPolicySerializer());
+ static PolicyDefinition<Boolean> AUDIT_LOGGING = new PolicyDefinition<>(
+ new NoArgsPolicyKey(DevicePolicyIdentifiers.AUDIT_LOGGING_POLICY),
+ TRUE_MORE_RESTRICTIVE,
+ POLICY_FLAG_GLOBAL_ONLY_POLICY,
+ PolicyEnforcerCallbacks::enforceAuditLogging,
+ new BooleanPolicySerializer());
+
static PolicyDefinition<LockTaskPolicy> LOCK_TASK = new PolicyDefinition<>(
new NoArgsPolicyKey(DevicePolicyIdentifiers.LOCK_TASK_POLICY),
new TopPriority<>(List.of(
@@ -352,7 +359,7 @@ final class PolicyDefinition<V> {
new NoArgsPolicyKey(DevicePolicyIdentifiers.CONTENT_PROTECTION_POLICY),
new MostRecent<>(),
POLICY_FLAG_LOCAL_ONLY_POLICY,
- (Integer value, Context context, Integer userId, PolicyKey policyKey) -> true,
+ PolicyEnforcerCallbacks::setContentProtectionPolicy,
new IntegerPolicySerializer());
private static final Map<String, PolicyDefinition<?>> POLICY_DEFINITIONS = new HashMap<>();
@@ -365,6 +372,8 @@ final class PolicyDefinition<V> {
GENERIC_PERMISSION_GRANT);
POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.SECURITY_LOGGING_POLICY,
SECURITY_LOGGING);
+ POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.AUDIT_LOGGING_POLICY,
+ AUDIT_LOGGING);
POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.LOCK_TASK_POLICY, LOCK_TASK);
POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.USER_CONTROL_DISABLED_PACKAGES_POLICY,
USER_CONTROLLED_DISABLED_PACKAGES);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
index 4aaefa670ea2..c108deaf33bc 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
@@ -18,6 +18,7 @@ package com.android.server.devicepolicy;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.AppGlobals;
import android.app.admin.DevicePolicyCache;
import android.app.admin.DevicePolicyManager;
@@ -136,6 +137,14 @@ final class PolicyEnforcerCallbacks {
return true;
}
+ static boolean enforceAuditLogging(
+ @Nullable Boolean value, @NonNull Context context, int userId,
+ @NonNull PolicyKey policyKey) {
+ final var dpmi = LocalServices.getService(DevicePolicyManagerInternal.class);
+ dpmi.enforceAuditLoggingPolicy(Boolean.TRUE.equals(value));
+ return true;
+ }
+
static boolean setLockTask(
@Nullable LockTaskPolicy policy, @NonNull Context context, int userId) {
List<String> packages = Collections.emptyList();
@@ -274,6 +283,21 @@ final class PolicyEnforcerCallbacks {
return true;
}
+ static boolean setContentProtectionPolicy(
+ @Nullable Integer value,
+ @NonNull Context context,
+ @UserIdInt Integer userId,
+ @NonNull PolicyKey policyKey) {
+ Binder.withCleanCallingIdentity(
+ () -> {
+ DevicePolicyCache cache = DevicePolicyCache.getInstance();
+ if (cache instanceof DevicePolicyCacheImpl cacheImpl) {
+ cacheImpl.setContentProtectionPolicy(userId, value);
+ }
+ });
+ return true;
+ }
+
private static void updateScreenCaptureDisabled() {
BackgroundThread.getHandler().post(() -> {
try {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
index 7a4454b11fce..c582a462db81 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
@@ -20,18 +20,27 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import android.app.admin.DeviceAdminReceiver;
+import android.app.admin.IAuditLogEventsCallback;
import android.app.admin.SecurityLog;
import android.app.admin.SecurityLog.SecurityEvent;
+import android.app.admin.flags.Flags;
+import android.os.Handler;
+import android.os.IBinder;
import android.os.Process;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
import android.util.Slog;
+import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.utils.Slogf;
import java.io.IOException;
+import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
@@ -53,15 +62,11 @@ class SecurityLogMonitor implements Runnable {
private int mEnabledUser;
- SecurityLogMonitor(DevicePolicyManagerService service) {
- this(service, 0 /* id */);
- }
-
- @VisibleForTesting
- SecurityLogMonitor(DevicePolicyManagerService service, long id) {
+ SecurityLogMonitor(DevicePolicyManagerService service, Handler handler) {
mService = service;
- mId = id;
+ mId = 0;
mLastForceNanos = System.nanoTime();
+ mHandler = handler;
}
private static final boolean DEBUG = false; // STOPSHIP if true.
@@ -118,6 +123,9 @@ class SecurityLogMonitor implements Runnable {
@GuardedBy("mLock")
private boolean mCriticalLevelLogged = false;
+ private boolean mLegacyLogEnabled;
+ private boolean mAuditLogEnabled;
+
/**
* Last events fetched from log to check for overlap between batches. We can leave it empty if
* we are sure there will be no overlap anymore, e.g. when we get empty batch.
@@ -143,6 +151,40 @@ class SecurityLogMonitor implements Runnable {
private long mLastForceNanos = 0;
/**
+ * Handler shared with DPMS.
+ */
+ private final Handler mHandler;
+
+ /**
+ * Oldest events get purged from audit log buffer if total number exceeds this value.
+ */
+ private static final int MAX_AUDIT_LOG_EVENTS = 10000;
+ /**
+ * Events older than this get purged from audit log buffer.
+ */
+ private static final long MAX_AUDIT_LOG_EVENT_AGE_NS = TimeUnit.HOURS.toNanos(8);
+
+ /**
+ * Audit log callbacks keyed by UID. The code should maintain the following invariant: all
+ * callbacks in this map have received (or are scheduled to receive) all events in
+ * mAuditLogEventsBuffer. To ensure this, before a callback is put into this map, it must be
+ * scheduled to receive all the events in the buffer, and conversely, before a new chunk of
+ * events is added to the buffer, it must be scheduled to be sent to all callbacks already in
+ * this list. All scheduling should happen on mHandler, so that they aren't reordered, and
+ * while holding the lock. This ensures that no callback misses an event or receives a duplicate
+ * or out of order events.
+ */
+ @GuardedBy("mLock")
+ private final SparseArray<IAuditLogEventsCallback> mAuditLogCallbacks = new SparseArray<>();
+
+ /**
+ * Audit log event buffer. It is shrunk automatically whenever either there are too many events
+ * or the oldest one is too old.
+ */
+ @GuardedBy("mLock")
+ private final ArrayDeque<SecurityEvent> mAuditLogEventBuffer = new ArrayDeque<>();
+
+ /**
* Start security logging.
*
* @param enabledUser which user logging is enabled on, or USER_ALL to enable logging for all
@@ -154,18 +196,8 @@ class SecurityLogMonitor implements Runnable {
mLock.lock();
try {
if (mMonitorThread == null) {
- mPendingLogs = new ArrayList<>();
- mCriticalLevelLogged = false;
- mId = 0;
- mAllowedToRetrieve = false;
- mNextAllowedRetrievalTimeMillis = -1;
- mPaused = false;
-
- mMonitorThread = new Thread(this);
- mMonitorThread.start();
-
- SecurityLog.writeEvent(SecurityLog.TAG_LOGGING_STARTED);
- Slog.i(TAG, "Security log monitor thread started");
+ resetLegacyBufferLocked();
+ startMonitorThreadLocked();
} else {
Slog.i(TAG, "Security log monitor thread is already running");
}
@@ -176,27 +208,80 @@ class SecurityLogMonitor implements Runnable {
void stop() {
Slog.i(TAG, "Stopping security logging.");
- SecurityLog.writeEvent(SecurityLog.TAG_LOGGING_STOPPED);
mLock.lock();
try {
if (mMonitorThread != null) {
- mMonitorThread.interrupt();
- try {
- mMonitorThread.join(TimeUnit.SECONDS.toMillis(5));
- } catch (InterruptedException e) {
- Log.e(TAG, "Interrupted while waiting for thread to stop", e);
- }
- // Reset state and clear buffer
- mPendingLogs = new ArrayList<>();
- mId = 0;
- mAllowedToRetrieve = false;
- mNextAllowedRetrievalTimeMillis = -1;
- mPaused = false;
- mMonitorThread = null;
+ stopMonitorThreadLocked();
+ resetLegacyBufferLocked();
+ }
+ } finally {
+ mLock.unlock();
+ }
+ }
+
+ void setLoggingParams(int enabledUser, boolean legacyLogEnabled, boolean auditLogEnabled) {
+ Slogf.i(TAG, "Setting logging params, user = %d -> %d, legacy: %b -> %b, audit %b -> %b",
+ mEnabledUser, enabledUser, mLegacyLogEnabled, legacyLogEnabled, mAuditLogEnabled,
+ auditLogEnabled);
+ mLock.lock();
+ try {
+ mEnabledUser = enabledUser;
+ if (mMonitorThread == null && (legacyLogEnabled || auditLogEnabled)) {
+ startMonitorThreadLocked();
+ } else if (mMonitorThread != null && !legacyLogEnabled && !auditLogEnabled) {
+ stopMonitorThreadLocked();
+ }
+
+ if (mLegacyLogEnabled != legacyLogEnabled) {
+ resetLegacyBufferLocked();
+ mLegacyLogEnabled = legacyLogEnabled;
+ }
+
+ if (mAuditLogEnabled != auditLogEnabled) {
+ resetAuditBufferLocked();
+ mAuditLogEnabled = auditLogEnabled;
}
} finally {
mLock.unlock();
}
+
+ }
+
+ @GuardedBy("mLock")
+ private void startMonitorThreadLocked() {
+ mId = 0;
+ mPaused = false;
+ mMonitorThread = new Thread(this);
+ mMonitorThread.start();
+ SecurityLog.writeEvent(SecurityLog.TAG_LOGGING_STARTED);
+ Slog.i(TAG, "Security log monitor thread started");
+ }
+
+ @GuardedBy("mLock")
+ private void stopMonitorThreadLocked() {
+ mMonitorThread.interrupt();
+ try {
+ mMonitorThread.join(TimeUnit.SECONDS.toMillis(5));
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Interrupted while waiting for thread to stop", e);
+ }
+ mMonitorThread = null;
+ SecurityLog.writeEvent(SecurityLog.TAG_LOGGING_STOPPED);
+ }
+
+ @GuardedBy("mLock")
+ private void resetLegacyBufferLocked() {
+ mPendingLogs = new ArrayList<>();
+ mCriticalLevelLogged = false;
+ mAllowedToRetrieve = false;
+ mNextAllowedRetrievalTimeMillis = -1;
+ Slog.i(TAG, "Legacy buffer reset.");
+ }
+
+ @GuardedBy("mLock")
+ private void resetAuditBufferLocked() {
+ mAuditLogEventBuffer.clear();
+ mAuditLogCallbacks.clear();
}
/**
@@ -338,8 +423,7 @@ class SecurityLogMonitor implements Runnable {
*/
@GuardedBy("mLock")
private void mergeBatchLocked(final ArrayList<SecurityEvent> newLogs) {
- // Reserve capacity so that copying doesn't occur.
- mPendingLogs.ensureCapacity(mPendingLogs.size() + newLogs.size());
+ List<SecurityEvent> dedupedLogs = new ArrayList<>();
// Run through the first events of the batch to check if there is an overlap with previous
// batch and if so, skip overlapping events. Events are sorted by timestamp, so we can
// compare it in linear time by advancing two pointers, one for each batch.
@@ -358,8 +442,7 @@ class SecurityLogMonitor implements Runnable {
if (lastNanos > currentNanos) {
// New event older than the last we've seen so far, must be due to reordering.
if (DEBUG) Slog.d(TAG, "New event in the overlap: " + currentNanos);
- assignLogId(curEvent);
- mPendingLogs.add(curEvent);
+ dedupedLogs.add(curEvent);
curPos++;
} else if (lastNanos < currentNanos) {
if (DEBUG) Slog.d(TAG, "Event disappeared from the overlap: " + lastNanos);
@@ -371,8 +454,7 @@ class SecurityLogMonitor implements Runnable {
if (DEBUG) Slog.d(TAG, "Skipped dup event with timestamp: " + lastNanos);
} else {
// Wow, what a coincidence, or probably the clock is too coarse.
- assignLogId(curEvent);
- mPendingLogs.add(curEvent);
+ dedupedLogs.add(curEvent);
if (DEBUG) Slog.d(TAG, "Event timestamp collision: " + lastNanos);
}
lastPos++;
@@ -380,12 +462,24 @@ class SecurityLogMonitor implements Runnable {
}
}
// Assign an id to the new logs, after the overlap with mLastEvents.
- List<SecurityEvent> idLogs = newLogs.subList(curPos, newLogs.size());
- for (SecurityEvent event : idLogs) {
+ dedupedLogs.addAll(newLogs.subList(curPos, newLogs.size()));
+ for (SecurityEvent event : dedupedLogs) {
assignLogId(event);
}
+
+ if (!Flags.securityLogV2Enabled() || mLegacyLogEnabled) {
+ addToLegacyBufferLocked(dedupedLogs);
+ }
+
+ if (Flags.securityLogV2Enabled() && mAuditLogEnabled) {
+ addAuditLogEventsLocked(dedupedLogs);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void addToLegacyBufferLocked(List<SecurityEvent> dedupedLogs) {
// Save the rest of the new batch.
- mPendingLogs.addAll(idLogs);
+ mPendingLogs.addAll(dedupedLogs);
checkCriticalLevel();
@@ -453,7 +547,10 @@ class SecurityLogMonitor implements Runnable {
saveLastEvents(newLogs);
newLogs.clear();
- notifyDeviceOwnerOrProfileOwnerIfNeeded(force);
+
+ if (!Flags.securityLogV2Enabled() || mLegacyLogEnabled) {
+ notifyDeviceOwnerOrProfileOwnerIfNeeded(force);
+ }
} catch (IOException e) {
Log.e(TAG, "Failed to read security log", e);
} catch (InterruptedException e) {
@@ -532,4 +629,117 @@ class SecurityLogMonitor implements Runnable {
return 0;
}
}
+
+ public void setAuditLogEventsCallback(int uid, IAuditLogEventsCallback callback) {
+ mLock.lock();
+ try {
+ if (callback == null) {
+ mAuditLogCallbacks.remove(uid);
+ Slogf.i(TAG, "Cleared audit log callback for UID %d", uid);
+ return;
+ }
+ // Create a copy while holding the lock, so that that new events are not added
+ // resulting in duplicates.
+ final List<SecurityEvent> events = new ArrayList<>(mAuditLogEventBuffer);
+ scheduleSendAuditLogs(uid, callback, events);
+ mAuditLogCallbacks.append(uid, callback);
+ } finally {
+ mLock.unlock();
+ }
+ Slogf.i(TAG, "Set audit log callback for UID %d", uid);
+ }
+
+ @GuardedBy("mLock")
+ private void addAuditLogEventsLocked(List<SecurityEvent> events) {
+ if (mPaused) {
+ // TODO: maybe we need to stash the logs in some temp buffer wile paused so that
+ // they can be accessed after affiliation is fixed.
+ return;
+ }
+ if (!events.isEmpty()) {
+ for (int i = 0; i < mAuditLogCallbacks.size(); i++) {
+ final int uid = mAuditLogCallbacks.keyAt(i);
+ scheduleSendAuditLogs(uid, mAuditLogCallbacks.valueAt(i), events);
+ }
+ }
+ if (DEBUG) {
+ Slogf.d(TAG, "Adding audit %d events to % already present in the buffer",
+ events.size(), mAuditLogEventBuffer.size());
+ }
+ mAuditLogEventBuffer.addAll(events);
+ trimAuditLogBufferLocked();
+ if (DEBUG) {
+ Slogf.d(TAG, "Audit event buffer size after trimming: %d",
+ mAuditLogEventBuffer.size());
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void trimAuditLogBufferLocked() {
+ long nowNanos = TimeUnit.MILLISECONDS.toNanos(System.currentTimeMillis());
+
+ final Iterator<SecurityEvent> iterator = mAuditLogEventBuffer.iterator();
+ while (iterator.hasNext()) {
+ final SecurityEvent event = iterator.next();
+ if (mAuditLogEventBuffer.size() <= MAX_AUDIT_LOG_EVENTS
+ && nowNanos - event.getTimeNanos() <= MAX_AUDIT_LOG_EVENT_AGE_NS) {
+ break;
+ }
+
+ iterator.remove();
+ }
+ }
+
+ private void scheduleSendAuditLogs(
+ int uid, IAuditLogEventsCallback callback, List<SecurityEvent> events) {
+ if (DEBUG) {
+ Slogf.d(TAG, "Scheduling to send %d audit log events to UID %d", events.size(), uid);
+ }
+ mHandler.post(() -> sendAuditLogs(uid, callback, events));
+ }
+
+ private void sendAuditLogs(
+ int uid, IAuditLogEventsCallback callback, List<SecurityEvent> events) {
+ try {
+ final int size = events.size();
+ if (DEBUG) {
+ Slogf.d(TAG, "Sending %d audit log events to UID %d", size, uid);
+ }
+ callback.onNewAuditLogEvents(events);
+ if (DEBUG) {
+ Slogf.d(TAG, "Sent %d audit log events to UID %d", size, uid);
+ }
+ } catch (RemoteException e) {
+ Slogf.e(TAG, e, "Failed to invoke audit log callback for UID %d", uid);
+ removeAuditLogEventsCallbackIfDead(uid, callback);
+ }
+ }
+
+ private void removeAuditLogEventsCallbackIfDead(int uid, IAuditLogEventsCallback callback) {
+ final IBinder binder = callback.asBinder();
+ if (binder.isBinderAlive()) {
+ Slog.i(TAG, "Callback binder is still alive, not removing.");
+ return;
+ }
+
+ mLock.lock();
+ try {
+ int index = mAuditLogCallbacks.indexOfKey(uid);
+ if (index < 0) {
+ Slogf.i(TAG, "Callback not registered for UID %d, nothing to remove", uid);
+ return;
+ }
+
+ final IBinder storedBinder = mAuditLogCallbacks.valueAt(index).asBinder();
+ if (!storedBinder.equals(binder)) {
+ Slogf.i(TAG, "Callback is already replaced for UID %d, not removing", uid);
+ return;
+ }
+
+ Slogf.i(TAG, "Removing callback for UID %d", uid);
+ mAuditLogCallbacks.removeAt(index);
+ } finally {
+ mLock.unlock();
+ }
+ }
}
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java b/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
index bf2619b52275..42e41d59cfeb 100644
--- a/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
@@ -18,8 +18,8 @@ package com.android.server.policy;
import static android.hardware.SensorManager.SENSOR_DELAY_FASTEST;
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
-import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
-import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
+import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE_IDENTIFIER;
+import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE_IDENTIFIER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.TYPE_EXTERNAL;
@@ -478,7 +478,8 @@ public final class FoldableDeviceStateProvider implements DeviceStateProvider,
}
public static DeviceStateConfiguration createConfig(
- @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier,
+ @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to =
+ MAXIMUM_DEVICE_STATE_IDENTIFIER) int identifier,
@NonNull String name,
@DeviceState.DeviceStateFlags int flags,
@NonNull Predicate<FoldableDeviceStateProvider> activeStatePredicate
@@ -488,7 +489,8 @@ public final class FoldableDeviceStateProvider implements DeviceStateProvider,
}
public static DeviceStateConfiguration createConfig(
- @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier,
+ @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to =
+ MAXIMUM_DEVICE_STATE_IDENTIFIER) int identifier,
@NonNull String name,
@NonNull Predicate<FoldableDeviceStateProvider> activeStatePredicate
) {
@@ -498,7 +500,8 @@ public final class FoldableDeviceStateProvider implements DeviceStateProvider,
/** Create a configuration with availability predicate **/
public static DeviceStateConfiguration createConfig(
- @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier,
+ @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to =
+ MAXIMUM_DEVICE_STATE_IDENTIFIER) int identifier,
@NonNull String name,
@DeviceState.DeviceStateFlags int flags,
@NonNull Predicate<FoldableDeviceStateProvider> activeStatePredicate,
@@ -543,7 +546,8 @@ public final class FoldableDeviceStateProvider implements DeviceStateProvider,
* @return device state configuration
*/
public static DeviceStateConfiguration createTentModeClosedState(
- @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier,
+ @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to =
+ MAXIMUM_DEVICE_STATE_IDENTIFIER) int identifier,
@NonNull String name,
@DeviceState.DeviceStateFlags int flags,
int minClosedAngleDegrees,
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 1d89d1722f74..e19f08cb04a1 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -107,6 +107,7 @@ import com.android.internal.util.EmergencyAffordanceManager;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.widget.ILockSettings;
import com.android.internal.widget.LockSettingsInternal;
+import com.android.server.adaptiveauth.AdaptiveAuthService;
import com.android.server.am.ActivityManagerService;
import com.android.server.appbinding.AppBindingService;
import com.android.server.appop.AppOpMigrationHelper;
@@ -1485,7 +1486,6 @@ public final class SystemServer implements Dumpable {
VcnManagementService vcnManagement = null;
NetworkPolicyManagerService networkPolicy = null;
WindowManagerService wm = null;
- SerialService serial = null;
NetworkTimeUpdateService networkTimeUpdater = null;
InputManagerService inputManager = null;
TelephonyRegistry telephonyRegistry = null;
@@ -2361,13 +2361,7 @@ public final class SystemServer implements Dumpable {
if (!isWatch) {
t.traceBegin("StartSerialService");
- try {
- // Serial port support
- serial = new SerialService(context);
- ServiceManager.addService(Context.SERIAL_SERVICE, serial);
- } catch (Throwable e) {
- Slog.e(TAG, "Failure starting SerialService", e);
- }
+ mSystemServiceManager.startService(SerialService.Lifecycle.class);
t.traceEnd();
}
@@ -2615,6 +2609,12 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startService(AuthService.class);
t.traceEnd();
+ if (android.adaptiveauth.Flags.enableAdaptiveAuth()) {
+ t.traceBegin("StartAdaptiveAuthService");
+ mSystemServiceManager.startService(AdaptiveAuthService.class);
+ t.traceEnd();
+ }
+
if (!isWatch) {
// We don't run this on watches as there are no plans to use the data logged
// on watch devices.
@@ -3014,7 +3014,7 @@ public final class SystemServer implements Dumpable {
t.traceEnd();
}
- if (com.android.server.notification.Flags.sensitiveNotificationAppProtection()
+ if (android.permission.flags.Flags.sensitiveNotificationAppProtection()
|| android.view.flags.Flags.sensitiveContentAppProtection()) {
t.traceBegin("StartSensitiveContentProtectionManager");
mSystemServiceManager.startService(SensitiveContentProtectionManagerService.class);
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index a212812b0768..c16c61271280 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -1012,7 +1012,11 @@ public class MidiService extends IMidiManager.Stub {
}
}
- if (user.getUserIdentifier() == mUserManager.getMainUser().getIdentifier()) {
+ // Allow only the main user to create BluetoothMidiService.
+ // If there is no main user, allow all users to create it.
+ UserHandle mainUser = mUserManager.getMainUser();
+ if ((mainUser == null)
+ || (user.getUserIdentifier() == mainUser.getIdentifier())) {
PackageInfo info;
try {
info = mPackageManager.getPackageInfoAsUser(
diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
index 558827631dfe..4b086b3aca17 100644
--- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
@@ -46,6 +46,7 @@ import com.android.server.pm.KnownPackages
import com.android.server.pm.parsing.PackageInfoUtils
import com.android.server.pm.pkg.AndroidPackage
import com.android.server.pm.pkg.PackageState
+import libcore.util.EmptyArray
class AppIdPermissionPolicy : SchemePolicy() {
private val persistence = AppIdPermissionPersistence()
@@ -73,40 +74,42 @@ class AppIdPermissionPolicy : SchemePolicy() {
}
override fun MutateStateScope.onInitialized() {
- newState.externalState.configPermissions.forEach { (permissionName, permissionEntry) ->
- val oldPermission = newState.systemState.permissions[permissionName]
- val newPermission =
- if (oldPermission != null) {
- if (permissionEntry.gids != null) {
- oldPermission.copy(
- gids = permissionEntry.gids,
- areGidsPerUser = permissionEntry.perUser
- )
- } else {
- return@forEach
- }
- } else {
- @Suppress("DEPRECATION")
- val permissionInfo =
- PermissionInfo().apply {
- name = permissionName
- packageName = PLATFORM_PACKAGE_NAME
- protectionLevel = PermissionInfo.PROTECTION_SIGNATURE
+ if (!Flags.newPermissionGidEnabled()) {
+ newState.externalState.configPermissions.forEach { (permissionName, permissionEntry) ->
+ val oldPermission = newState.systemState.permissions[permissionName]
+ val newPermission =
+ if (oldPermission != null) {
+ if (permissionEntry.gids != null) {
+ oldPermission.copy(
+ gids = permissionEntry.gids,
+ areGidsPerUser = permissionEntry.perUser
+ )
+ } else {
+ return@forEach
}
- if (permissionEntry.gids != null) {
- Permission(
- permissionInfo,
- false,
- Permission.TYPE_CONFIG,
- 0,
- permissionEntry.gids,
- permissionEntry.perUser
- )
} else {
- Permission(permissionInfo, false, Permission.TYPE_CONFIG, 0)
+ @Suppress("DEPRECATION")
+ val permissionInfo =
+ PermissionInfo().apply {
+ name = permissionName
+ packageName = PLATFORM_PACKAGE_NAME
+ protectionLevel = PermissionInfo.PROTECTION_SIGNATURE
+ }
+ if (permissionEntry.gids != null) {
+ Permission(
+ permissionInfo,
+ false,
+ Permission.TYPE_CONFIG,
+ 0,
+ permissionEntry.gids,
+ permissionEntry.perUser
+ )
+ } else {
+ Permission(permissionInfo, false, Permission.TYPE_CONFIG, 0)
+ }
}
- }
- newState.mutateSystemState().mutatePermissions()[permissionName] = newPermission
+ newState.mutateSystemState().mutatePermissions()[permissionName] = newPermission
+ }
}
}
@@ -459,7 +462,7 @@ class AppIdPermissionPolicy : SchemePolicy() {
)
return@forEachIndexed
}
- val newPermission =
+ var newPermission =
if (oldPermission != null && newPackageName != oldPermission.packageName) {
val oldPackageName = oldPermission.packageName
// Only allow system apps to redefine non-system permissions.
@@ -582,6 +585,29 @@ class AppIdPermissionPolicy : SchemePolicy() {
)
}
}
+ if (Flags.newPermissionGidEnabled()) {
+ var gids = EmptyArray.INT
+ var areGidsPerUser = false
+ if (!parsedPermission.isTree && packageState.isSystem) {
+ newState.externalState.configPermissions[permissionName]?.let {
+ // PermissionEntry.gids may return null when parsing legacy config trying
+ // to work around an issue about upgrading from L platfrm. We can just
+ // ignore such entries now.
+ if (it.gids != null) {
+ gids = it.gids
+ areGidsPerUser = it.perUser
+ }
+ }
+ }
+ newPermission = Permission(
+ newPermissionInfo,
+ true,
+ Permission.TYPE_MANIFEST,
+ packageState.appId,
+ gids,
+ areGidsPerUser
+ )
+ }
if (parsedPermission.isTree) {
newState.mutateSystemState().mutatePermissionTrees()[permissionName] = newPermission
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index 67f66de71d39..0704c8ffca25 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -466,7 +466,7 @@ class PermissionService(private val service: AccessCheckingService) :
return size
}
- override fun checkUidPermission(uid: Int, permissionName: String, deviceId: Int): Int {
+ override fun checkUidPermission(uid: Int, permissionName: String, deviceId: String): Int {
val userId = UserHandle.getUserId(uid)
if (!userManagerInternal.exists(userId)) {
return PackageManager.PERMISSION_DENIED
@@ -489,15 +489,9 @@ class PermissionService(private val service: AccessCheckingService) :
return PackageManager.PERMISSION_DENIED
}
- val persistentDeviceId = getPersistentDeviceId(deviceId)
- if (persistentDeviceId == null) {
- Slog.e(LOG_TAG, "Cannot find persistent device id for $deviceId.")
- return PackageManager.PERMISSION_DENIED
- }
-
val isPermissionGranted =
service.getState {
- isPermissionGranted(packageState, userId, permissionName, persistentDeviceId)
+ isPermissionGranted(packageState, userId, permissionName, deviceId)
}
return if (isPermissionGranted) {
PackageManager.PERMISSION_GRANTED
@@ -531,7 +525,7 @@ class PermissionService(private val service: AccessCheckingService) :
override fun checkPermission(
packageName: String,
permissionName: String,
- persistentDeviceId: String,
+ deviceId: String,
userId: Int
): Int {
if (!userManagerInternal.exists(userId)) {
@@ -545,9 +539,7 @@ class PermissionService(private val service: AccessCheckingService) :
?: return PackageManager.PERMISSION_DENIED
val isPermissionGranted =
- service.getState {
- isPermissionGranted(packageState, userId, permissionName, persistentDeviceId)
- }
+ service.getState { isPermissionGranted(packageState, userId, permissionName, deviceId) }
return if (isPermissionGranted) {
PackageManager.PERMISSION_GRANTED
} else {
@@ -565,21 +557,13 @@ class PermissionService(private val service: AccessCheckingService) :
packageState: PackageState,
userId: Int,
permissionName: String,
- persistentDeviceId: String
+ deviceId: String
): Boolean {
val appId = packageState.appId
// Note that instant apps can't have shared UIDs, so we only need to check the current
// package state.
val isInstantApp = packageState.getUserStateOrDefault(userId).isInstantApp
- if (
- isSinglePermissionGranted(
- appId,
- userId,
- isInstantApp,
- permissionName,
- persistentDeviceId
- )
- ) {
+ if (isSinglePermissionGranted(appId, userId, isInstantApp, permissionName, deviceId)) {
return true
}
@@ -591,7 +575,7 @@ class PermissionService(private val service: AccessCheckingService) :
userId,
isInstantApp,
fullerPermissionName,
- persistentDeviceId
+ deviceId
)
) {
return true
@@ -606,9 +590,9 @@ class PermissionService(private val service: AccessCheckingService) :
userId: Int,
isInstantApp: Boolean,
permissionName: String,
- persistentDeviceId: String,
+ deviceId: String,
): Boolean {
- val flags = getPermissionFlagsWithPolicy(appId, userId, permissionName, persistentDeviceId)
+ val flags = getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId)
if (!PermissionFlags.isPermissionGranted(flags)) {
return false
}
@@ -689,22 +673,16 @@ class PermissionService(private val service: AccessCheckingService) :
override fun grantRuntimePermission(
packageName: String,
permissionName: String,
- persistentDeviceId: String,
+ deviceId: String,
userId: Int
) {
- setRuntimePermissionGranted(
- packageName,
- userId,
- permissionName,
- persistentDeviceId,
- isGranted = true
- )
+ setRuntimePermissionGranted(packageName, userId, permissionName, deviceId, isGranted = true)
}
override fun revokeRuntimePermission(
packageName: String,
permissionName: String,
- persistentDeviceId: String,
+ deviceId: String,
userId: Int,
reason: String?
) {
@@ -712,7 +690,7 @@ class PermissionService(private val service: AccessCheckingService) :
packageName,
userId,
permissionName,
- persistentDeviceId,
+ deviceId,
isGranted = false,
revokeReason = reason
)
@@ -740,7 +718,7 @@ class PermissionService(private val service: AccessCheckingService) :
packageName: String,
userId: Int,
permissionName: String,
- persistentDeviceId: String,
+ deviceId: String,
isGranted: Boolean,
skipKillUid: Boolean = false,
revokeReason: String? = null
@@ -765,7 +743,7 @@ class PermissionService(private val service: AccessCheckingService) :
(if (isGranted) "" else "skipKillUid = $skipKillUid, reason = $revokeReason") +
", userId = $userId," +
" callingUid = $callingUidName ($callingUid))," +
- " persistentDeviceId = $persistentDeviceId",
+ " deviceId = $deviceId",
RuntimeException()
)
}
@@ -835,7 +813,7 @@ class PermissionService(private val service: AccessCheckingService) :
packageState,
userId,
permissionName,
- persistentDeviceId,
+ deviceId,
isGranted,
canManageRolePermission,
overridePolicyFixed,
@@ -923,7 +901,7 @@ class PermissionService(private val service: AccessCheckingService) :
packageState: PackageState,
userId: Int,
permissionName: String,
- persistentDeviceId: String,
+ deviceId: String,
isGranted: Boolean,
canManageRolePermission: Boolean,
overridePolicyFixed: Boolean,
@@ -982,8 +960,7 @@ class PermissionService(private val service: AccessCheckingService) :
}
val appId = packageState.appId
- val oldFlags =
- getPermissionFlagsWithPolicy(appId, userId, permissionName, persistentDeviceId)
+ val oldFlags = getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId)
if (permissionName !in androidPackage.requestedPermissions && oldFlags == 0) {
if (reportError) {
@@ -1055,7 +1032,7 @@ class PermissionService(private val service: AccessCheckingService) :
return
}
- setPermissionFlagsWithPolicy(appId, userId, permissionName, persistentDeviceId, newFlags)
+ setPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId, newFlags)
if (permission.isRuntime) {
val action =
@@ -1089,7 +1066,7 @@ class PermissionService(private val service: AccessCheckingService) :
override fun getPermissionFlags(
packageName: String,
permissionName: String,
- persistentDeviceId: String,
+ deviceId: String,
userId: Int,
): Int {
if (!userManagerInternal.exists(userId)) {
@@ -1125,12 +1102,7 @@ class PermissionService(private val service: AccessCheckingService) :
}
val flags =
- getPermissionFlagsWithPolicy(
- packageState.appId,
- userId,
- permissionName,
- persistentDeviceId
- )
+ getPermissionFlagsWithPolicy(packageState.appId, userId, permissionName, deviceId)
return PermissionFlags.toApiFlags(flags)
}
@@ -1138,7 +1110,7 @@ class PermissionService(private val service: AccessCheckingService) :
override fun getAllPermissionStates(
packageName: String,
- persistentDeviceId: String,
+ deviceId: String,
userId: Int
): Map<String, PermissionState> {
if (!userManagerInternal.exists(userId)) {
@@ -1165,14 +1137,15 @@ class PermissionService(private val service: AccessCheckingService) :
val permissionFlagsMap =
service.getState {
- if (persistentDeviceId == VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT) {
+ if (deviceId == VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT) {
with(policy) { getAllPermissionFlags(packageState.appId, userId) }
} else {
with(devicePolicy) {
- getAllPermissionFlags(packageState.appId, persistentDeviceId, userId)
+ getAllPermissionFlags(packageState.appId, deviceId, userId)
}
}
- } ?: return emptyMap()
+ }
+ ?: return emptyMap()
val permissionStates = ArrayMap<String, PermissionState>()
permissionFlagsMap.forEachIndexed { _, permissionName, flags ->
@@ -1186,7 +1159,7 @@ class PermissionService(private val service: AccessCheckingService) :
override fun isPermissionRevokedByPolicy(
packageName: String,
permissionName: String,
- deviceId: Int,
+ deviceId: String,
userId: Int
): Boolean {
if (!userManagerInternal.exists(userId)) {
@@ -1207,24 +1180,13 @@ class PermissionService(private val service: AccessCheckingService) :
}
?: return false
- val persistentDeviceId = getPersistentDeviceId(deviceId)
- if (persistentDeviceId == null) {
- Slog.w(LOG_TAG, "Cannot find persistent device Id for $deviceId")
- return false
- }
-
service.getState {
- if (isPermissionGranted(packageState, userId, permissionName, persistentDeviceId)) {
+ if (isPermissionGranted(packageState, userId, permissionName, deviceId)) {
return false
}
val flags =
- getPermissionFlagsWithPolicy(
- packageState.appId,
- userId,
- permissionName,
- persistentDeviceId
- )
+ getPermissionFlagsWithPolicy(packageState.appId, userId, permissionName, deviceId)
return flags.hasBits(PermissionFlags.POLICY_FIXED)
}
@@ -1248,7 +1210,7 @@ class PermissionService(private val service: AccessCheckingService) :
override fun shouldShowRequestPermissionRationale(
packageName: String,
permissionName: String,
- deviceId: Int,
+ deviceId: String,
userId: Int,
): Boolean {
if (!userManagerInternal.exists(userId)) {
@@ -1274,19 +1236,13 @@ class PermissionService(private val service: AccessCheckingService) :
return false
}
- val persistentDeviceId = getPersistentDeviceId(deviceId)
- if (persistentDeviceId == null) {
- Slog.w(LOG_TAG, "Cannot find persistent device Id for $deviceId")
- return false
- }
-
val flags: Int
service.getState {
- if (isPermissionGranted(packageState, userId, permissionName, persistentDeviceId)) {
+ if (isPermissionGranted(packageState, userId, permissionName, deviceId)) {
return false
}
- flags = getPermissionFlagsWithPolicy(appId, userId, permissionName, persistentDeviceId)
+ flags = getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId)
}
if (flags.hasAnyBit(UNREQUESTABLE_MASK)) {
return false
@@ -1325,7 +1281,7 @@ class PermissionService(private val service: AccessCheckingService) :
flagMask: Int,
flagValues: Int,
enforceAdjustPolicyPermission: Boolean,
- persistentDeviceId: String,
+ deviceId: String,
userId: Int
) {
val callingUid = Binder.getCallingUid()
@@ -1351,7 +1307,7 @@ class PermissionService(private val service: AccessCheckingService) :
"updatePermissionFlags(packageName = $packageName," +
" permissionName = $permissionName, flagMask = $flagMaskString," +
" flagValues = $flagValuesString, userId = $userId," +
- " persistentDeviceId = $persistentDeviceId," +
+ " deviceId = $deviceId," +
" callingUid = $callingUidName ($callingUid))",
RuntimeException()
)
@@ -1441,7 +1397,7 @@ class PermissionService(private val service: AccessCheckingService) :
appId,
userId,
permissionName,
- persistentDeviceId,
+ deviceId,
flagMask,
flagValues,
canUpdateSystemFlags,
@@ -1527,7 +1483,7 @@ class PermissionService(private val service: AccessCheckingService) :
appId: Int,
userId: Int,
permissionName: String,
- persistentDeviceId: String,
+ deviceId: String,
flagMask: Int,
flagValues: Int,
canUpdateSystemFlags: Boolean,
@@ -1561,8 +1517,7 @@ class PermissionService(private val service: AccessCheckingService) :
return
}
- val oldFlags =
- getPermissionFlagsWithPolicy(appId, userId, permissionName, persistentDeviceId)
+ val oldFlags = getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId)
if (!isPermissionRequested && oldFlags == 0) {
Slog.w(
LOG_TAG,
@@ -1573,7 +1528,7 @@ class PermissionService(private val service: AccessCheckingService) :
}
val newFlags = PermissionFlags.updateFlags(permission, oldFlags, flagMask, flagValues)
- setPermissionFlagsWithPolicy(appId, userId, permissionName, persistentDeviceId, newFlags)
+ setPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId, newFlags)
}
override fun getAllowlistedRestrictedPermissions(
@@ -1648,11 +1603,11 @@ class PermissionService(private val service: AccessCheckingService) :
appId: Int,
userId: Int,
permissionName: String,
- persistentDeviceId: String,
+ deviceId: String,
): Int {
return if (
!Flags.deviceAwarePermissionApisEnabled() ||
- persistentDeviceId == VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT
+ deviceId == VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT
) {
with(policy) { getPermissionFlags(appId, userId, permissionName) }
} else {
@@ -1664,9 +1619,7 @@ class PermissionService(private val service: AccessCheckingService) :
)
return with(policy) { getPermissionFlags(appId, userId, permissionName) }
}
- with(devicePolicy) {
- getPermissionFlags(appId, persistentDeviceId, userId, permissionName)
- }
+ with(devicePolicy) { getPermissionFlags(appId, deviceId, userId, permissionName) }
}
}
@@ -1674,12 +1627,12 @@ class PermissionService(private val service: AccessCheckingService) :
appId: Int,
userId: Int,
permissionName: String,
- persistentDeviceId: String,
+ deviceId: String,
flags: Int
): Boolean {
return if (
!Flags.deviceAwarePermissionApisEnabled() ||
- persistentDeviceId == VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT
+ deviceId == VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT
) {
with(policy) { setPermissionFlags(appId, userId, permissionName, flags) }
} else {
@@ -1693,23 +1646,11 @@ class PermissionService(private val service: AccessCheckingService) :
}
with(devicePolicy) {
- setPermissionFlags(appId, persistentDeviceId, userId, permissionName, flags)
+ setPermissionFlags(appId, deviceId, userId, permissionName, flags)
}
}
}
- private fun getPersistentDeviceId(deviceId: Int): String? {
- if (deviceId == Context.DEVICE_ID_DEFAULT) {
- return VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT
- }
-
- if (virtualDeviceManagerInternal == null) {
- virtualDeviceManagerInternal =
- LocalServices.getService(VirtualDeviceManagerInternal::class.java)
- }
- return virtualDeviceManagerInternal?.getPersistentIdForDevice(deviceId)
- }
-
/**
* This method does not enforce checks on the caller, should only be called after required
* checks.
@@ -2270,9 +2211,9 @@ class PermissionService(private val service: AccessCheckingService) :
userState.appIdDevicePermissionFlags[appId]?.forEachIndexed {
_,
- persistentDeviceId,
+ deviceId,
devicePermissionFlags ->
- println("Permissions (Device $persistentDeviceId):")
+ println("Permissions (Device $deviceId):")
withIndent {
devicePermissionFlags.forEachIndexed { _, permissionName, flags ->
val isGranted = PermissionFlags.isPermissionGranted(flags)
@@ -2415,9 +2356,8 @@ class PermissionService(private val service: AccessCheckingService) :
with(devicePolicy) { trimDevicePermissionStates(persistentDeviceIds) }
}
}
- virtualDeviceManagerInternal?.registerPersistentDeviceIdRemovedListener { persistentDeviceId
- ->
- service.mutateState { with(devicePolicy) { onDeviceIdRemoved(persistentDeviceId) } }
+ virtualDeviceManagerInternal?.registerPersistentDeviceIdRemovedListener { deviceId ->
+ service.mutateState { with(devicePolicy) { onDeviceIdRemoved(deviceId) } }
}
permissionControllerManager =
@@ -2764,7 +2704,7 @@ class PermissionService(private val service: AccessCheckingService) :
override fun onDevicePermissionFlagsChanged(
appId: Int,
userId: Int,
- persistentDeviceId: String,
+ deviceId: String,
permissionName: String,
oldFlags: Int,
newFlags: Int
@@ -2787,8 +2727,7 @@ class PermissionService(private val service: AccessCheckingService) :
permissionName in NOTIFICATIONS_PERMISSIONS &&
runtimePermissionRevokedUids.get(uid, true)
}
- runtimePermissionChangedUidDevices.getOrPut(uid) { mutableSetOf() } +=
- persistentDeviceId
+ runtimePermissionChangedUidDevices.getOrPut(uid) { mutableSetOf() } += deviceId
}
if (permission.hasGids && !wasPermissionGranted && isPermissionGranted) {
@@ -2803,8 +2742,8 @@ class PermissionService(private val service: AccessCheckingService) :
}
runtimePermissionChangedUidDevices.forEachIndexed { _, uid, persistentDeviceIds ->
- persistentDeviceIds.forEach { persistentDeviceId ->
- onPermissionsChangeListeners.onPermissionsChanged(uid, persistentDeviceId)
+ persistentDeviceIds.forEach { deviceId ->
+ onPermissionsChangeListeners.onPermissionsChanged(uid, deviceId)
}
}
runtimePermissionChangedUidDevices.clear()
@@ -2844,8 +2783,11 @@ class PermissionService(private val service: AccessCheckingService) :
private fun isAppBackupAndRestoreRunning(uid: Int): Boolean {
if (
- checkUidPermission(uid, Manifest.permission.BACKUP, Context.DEVICE_ID_DEFAULT) !=
- PackageManager.PERMISSION_GRANTED
+ checkUidPermission(
+ uid,
+ Manifest.permission.BACKUP,
+ VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT
+ ) != PackageManager.PERMISSION_GRANTED
) {
return false
}
@@ -2879,16 +2821,16 @@ class PermissionService(private val service: AccessCheckingService) :
when (msg.what) {
MSG_ON_PERMISSIONS_CHANGED -> {
val uid = msg.arg1
- val persistentDeviceId = msg.obj as String
- handleOnPermissionsChanged(uid, persistentDeviceId)
+ val deviceId = msg.obj as String
+ handleOnPermissionsChanged(uid, deviceId)
}
}
}
- private fun handleOnPermissionsChanged(uid: Int, persistentDeviceId: String) {
+ private fun handleOnPermissionsChanged(uid: Int, deviceId: String) {
listeners.broadcast { listener ->
try {
- listener.onPermissionsChanged(uid, persistentDeviceId)
+ listener.onPermissionsChanged(uid, deviceId)
} catch (e: RemoteException) {
Slog.e(LOG_TAG, "Error when calling OnPermissionsChangeListener", e)
}
@@ -2903,9 +2845,9 @@ class PermissionService(private val service: AccessCheckingService) :
listeners.unregister(listener)
}
- fun onPermissionsChanged(uid: Int, persistentDeviceId: String) {
+ fun onPermissionsChanged(uid: Int, deviceId: String) {
if (listeners.registeredCallbackCount > 0) {
- obtainMessage(MSG_ON_PERMISSIONS_CHANGED, uid, 0, persistentDeviceId).sendToTarget()
+ obtainMessage(MSG_ON_PERMISSIONS_CHANGED, uid, 0, deviceId).sendToTarget()
}
}
diff --git a/services/tests/InputMethodSystemServerTests/Android.bp b/services/tests/InputMethodSystemServerTests/Android.bp
index afd6dbd7f6a7..3bce9b54e320 100644
--- a/services/tests/InputMethodSystemServerTests/Android.bp
+++ b/services/tests/InputMethodSystemServerTests/Android.bp
@@ -69,7 +69,7 @@ android_test {
}
android_ravenwood_test {
- name: "FrameworksInputMethodSystemServerTests_host",
+ name: "FrameworksInputMethodSystemServerTestsRavenwood",
static_libs: [
"androidx.annotation_annotation",
"androidx.test.rules",
@@ -85,7 +85,6 @@ android_ravenwood_test {
srcs: [
"src/com/android/server/inputmethod/**/ClientControllerTest.java",
],
- sdk_version: "test_current",
auto_gen_config: true,
}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ClientControllerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ClientControllerTest.java
index dc9631a8f2e2..9e3d9ec6b9b6 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ClientControllerTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ClientControllerTest.java
@@ -32,10 +32,8 @@ import android.content.pm.PackageManagerInternal;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.ravenwood.RavenwoodRule;
import android.view.Display;
-import android.view.inputmethod.InputBinding;
import com.android.internal.inputmethod.IInputMethodClient;
import com.android.internal.inputmethod.IRemoteInputConnection;
@@ -53,7 +51,7 @@ import java.util.concurrent.TimeUnit;
public final class ClientControllerTest {
private static final int ANY_DISPLAY_ID = Display.DEFAULT_DISPLAY;
private static final int ANY_CALLER_UID = 1;
- private static final int ANY_CALLER_PID = 1;
+ private static final int ANY_CALLER_PID = 2;
private static final String SOME_PACKAGE_NAME = "some.package";
@Rule
@@ -82,13 +80,16 @@ public final class ClientControllerTest {
mController = new ClientController(mMockPackageManagerInternal);
}
+ // TODO(b/322895594): No need to directly invoke create$ravenwood once b/322895594 is fixed.
+ private IInputMethodClientInvoker createInvoker(IInputMethodClient client, Handler handler) {
+ return RavenwoodRule.isOnRavenwood()
+ ? IInputMethodClientInvoker.create$ravenwood(client, handler) :
+ IInputMethodClientInvoker.create(client, handler);
+ }
+
@Test
- // TODO(b/314150112): Enable host side mode for this test once Ravenwood is enabled for
- // inputmethod server classes.
- @IgnoreUnderRavenwood(blockedBy = {InputBinding.class, IInputMethodClientInvoker.class})
public void testAddClient_cannotAddTheSameClientTwice() {
- var invoker = IInputMethodClientInvoker.create(mClient, mHandler);
-
+ final var invoker = createInvoker(mClient, mHandler);
synchronized (ImfLock.class) {
mController.addClient(invoker, mConnection, ANY_DISPLAY_ID, ANY_CALLER_UID,
ANY_CALLER_PID);
@@ -101,18 +102,17 @@ public final class ClientControllerTest {
}
});
assertThat(thrown.getMessage()).isEqualTo(
- "uid=1/pid=1/displayId=0 is already registered");
+ "uid=" + ANY_CALLER_UID + "/pid=" + ANY_CALLER_PID
+ + "/displayId=0 is already registered");
}
}
@Test
- // TODO(b/314150112): Enable host side mode for this test once Ravenwood is enabled for
- // inputmethod server classes.
- @IgnoreUnderRavenwood(blockedBy = {InputBinding.class, IInputMethodClientInvoker.class})
public void testAddClient() throws Exception {
+ final var invoker = createInvoker(mClient, mHandler);
synchronized (ImfLock.class) {
- var invoker = IInputMethodClientInvoker.create(mClient, mHandler);
- var added = mController.addClient(invoker, mConnection, ANY_DISPLAY_ID, ANY_CALLER_UID,
+ final var added = mController.addClient(invoker, mConnection, ANY_DISPLAY_ID,
+ ANY_CALLER_UID,
ANY_CALLER_PID);
verify(invoker.asBinder()).linkToDeath(any(IBinder.DeathRecipient.class), eq(0));
@@ -121,16 +121,12 @@ public final class ClientControllerTest {
}
@Test
- // TODO(b/314150112): Enable host side mode for this test once Ravenwood is enabled for
- // inputmethod server classes.
- @IgnoreUnderRavenwood(blockedBy = {InputBinding.class, IInputMethodClientInvoker.class})
public void testRemoveClient() {
- var callback = new TestClientControllerCallback();
+ final var invoker = createInvoker(mClient, mHandler);
+ final var callback = new TestClientControllerCallback();
ClientState added;
synchronized (ImfLock.class) {
mController.addClientControllerCallback(callback);
-
- var invoker = IInputMethodClientInvoker.create(mClient, mHandler);
added = mController.addClient(invoker, mConnection, ANY_DISPLAY_ID, ANY_CALLER_UID,
ANY_CALLER_PID);
assertThat(mController.getClient(invoker.asBinder())).isSameInstanceAs(added);
@@ -138,21 +134,17 @@ public final class ClientControllerTest {
}
// Test callback
- var removed = callback.waitForRemovedClient(5, TimeUnit.SECONDS);
+ final var removed = callback.waitForRemovedClient(5, TimeUnit.SECONDS);
assertThat(removed).isSameInstanceAs(added);
}
@Test
- // TODO(b/314150112): Enable host side mode for this test once Ravenwood is enabled for
- // inputmethod server classes and updated to newer Mockito with static mock support (mock
- // InputMethodUtils#checkIfPackageBelongsToUid instead of PackageManagerInternal#isSameApp)
- @IgnoreUnderRavenwood(blockedBy = {InputMethodUtils.class})
public void testVerifyClientAndPackageMatch() {
+ final var invoker = createInvoker(mClient, mHandler);
when(mMockPackageManagerInternal.isSameApp(eq(SOME_PACKAGE_NAME), /* flags= */
anyLong(), eq(ANY_CALLER_UID), /* userId= */ anyInt())).thenReturn(true);
synchronized (ImfLock.class) {
- var invoker = IInputMethodClientInvoker.create(mClient, mHandler);
mController.addClient(invoker, mConnection, ANY_DISPLAY_ID, ANY_CALLER_UID,
ANY_CALLER_PID);
assertThat(
diff --git a/services/tests/PackageManagerServiceTests/apks/Android.bp b/services/tests/PackageManagerServiceTests/apks/Android.bp
index 6c918064dbff..55f2bc9f2828 100644
--- a/services/tests/PackageManagerServiceTests/apks/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/apks/install-split-base/Android.bp b/services/tests/PackageManagerServiceTests/apks/install-split-base/Android.bp
index 39992f600b96..2f5a57f96c1c 100644
--- a/services/tests/PackageManagerServiceTests/apks/install-split-base/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/install-split-base/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/apks/install-split-feature-a/Android.bp b/services/tests/PackageManagerServiceTests/apks/install-split-feature-a/Android.bp
index ca7295e48fbc..fee9b020a5c3 100644
--- a/services/tests/PackageManagerServiceTests/apks/install-split-feature-a/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/install-split-feature-a/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/apks/install/Android.bp b/services/tests/PackageManagerServiceTests/apks/install/Android.bp
index 12175fdb7327..b0a2adbc6d5c 100644
--- a/services/tests/PackageManagerServiceTests/apks/install/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/install/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/apks/install_bad_dex/Android.bp b/services/tests/PackageManagerServiceTests/apks/install_bad_dex/Android.bp
index ad7566810a62..891ed2968c0c 100644
--- a/services/tests/PackageManagerServiceTests/apks/install_bad_dex/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/install_bad_dex/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/apks/install_complete_package_info/Android.bp b/services/tests/PackageManagerServiceTests/apks/install_complete_package_info/Android.bp
index 98aa750231d7..4f03bb9f1d0f 100644
--- a/services/tests/PackageManagerServiceTests/apks/install_complete_package_info/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/install_complete_package_info/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/apks/install_decl_perm/Android.bp b/services/tests/PackageManagerServiceTests/apks/install_decl_perm/Android.bp
index ef65f5de6a1b..452a6ad4972a 100644
--- a/services/tests/PackageManagerServiceTests/apks/install_decl_perm/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/install_decl_perm/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/apks/install_intent_filters/Android.bp b/services/tests/PackageManagerServiceTests/apks/install_intent_filters/Android.bp
index 643824de785c..b05f0ecd23bf 100644
--- a/services/tests/PackageManagerServiceTests/apks/install_intent_filters/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/install_intent_filters/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/apks/install_loc_auto/Android.bp b/services/tests/PackageManagerServiceTests/apks/install_loc_auto/Android.bp
index 4e4ae526d0dc..1f1d901ef328 100644
--- a/services/tests/PackageManagerServiceTests/apks/install_loc_auto/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/install_loc_auto/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/apks/install_loc_internal/Android.bp b/services/tests/PackageManagerServiceTests/apks/install_loc_internal/Android.bp
index 39cdd5178a6d..b04bb509e30e 100644
--- a/services/tests/PackageManagerServiceTests/apks/install_loc_internal/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/install_loc_internal/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/apks/install_loc_sdcard/Android.bp b/services/tests/PackageManagerServiceTests/apks/install_loc_sdcard/Android.bp
index ed82793ff6e6..d0a5192a2a1d 100644
--- a/services/tests/PackageManagerServiceTests/apks/install_loc_sdcard/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/install_loc_sdcard/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/apks/install_loc_unspecified/Android.bp b/services/tests/PackageManagerServiceTests/apks/install_loc_unspecified/Android.bp
index fd15cb8e9f92..7f6b29e22ba2 100644
--- a/services/tests/PackageManagerServiceTests/apks/install_loc_unspecified/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/install_loc_unspecified/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/apks/install_target_sdk_22/Android.bp b/services/tests/PackageManagerServiceTests/apks/install_target_sdk_22/Android.bp
index 69b26cc30214..8159bd1ffa82 100644
--- a/services/tests/PackageManagerServiceTests/apks/install_target_sdk_22/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/install_target_sdk_22/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/apks/install_target_sdk_23/Android.bp b/services/tests/PackageManagerServiceTests/apks/install_target_sdk_23/Android.bp
index e3154dba785c..9ce980f04614 100644
--- a/services/tests/PackageManagerServiceTests/apks/install_target_sdk_23/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/install_target_sdk_23/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/apks/install_use_perm_good/Android.bp b/services/tests/PackageManagerServiceTests/apks/install_use_perm_good/Android.bp
index 959ffbcc48b1..e4a57287bbe0 100644
--- a/services/tests/PackageManagerServiceTests/apks/install_use_perm_good/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/install_use_perm_good/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/apks/install_uses_feature/Android.bp b/services/tests/PackageManagerServiceTests/apks/install_uses_feature/Android.bp
index fa25af4c5b30..eb2f52af4d11 100644
--- a/services/tests/PackageManagerServiceTests/apks/install_uses_feature/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/install_uses_feature/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/apks/install_uses_sdk/Android.bp b/services/tests/PackageManagerServiceTests/apks/install_uses_sdk/Android.bp
index 24e380ccd82e..46b4fc2d5a43 100644
--- a/services/tests/PackageManagerServiceTests/apks/install_uses_sdk/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/install_uses_sdk/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/apks/keyset/Android.bp b/services/tests/PackageManagerServiceTests/apks/keyset/Android.bp
index ce7919c9d0a8..d86b51d3464d 100644
--- a/services/tests/PackageManagerServiceTests/apks/keyset/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/keyset/Android.bp
@@ -1,5 +1,6 @@
//apks signed by keyset_A
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/host/Android.bp b/services/tests/PackageManagerServiceTests/host/Android.bp
index c617ec49ab32..6fd21f74cf8c 100644
--- a/services/tests/PackageManagerServiceTests/host/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp
index e70a734cf271..69d79c92a03a 100644
--- a/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/Apex/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/Apex/Android.bp
index aef365e9d5fc..fe78d7d7ec54 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/Apex/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/Apex/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_framework_android_packages",
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp
index cea9c599317d..73cec6c13d6d 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/Generic/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/Generic/Android.bp
index b826590b7440..08e41e72d105 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/Generic/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/Generic/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/Overlay/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/Overlay/Android.bp
index 92dcd348a8e2..2fe7f47287b6 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/Overlay/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/Overlay/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_framework_android_packages",
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/Android.bp
index ed5f2b51b9ed..e80b5b55d97a 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/OverlayTarget/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayTarget/Android.bp
index 2bb6b82a6bbc..a8b4dd1e0034 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/OverlayTarget/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayTarget/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_framework_android_packages",
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/Android.bp
index 93d70bb6cdba..c65a4ecec1f4 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
index cfe701f42065..d4b57f191ecd 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
@@ -273,7 +273,8 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag
AndroidPackage::hasRequestForegroundServiceExemption,
AndroidPackage::hasRequestRawExternalStorageAccess,
AndroidPackage::isUpdatableSystem,
- AndroidPackage::getEmergencyInstaller
+ AndroidPackage::getEmergencyInstaller,
+ AndroidPackage::isAllowCrossUidActivitySwitchFromBelow,
)
override fun extraParams() = listOf(
diff --git a/services/tests/displayservicetests/src/com/android/server/display/BrightnessThrottlerTest.java b/services/tests/displayservicetests/src/com/android/server/display/BrightnessThrottlerTest.java
index 8faaf5998d13..05c243fda2b8 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/BrightnessThrottlerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/BrightnessThrottlerTest.java
@@ -43,6 +43,7 @@ import com.android.internal.os.BackgroundThread;
import com.android.server.display.BrightnessThrottler.Injector;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel;
+import com.android.server.display.config.SensorData;
import com.android.server.display.mode.DisplayModeDirectorTest;
import org.junit.Before;
@@ -56,6 +57,7 @@ import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
+import java.util.Map;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -292,6 +294,53 @@ public class BrightnessThrottlerTest {
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason());
}
+
+ @Test
+ public void testThermalThrottlingWithDisplaySensor() throws Exception {
+ final ThrottlingLevel level =
+ new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.25f);
+ List<ThrottlingLevel> levels = new ArrayList<>(List.of(level));
+ final ThermalBrightnessThrottlingData data = ThermalBrightnessThrottlingData.create(levels);
+ final SensorData tempSensor = new SensorData("DISPLAY", "VIRTUAL-SKIN-DISPLAY");
+ final BrightnessThrottler throttler =
+ createThrottlerSupportedWithTempSensor(data, tempSensor);
+ assertTrue(throttler.deviceSupportsThrottling());
+
+ verify(mThermalServiceMock)
+ .registerThermalEventListenerWithType(
+ mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_DISPLAY));
+ final IThermalEventListener listener = mThermalEventListenerCaptor.getValue();
+
+ // Set VIRTUAL-SKIN-DISPLAY tatus too low to verify no throttling.
+ listener.notifyThrottling(getDisplayTempWithName(tempSensor.name, level.thermalStatus - 1));
+ mTestLooper.dispatchAll();
+ assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
+ assertFalse(throttler.isThrottled());
+ assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason());
+
+ // Verify when skin sensor throttled, no brightness throttling triggered.
+ listener.notifyThrottling(getSkinTemp(level.thermalStatus + 1));
+ mTestLooper.dispatchAll();
+ assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
+ assertFalse(throttler.isThrottled());
+ assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason());
+
+ // Verify when display sensor of another name throttled, no brightness throttling triggered.
+ listener.notifyThrottling(getDisplayTempWithName("ANOTHER-NAME", level.thermalStatus + 1));
+ mTestLooper.dispatchAll();
+ assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
+ assertFalse(throttler.isThrottled());
+ assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason());
+
+ // Verify when display sensor of current name throttled, brightness throttling triggered.
+ listener.notifyThrottling(getDisplayTempWithName(tempSensor.name, level.thermalStatus + 1));
+ mTestLooper.dispatchAll();
+ assertEquals(level.brightness, throttler.getBrightnessCap(), 0f);
+ assertTrue(throttler.isThrottled());
+ assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
+ throttler.getBrightnessMaxReason());
+ }
+
@Test public void testUpdateThermalThrottlingData() throws Exception {
// Initialise brightness throttling levels
// Ensure that they are overridden by setting the data through device config.
@@ -476,18 +525,30 @@ public class BrightnessThrottlerTest {
return new BrightnessThrottler(mInjectorMock, mHandler, mHandler,
/* throttlingChangeCallback= */ () -> {}, /* uniqueDisplayId= */ null,
/* thermalThrottlingDataId= */ null,
- /* thermalThrottlingDataMap= */ new HashMap<>(1));
+ /* thermalThrottlingDataMap= */ new HashMap<>(1),
+ /* tempSensor= */ null);
}
private BrightnessThrottler createThrottlerSupported(ThermalBrightnessThrottlingData data) {
+ SensorData tempSensor = SensorData.loadTempSensorUnspecifiedConfig();
+ return createThrottlerSupportedWithTempSensor(data, tempSensor);
+ }
+ private BrightnessThrottler createThrottlerSupportedWithTempSensor(
+ ThermalBrightnessThrottlingData data, SensorData tempSensor) {
assertNotNull(data);
- HashMap<String, ThermalBrightnessThrottlingData> throttlingDataMap = new HashMap<>(1);
+ Map<String, ThermalBrightnessThrottlingData> throttlingDataMap = new HashMap<>(1);
throttlingDataMap.put("default", data);
return new BrightnessThrottler(mInjectorMock, mHandler, BackgroundThread.getHandler(),
- () -> {}, "123", "default", throttlingDataMap);
+ () -> {}, "123", "default", throttlingDataMap, tempSensor);
}
private Temperature getSkinTemp(@ThrottlingStatus int status) {
return new Temperature(30.0f, Temperature.TYPE_SKIN, "test_skin_temp", status);
}
+
+ private Temperature getDisplayTempWithName(
+ String sensorName, @ThrottlingStatus int status) {
+ assertNotNull(sensorName);
+ return new Temperature(30.0f, Temperature.TYPE_DISPLAY, sensorName, status);
+ }
}
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 b29fc8828f58..2867041511b5 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -20,6 +20,7 @@ package com.android.server.display;
import static com.android.internal.display.BrightnessSynchronizer.brightnessIntToFloat;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
+import static com.android.server.display.config.SensorData.TEMPERATURE_TYPE_SKIN;
import static com.android.server.display.config.SensorData.SupportedMode;
import static com.android.server.display.utils.DeviceConfigParsingUtils.ambientBrightnessThresholdsIntToFloat;
import static com.android.server.display.utils.DeviceConfigParsingUtils.displayBrightnessThresholdsIntToFloat;
@@ -106,6 +107,7 @@ public final class DisplayDeviceConfigTest {
MockitoAnnotations.initMocks(this);
when(mContext.getResources()).thenReturn(mResources);
when(mFlags.areAutoBrightnessModesEnabled()).thenReturn(true);
+ when(mFlags.isSensorBasedBrightnessThrottlingEnabled()).thenReturn(true);
mockDeviceConfigs();
}
@@ -143,6 +145,8 @@ public final class DisplayDeviceConfigTest {
assertEquals("", mDisplayDeviceConfig.getAmbientLightSensor().name);
assertNull(mDisplayDeviceConfig.getProximitySensor().type);
assertNull(mDisplayDeviceConfig.getProximitySensor().name);
+ assertEquals(TEMPERATURE_TYPE_SKIN, mDisplayDeviceConfig.getTempSensor().type);
+ assertNull(mDisplayDeviceConfig.getTempSensor().name);
assertTrue(mDisplayDeviceConfig.isAutoBrightnessAvailable());
}
@@ -592,6 +596,13 @@ public final class DisplayDeviceConfigTest {
}
@Test
+ public void testTempSensorFromDisplayConfig() throws IOException {
+ setupDisplayDeviceConfigFromDisplayConfigFile();
+ assertEquals("DISPLAY", mDisplayDeviceConfig.getTempSensor().type);
+ assertEquals("VIRTUAL-SKIN-DISPLAY", mDisplayDeviceConfig.getTempSensor().name);
+ }
+
+ @Test
public void testBlockingZoneThresholdsFromDisplayConfig() throws IOException {
setupDisplayDeviceConfigFromDisplayConfigFile();
@@ -1356,6 +1367,10 @@ public final class DisplayDeviceConfigTest {
+ "<name>Test Binned Brightness Sensor</name>\n"
+ "</screenOffBrightnessSensor>\n"
+ proxSensor
+ + "<tempSensor>\n"
+ + "<type>DISPLAY</type>\n"
+ + "<name>VIRTUAL-SKIN-DISPLAY</name>\n"
+ + "</tempSensor>\n"
+ "<ambientBrightnessChangeThresholds>\n"
+ "<brighteningThresholds>\n"
+ "<minimum>10</minimum>\n"
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index 57b86324e171..807774f90655 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -725,8 +725,8 @@ public final class DisplayPowerControllerTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_FAST_HDR_TRANSITIONS)
public void testDisplayBrightnessHdr_SkipAnimationOnHdrAppearance() {
+ when(mDisplayManagerFlagsMock.isFastHdrTransitionsEnabled()).thenReturn(true);
Settings.System.putInt(mContext.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS_MODE,
Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
@@ -757,12 +757,12 @@ public final class DisplayPowerControllerTest {
advanceTime(1); // Run updatePowerState
verify(mHolder.animator).animateTo(eq(hdrBrightness), eq(sdrBrightness),
- eq(BRIGHTNESS_RAMP_RATE_MINIMUM), eq(false));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE), eq(false));
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_FAST_HDR_TRANSITIONS)
public void testDisplayBrightnessHdr_SkipAnimationOnHdrRemoval() {
+ when(mDisplayManagerFlagsMock.isFastHdrTransitionsEnabled()).thenReturn(true);
Settings.System.putInt(mContext.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS_MODE,
Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
@@ -797,7 +797,7 @@ public final class DisplayPowerControllerTest {
advanceTime(1); // Run updatePowerState
verify(mHolder.animator).animateTo(eq(sdrBrightness), eq(sdrBrightness),
- eq(BRIGHTNESS_RAMP_RATE_MINIMUM), eq(false));
+ eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE), eq(false));
}
@Test
diff --git a/services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java b/services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java
index a8af98f7a332..8a6c2440a232 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java
@@ -16,6 +16,8 @@
package com.android.server.display;
+import static android.hardware.display.DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED;
+
import static com.android.internal.display.RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE;
import static org.junit.Assert.assertEquals;
@@ -50,6 +52,8 @@ public class RefreshRateSettingsUtilsTest {
private DisplayManager mDisplayManagerMock;
@Mock
private Display mDisplayMock;
+ @Mock
+ private Display mDisplayMock2;
@Before
public void setUp() {
@@ -65,25 +69,54 @@ public class RefreshRateSettingsUtilsTest {
new Display.Mode(/* modeId= */ 0, /* width= */ 800, /* height= */ 600,
/* refreshRate= */ 90)
};
-
when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(mDisplayMock);
when(mDisplayMock.getSupportedModes()).thenReturn(modes);
+
+ Display.Mode[] modes2 = new Display.Mode[]{
+ new Display.Mode(/* modeId= */ 0, /* width= */ 800, /* height= */ 600,
+ /* refreshRate= */ 70),
+ new Display.Mode(/* modeId= */ 0, /* width= */ 800, /* height= */ 600,
+ /* refreshRate= */ 130),
+ new Display.Mode(/* modeId= */ 0, /* width= */ 800, /* height= */ 600,
+ /* refreshRate= */ 80)
+ };
+ when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY + 1)).thenReturn(mDisplayMock2);
+ when(mDisplayMock2.getSupportedModes()).thenReturn(modes2);
+
+ when(mDisplayManagerMock.getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED))
+ .thenReturn(new Display[]{ mDisplayMock, mDisplayMock2 });
}
@Test
public void testFindHighestRefreshRateForDefaultDisplay() {
- when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(mDisplayMock);
assertEquals(120,
RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext),
/* delta= */ 0);
}
@Test
- public void testFindHighestRefreshRate_DisplayIsNull() {
+ public void testFindHighestRefreshRateForDefaultDisplay_DisplayIsNull() {
when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(null);
assertEquals(DEFAULT_REFRESH_RATE,
RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext),
/* delta= */ 0);
}
+
+ @Test
+ public void testFindHighestRefreshRateAmongAllDisplays() {
+ assertEquals(130,
+ RefreshRateSettingsUtils.findHighestRefreshRateAmongAllDisplays(mContext),
+ /* delta= */ 0);
+ }
+
+ @Test
+ public void testFindHighestRefreshRateAmongAllDisplays_NoDisplays() {
+ when(mDisplayManagerMock.getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED))
+ .thenReturn(new Display[0]);
+ assertEquals(DEFAULT_REFRESH_RATE,
+ RefreshRateSettingsUtils.findHighestRefreshRateAmongAllDisplays(mContext),
+ /* delta= */ 0);
+
+ }
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalClamperTest.java
index 37d0f6250aaf..34f352e7bf54 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalClamperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalClamperTest.java
@@ -37,10 +37,14 @@ import com.android.internal.annotations.Keep;
import com.android.server.display.DisplayDeviceConfig;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel;
+import com.android.server.display.config.SensorData;
import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.testutils.FakeDeviceConfigInterface;
import com.android.server.testutils.TestHandler;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -50,9 +54,6 @@ import org.mockito.MockitoAnnotations;
import java.util.List;
-import junitparams.JUnitParamsRunner;
-import junitparams.Parameters;
-
@RunWith(JUnitParamsRunner.class)
public class BrightnessThermalClamperTest {
@@ -125,13 +126,13 @@ public class BrightnessThermalClamperTest {
public void testNotifyThrottlingAfterOnDisplayChange(List<ThrottlingLevel> throttlingLevels,
@Temperature.ThrottlingStatus int throttlingStatus,
boolean expectedActive, float expectedBrightness) throws RemoteException {
- IThermalEventListener thermalEventListener = captureThermalEventListener();
+ IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
mClamper.onDisplayChanged(new TestThermalData(throttlingLevels));
mTestHandler.flush();
assertFalse(mClamper.isActive());
assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
- thermalEventListener.notifyThrottling(createTemperature(throttlingStatus));
+ thermalEventListener.notifyThrottling(createSkinTemperature(throttlingStatus));
mTestHandler.flush();
assertEquals(expectedActive, mClamper.isActive());
assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
@@ -139,11 +140,11 @@ public class BrightnessThermalClamperTest {
@Test
@Parameters(method = "testThrottlingData")
- public void testOnDisplayChangeAfterNotifyThrottlng(List<ThrottlingLevel> throttlingLevels,
+ public void testOnDisplayChangeAfterNotifyThrottling(List<ThrottlingLevel> throttlingLevels,
@Temperature.ThrottlingStatus int throttlingStatus,
boolean expectedActive, float expectedBrightness) throws RemoteException {
- IThermalEventListener thermalEventListener = captureThermalEventListener();
- thermalEventListener.notifyThrottling(createTemperature(throttlingStatus));
+ IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
+ thermalEventListener.notifyThrottling(createSkinTemperature(throttlingStatus));
mTestHandler.flush();
assertFalse(mClamper.isActive());
assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
@@ -156,8 +157,8 @@ public class BrightnessThermalClamperTest {
@Test
public void testOverrideData() throws RemoteException {
- IThermalEventListener thermalEventListener = captureThermalEventListener();
- thermalEventListener.notifyThrottling(createTemperature(Temperature.THROTTLING_SEVERE));
+ IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
+ thermalEventListener.notifyThrottling(createSkinTemperature(Temperature.THROTTLING_SEVERE));
mTestHandler.flush();
assertFalse(mClamper.isActive());
assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
@@ -183,15 +184,60 @@ public class BrightnessThermalClamperTest {
assertEquals(0.4f, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
}
- private IThermalEventListener captureThermalEventListener() throws RemoteException {
+ @Test
+ public void testDisplaySensorBasedThrottling() throws RemoteException {
+ final int severity = PowerManager.THERMAL_STATUS_SEVERE;
+ IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
+ // Update config to listen to display type sensor.
+ final SensorData tempSensor = new SensorData("DISPLAY", "VIRTUAL-SKIN-DISPLAY");
+ final TestThermalData thermalData =
+ new TestThermalData(
+ DISPLAY_ID,
+ DisplayDeviceConfig.DEFAULT_ID,
+ List.of(new ThrottlingLevel(severity, 0.5f)),
+ tempSensor);
+ mClamper.onDisplayChanged(thermalData);
+ mTestHandler.flush();
+ verify(mMockThermalService).unregisterThermalEventListener(thermalEventListener);
+ thermalEventListener = captureThermalEventListener(Temperature.TYPE_DISPLAY);
+ assertFalse(mClamper.isActive());
+
+ // Verify no throttling triggered when any other sensor notification received.
+ thermalEventListener.notifyThrottling(createSkinTemperature(severity));
+ mTestHandler.flush();
+ assertFalse(mClamper.isActive());
+
+ thermalEventListener.notifyThrottling(createDisplayTemperature("OTHER-SENSOR", severity));
+ mTestHandler.flush();
+ assertFalse(mClamper.isActive());
+
+ assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
+
+ // Verify throttling triggered when display sensor of given name throttled.
+ thermalEventListener.notifyThrottling(createDisplayTemperature(tempSensor.name, severity));
+ mTestHandler.flush();
+ assertTrue(mClamper.isActive());
+ assertEquals(0.5f, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
+ }
+
+ private IThermalEventListener captureSkinThermalEventListener() throws RemoteException {
+ return captureThermalEventListener(Temperature.TYPE_SKIN);
+ }
+
+ private IThermalEventListener captureThermalEventListener(int type) throws RemoteException {
ArgumentCaptor<IThermalEventListener> captor = ArgumentCaptor.forClass(
IThermalEventListener.class);
verify(mMockThermalService).registerThermalEventListenerWithType(captor.capture(), eq(
- Temperature.TYPE_SKIN));
+ type));
return captor.getValue();
}
- private Temperature createTemperature(@Temperature.ThrottlingStatus int status) {
+ private Temperature createDisplayTemperature(
+ @NonNull String sensorName, @Temperature.ThrottlingStatus int status) {
+ return new Temperature(100, Temperature.TYPE_DISPLAY, sensorName, status);
+ }
+
+ private Temperature createSkinTemperature(@Temperature.ThrottlingStatus int status) {
return new Temperature(100, Temperature.TYPE_SKIN, "test_temperature", status);
}
@@ -217,19 +263,26 @@ public class BrightnessThermalClamperTest {
private final String mUniqueDisplayId;
private final String mDataId;
private final ThermalBrightnessThrottlingData mData;
+ private final SensorData mTempSensor;
private TestThermalData() {
- this(DISPLAY_ID, DisplayDeviceConfig.DEFAULT_ID, null);
+ this(DISPLAY_ID, DisplayDeviceConfig.DEFAULT_ID, null,
+ SensorData.loadTempSensorUnspecifiedConfig());
}
private TestThermalData(List<ThrottlingLevel> data) {
- this(DISPLAY_ID, DisplayDeviceConfig.DEFAULT_ID, data);
+ this(DISPLAY_ID, DisplayDeviceConfig.DEFAULT_ID, data,
+ SensorData.loadTempSensorUnspecifiedConfig());
}
- private TestThermalData(String uniqueDisplayId, String dataId, List<ThrottlingLevel> data) {
+
+ private TestThermalData(String uniqueDisplayId, String dataId, List<ThrottlingLevel> data,
+ SensorData tempSensor) {
mUniqueDisplayId = uniqueDisplayId;
mDataId = dataId;
mData = ThermalBrightnessThrottlingData.create(data);
+ mTempSensor = tempSensor;
}
+
@NonNull
@Override
public String getUniqueDisplayId() {
@@ -247,5 +300,11 @@ public class BrightnessThermalClamperTest {
public ThermalBrightnessThrottlingData getThermalBrightnessThrottlingData() {
return mData;
}
+
+ @NonNull
+ @Override
+ public SensorData getTempSensor() {
+ return mTempSensor;
+ }
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java
index 3458b08245a2..306b4f86024e 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java
@@ -85,7 +85,7 @@ public class BrightnessWearBedtimeModeClamperTest {
@Test
public void testType() {
- assertEquals(BrightnessClamper.Type.BEDTIME_MODE, mClamper.getType());
+ assertEquals(BrightnessClamper.Type.WEAR_BEDTIME_MODE, mClamper.getType());
}
@Test
diff --git a/services/tests/dreamservicetests/Android.bp b/services/tests/dreamservicetests/Android.bp
index 8ef443e29cf6..5aa5d619854f 100644
--- a/services/tests/dreamservicetests/Android.bp
+++ b/services/tests/dreamservicetests/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_",
// See: http://go/android-license-faq
default_applicable_licenses: ["frameworks_base_license"],
}
diff --git a/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java b/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java
index 8f5d1253406e..a918be2292af 100644
--- a/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java
+++ b/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java
@@ -16,6 +16,8 @@
package com.android.server.media;
+import static android.Manifest.permission.MODIFY_AUDIO_ROUTING;
+
import static com.android.server.media.AudioRoutingUtils.ATTRIBUTES_MEDIA;
import static com.android.server.media.AudioRoutingUtils.getMediaAudioProductStrategy;
@@ -31,6 +33,7 @@ import static org.mockito.Mockito.when;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.Instrumentation;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothManager;
import android.content.Context;
@@ -48,6 +51,7 @@ import android.os.UserHandle;
import androidx.test.platform.app.InstrumentationRegistry;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -85,6 +89,7 @@ public class AudioManagerRouteControllerTest {
/* name= */ null,
/* address= */ null);
+ private Instrumentation mInstrumentation;
private AudioDeviceInfo mSelectedAudioDeviceInfo;
private Set<AudioDeviceInfo> mAvailableAudioDeviceInfos;
@Mock private AudioManager mMockAudioManager;
@@ -96,10 +101,11 @@ public class AudioManagerRouteControllerTest {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
-
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mInstrumentation.getUiAutomation().adoptShellPermissionIdentity(MODIFY_AUDIO_ROUTING);
Resources mockResources = Mockito.mock(Resources.class);
when(mockResources.getText(anyInt())).thenReturn(FAKE_ROUTE_NAME);
- Context realContext = InstrumentationRegistry.getInstrumentation().getContext();
+ Context realContext = mInstrumentation.getContext();
Context mockContext = Mockito.mock(Context.class);
when(mockContext.getResources()).thenReturn(mockResources);
// The bluetooth stack needs the application info, but we cannot use a spy because the
@@ -135,6 +141,11 @@ public class AudioManagerRouteControllerTest {
clearInvocations(mOnDeviceRouteChangedListener);
}
+ @After
+ public void tearDown() {
+ mInstrumentation.getUiAutomation().dropShellPermissionIdentity();
+ }
+
@Test
public void getSelectedRoute_afterDevicesConnect_returnsRightSelectedRoute() {
assertThat(mControllerUnderTest.getSelectedRoute().getType())
diff --git a/services/tests/mockingservicestests/src/com/android/server/OWNERS b/services/tests/mockingservicestests/src/com/android/server/OWNERS
index b363f545760e..f80156021408 100644
--- a/services/tests/mockingservicestests/src/com/android/server/OWNERS
+++ b/services/tests/mockingservicestests/src/com/android/server/OWNERS
@@ -2,3 +2,4 @@ per-file *Alarm* = file:/apex/jobscheduler/OWNERS
per-file *AppStateTracker* = file:/apex/jobscheduler/OWNERS
per-file *DeviceIdleController* = file:/apex/jobscheduler/OWNERS
per-file SensitiveContentProtectionManagerServiceTest.java = file:/core/java/android/permission/OWNERS
+per-file RescuePartyTest.java = file:/packages/CrashRecovery/OWNERS
diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
index 2d065e263a6f..211a83d8588e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
@@ -33,6 +33,7 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import android.content.ContentResolver;
@@ -45,7 +46,6 @@ import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.provider.Settings;
-import android.sysprop.CrashRecoveryProperties;
import android.util.ArraySet;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
@@ -64,6 +64,7 @@ import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;
+import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
@@ -101,6 +102,7 @@ public class RescuePartyTest {
private MockitoSession mSession;
private HashMap<String, String> mSystemSettingsMap;
+ private HashMap<String, String> mCrashRecoveryPropertiesMap;
//Records the namespaces wiped by setProperties().
private HashSet<String> mNamespacesWiped;
@@ -113,6 +115,9 @@ public class RescuePartyTest {
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private PackageManager mPackageManager;
+ // Mock only sysprop apis
+ private PackageWatchdog.BootThreshold mSpyBootThreshold;
+
@Captor
private ArgumentCaptor<DeviceConfig.MonitorCallback> mMonitorCallbackCaptor;
@Captor
@@ -208,11 +213,12 @@ public class RescuePartyTest {
// Mock PackageWatchdog
doAnswer((Answer<PackageWatchdog>) invocationOnMock -> mMockPackageWatchdog)
.when(() -> PackageWatchdog.getInstance(mMockContext));
+ mockCrashRecoveryProperties(mMockPackageWatchdog);
doReturn(CURRENT_NETWORK_TIME_MILLIS).when(() -> RescueParty.getElapsedRealtime());
- CrashRecoveryProperties.rescueBootCount(0);
- CrashRecoveryProperties.enableRescueParty(true);
+ setCrashRecoveryPropRescueBootCount(0);
+ SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true));
SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(false));
}
@@ -255,7 +261,7 @@ public class RescuePartyTest {
noteBoot(4);
assertTrue(RescueParty.isRebootPropertySet());
- CrashRecoveryProperties.attemptingReboot(false);
+ setCrashRecoveryPropAttemptingReboot(false);
noteBoot(5);
assertTrue(RescueParty.isFactoryResetPropertySet());
}
@@ -280,7 +286,7 @@ public class RescuePartyTest {
noteAppCrash(4, true);
assertTrue(RescueParty.isRebootPropertySet());
- CrashRecoveryProperties.attemptingReboot(false);
+ setCrashRecoveryPropAttemptingReboot(false);
noteAppCrash(5, true);
assertTrue(RescueParty.isFactoryResetPropertySet());
}
@@ -438,7 +444,7 @@ public class RescuePartyTest {
noteBoot(i + 1);
}
assertFalse(RescueParty.isFactoryResetPropertySet());
- CrashRecoveryProperties.attemptingReboot(false);
+ setCrashRecoveryPropAttemptingReboot(false);
noteBoot(LEVEL_FACTORY_RESET + 1);
assertTrue(RescueParty.isAttemptingFactoryReset());
assertTrue(RescueParty.isFactoryResetPropertySet());
@@ -456,7 +462,7 @@ public class RescuePartyTest {
noteBoot(mitigationCount++);
assertFalse(RescueParty.isFactoryResetPropertySet());
noteBoot(mitigationCount++);
- CrashRecoveryProperties.attemptingReboot(false);
+ setCrashRecoveryPropAttemptingReboot(false);
noteBoot(mitigationCount + 1);
assertTrue(RescueParty.isAttemptingFactoryReset());
assertTrue(RescueParty.isFactoryResetPropertySet());
@@ -464,10 +470,10 @@ public class RescuePartyTest {
@Test
public void testThrottlingOnBootFailures() {
- CrashRecoveryProperties.attemptingReboot(false);
+ setCrashRecoveryPropAttemptingReboot(false);
long now = System.currentTimeMillis();
long beforeTimeout = now - TimeUnit.MINUTES.toMillis(THROTTLING_DURATION_MIN - 1);
- CrashRecoveryProperties.lastFactoryResetTimeMs(beforeTimeout);
+ setCrashRecoveryPropLastFactoryReset(beforeTimeout);
for (int i = 1; i <= LEVEL_FACTORY_RESET; i++) {
noteBoot(i);
}
@@ -476,10 +482,10 @@ public class RescuePartyTest {
@Test
public void testThrottlingOnAppCrash() {
- CrashRecoveryProperties.attemptingReboot(false);
+ setCrashRecoveryPropAttemptingReboot(false);
long now = System.currentTimeMillis();
long beforeTimeout = now - TimeUnit.MINUTES.toMillis(THROTTLING_DURATION_MIN - 1);
- CrashRecoveryProperties.lastFactoryResetTimeMs(beforeTimeout);
+ setCrashRecoveryPropLastFactoryReset(beforeTimeout);
for (int i = 0; i <= LEVEL_FACTORY_RESET; i++) {
noteAppCrash(i + 1, true);
}
@@ -488,10 +494,10 @@ public class RescuePartyTest {
@Test
public void testNotThrottlingAfterTimeoutOnBootFailures() {
- CrashRecoveryProperties.attemptingReboot(false);
+ setCrashRecoveryPropAttemptingReboot(false);
long now = System.currentTimeMillis();
long afterTimeout = now - TimeUnit.MINUTES.toMillis(THROTTLING_DURATION_MIN + 1);
- CrashRecoveryProperties.lastFactoryResetTimeMs(afterTimeout);
+ setCrashRecoveryPropLastFactoryReset(afterTimeout);
for (int i = 1; i <= LEVEL_FACTORY_RESET; i++) {
noteBoot(i);
}
@@ -499,10 +505,10 @@ public class RescuePartyTest {
}
@Test
public void testNotThrottlingAfterTimeoutOnAppCrash() {
- CrashRecoveryProperties.attemptingReboot(false);
+ setCrashRecoveryPropAttemptingReboot(false);
long now = System.currentTimeMillis();
long afterTimeout = now - TimeUnit.MINUTES.toMillis(THROTTLING_DURATION_MIN + 1);
- CrashRecoveryProperties.lastFactoryResetTimeMs(afterTimeout);
+ setCrashRecoveryPropLastFactoryReset(afterTimeout);
for (int i = 0; i <= LEVEL_FACTORY_RESET; i++) {
noteAppCrash(i + 1, true);
}
@@ -525,26 +531,26 @@ public class RescuePartyTest {
@Test
public void testExplicitlyEnablingAndDisablingRescue() {
- CrashRecoveryProperties.enableRescueParty(false);
+ SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false));
SystemProperties.set(PROP_DISABLE_RESCUE, Boolean.toString(true));
assertEquals(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage,
PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1), false);
- CrashRecoveryProperties.enableRescueParty(true);
+ SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true));
assertTrue(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage,
PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1));
}
@Test
public void testDisablingRescueByDeviceConfigFlag() {
- CrashRecoveryProperties.enableRescueParty(false);
+ SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false));
SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(true));
assertEquals(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage,
PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1), false);
// Restore the property value initialized in SetUp()
- CrashRecoveryProperties.enableRescueParty(true);
+ SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true));
SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(false));
}
@@ -753,4 +759,138 @@ public class RescuePartyTest {
RescuePartyObserver.getInstance(mMockContext).execute(new VersionedPackage(
packageName, 1), PackageWatchdog.FAILURE_REASON_APP_CRASH, mitigationCount);
}
+
+ // Mock CrashRecoveryProperties as they cannot be accessed due to SEPolicy restrictions
+ private void mockCrashRecoveryProperties(PackageWatchdog watchdog) {
+ // mock properties in RescueParty
+ try {
+
+ doAnswer((Answer<Boolean>) invocationOnMock -> {
+ String storedValue = mCrashRecoveryPropertiesMap
+ .getOrDefault("crashrecovery.attempting_factory_reset", "false");
+ return Boolean.parseBoolean(storedValue);
+ }).when(() -> RescueParty.isFactoryResetPropertySet());
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ boolean value = invocationOnMock.getArgument(0);
+ mCrashRecoveryPropertiesMap.put("crashrecovery.attempting_factory_reset",
+ Boolean.toString(value));
+ return null;
+ }).when(() -> RescueParty.setFactoryResetProperty(anyBoolean()));
+
+ doAnswer((Answer<Boolean>) invocationOnMock -> {
+ String storedValue = mCrashRecoveryPropertiesMap
+ .getOrDefault("crashrecovery.attempting_reboot", "false");
+ return Boolean.parseBoolean(storedValue);
+ }).when(() -> RescueParty.isRebootPropertySet());
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ boolean value = invocationOnMock.getArgument(0);
+ setCrashRecoveryPropAttemptingReboot(value);
+ return null;
+ }).when(() -> RescueParty.setRebootProperty(anyBoolean()));
+
+ doAnswer((Answer<Long>) invocationOnMock -> {
+ String storedValue = mCrashRecoveryPropertiesMap
+ .getOrDefault("persist.crashrecovery.last_factory_reset", "0");
+ return Long.parseLong(storedValue);
+ }).when(() -> RescueParty.getLastFactoryResetTimeMs());
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ long value = invocationOnMock.getArgument(0);
+ setCrashRecoveryPropLastFactoryReset(value);
+ return null;
+ }).when(() -> RescueParty.setLastFactoryResetTimeMs(anyLong()));
+
+ doAnswer((Answer<Integer>) invocationOnMock -> {
+ String storedValue = mCrashRecoveryPropertiesMap
+ .getOrDefault("crashrecovery.max_rescue_level_attempted", "0");
+ return Integer.parseInt(storedValue);
+ }).when(() -> RescueParty.getMaxRescueLevelAttempted());
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ int value = invocationOnMock.getArgument(0);
+ mCrashRecoveryPropertiesMap.put("crashrecovery.max_rescue_level_attempted",
+ Integer.toString(value));
+ return null;
+ }).when(() -> RescueParty.setMaxRescueLevelAttempted(anyInt()));
+
+ } catch (Exception e) {
+ // tests will fail, just printing the error
+ System.out.println("Error while mocking crashrecovery properties " + e.getMessage());
+ }
+
+ // mock properties in BootThreshold
+ try {
+ mSpyBootThreshold = spy(watchdog.new BootThreshold(
+ PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT,
+ PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS));
+ mCrashRecoveryPropertiesMap = new HashMap<>();
+
+ doAnswer((Answer<Integer>) invocationOnMock -> {
+ String storedValue = mCrashRecoveryPropertiesMap
+ .getOrDefault("crashrecovery.rescue_boot_count", "0");
+ return Integer.parseInt(storedValue);
+ }).when(mSpyBootThreshold).getCount();
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ int count = invocationOnMock.getArgument(0);
+ setCrashRecoveryPropRescueBootCount(count);
+ return null;
+ }).when(mSpyBootThreshold).setCount(anyInt());
+
+ doAnswer((Answer<Integer>) invocationOnMock -> {
+ String storedValue = mCrashRecoveryPropertiesMap
+ .getOrDefault("crashrecovery.boot_mitigation_count", "0");
+ return Integer.parseInt(storedValue);
+ }).when(mSpyBootThreshold).getMitigationCount();
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ int count = invocationOnMock.getArgument(0);
+ mCrashRecoveryPropertiesMap.put("crashrecovery.boot_mitigation_count",
+ Integer.toString(count));
+ return null;
+ }).when(mSpyBootThreshold).setMitigationCount(anyInt());
+
+ doAnswer((Answer<Long>) invocationOnMock -> {
+ String storedValue = mCrashRecoveryPropertiesMap
+ .getOrDefault("crashrecovery.rescue_boot_start", "0");
+ return Long.parseLong(storedValue);
+ }).when(mSpyBootThreshold).getStart();
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ long count = invocationOnMock.getArgument(0);
+ mCrashRecoveryPropertiesMap.put("crashrecovery.rescue_boot_start",
+ Long.toString(count));
+ return null;
+ }).when(mSpyBootThreshold).setStart(anyLong());
+
+ doAnswer((Answer<Long>) invocationOnMock -> {
+ String storedValue = mCrashRecoveryPropertiesMap
+ .getOrDefault("crashrecovery.boot_mitigation_start", "0");
+ return Long.parseLong(storedValue);
+ }).when(mSpyBootThreshold).getMitigationStart();
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ long count = invocationOnMock.getArgument(0);
+ mCrashRecoveryPropertiesMap.put("crashrecovery.boot_mitigation_start",
+ Long.toString(count));
+ return null;
+ }).when(mSpyBootThreshold).setMitigationStart(anyLong());
+
+ Field mBootThresholdField = watchdog.getClass().getDeclaredField("mBootThreshold");
+ mBootThresholdField.setAccessible(true);
+ mBootThresholdField.set(watchdog, mSpyBootThreshold);
+ } catch (Exception e) {
+ // tests will fail, just printing the error
+ System.out.println("Error while spying BootThreshold " + e.getMessage());
+ }
+ }
+
+ private void setCrashRecoveryPropRescueBootCount(int count) {
+ mCrashRecoveryPropertiesMap.put("crashrecovery.rescue_boot_count",
+ Integer.toString(count));
+ }
+
+ private void setCrashRecoveryPropAttemptingReboot(boolean value) {
+ mCrashRecoveryPropertiesMap.put("crashrecovery.attempting_reboot",
+ Boolean.toString(value));
+ }
+
+ private void setCrashRecoveryPropLastFactoryReset(long value) {
+ mCrashRecoveryPropertiesMap.put("persist.crashrecovery.last_factory_reset",
+ Long.toString(value));
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceContentTest.java b/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceContentTest.java
new file mode 100644
index 000000000000..dad36e787360
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceContentTest.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static android.view.flags.Flags.FLAG_SENSITIVE_CONTENT_APP_PROTECTION;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.media.projection.MediaProjectionInfo;
+import android.media.projection.MediaProjectionManager;
+import android.os.Binder;
+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.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
+import android.util.ArraySet;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.wm.SensitiveContentPackages.PackageInfo;
+import com.android.server.wm.WindowManagerInternal;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RequiresFlagsEnabled(FLAG_SENSITIVE_CONTENT_APP_PROTECTION)
+/**
+ * Test {@link SensitiveContentProtectionManagerService} for sensitive on screen content
+ * protection, the service protects sensitive content during screen share.
+ */
+public class SensitiveContentProtectionManagerServiceContentTest {
+ private final PackageInfo mPackageInfo =
+ new PackageInfo("test.package", 12345, new Binder());
+ private SensitiveContentProtectionManagerService mSensitiveContentProtectionManagerService;
+ private MediaProjectionManager.Callback mMediaPorjectionCallback;
+
+ @Mock private WindowManagerInternal mWindowManager;
+ @Mock private MediaProjectionManager mProjectionManager;
+ @Mock private MediaProjectionInfo mMediaProjectionInfo;
+
+ @Captor
+ private ArgumentCaptor<MediaProjectionManager.Callback> mMediaProjectionCallbackCaptor;
+ @Captor
+ private ArgumentCaptor<ArraySet<PackageInfo>> mPackageInfoCaptor;
+
+ @Rule
+ public final TestableContext mContext =
+ new TestableContext(getInstrumentation().getTargetContext(), null);
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mSensitiveContentProtectionManagerService =
+ new SensitiveContentProtectionManagerService(mContext);
+ mSensitiveContentProtectionManagerService.init(mProjectionManager, mWindowManager);
+ verify(mProjectionManager).addCallback(mMediaProjectionCallbackCaptor.capture(), any());
+ mMediaPorjectionCallback = mMediaProjectionCallbackCaptor.getValue();
+ }
+
+ @Test
+ public void testBlockAppWindowForScreenCapture() {
+ mMediaPorjectionCallback.onStart(mMediaProjectionInfo);
+ mSensitiveContentProtectionManagerService.setSensitiveContentProtection(
+ mPackageInfo.getWindowToken(), mPackageInfo.getPkg(), mPackageInfo.getUid(), true);
+ verify(mWindowManager, atLeast(1))
+ .addBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
+ assertThat(mPackageInfoCaptor.getValue()).containsExactly(mPackageInfo);
+ }
+
+ @Test
+ public void testUnblockAppWindowForScreenCapture() {
+ mMediaPorjectionCallback.onStart(mMediaProjectionInfo);
+ mSensitiveContentProtectionManagerService.setSensitiveContentProtection(
+ mPackageInfo.getWindowToken(), mPackageInfo.getPkg(), mPackageInfo.getUid(), false);
+ verify(mWindowManager).removeBlockScreenCaptureForApps(mPackageInfoCaptor.capture());
+ assertThat(mPackageInfoCaptor.getValue()).containsExactly(mPackageInfo);
+ }
+
+ @Test
+ public void testAppWindowIsUnblockedBeforeScreenCapture() {
+ // when screen sharing is not active, no app window should be blocked.
+ mSensitiveContentProtectionManagerService.setSensitiveContentProtection(
+ mPackageInfo.getWindowToken(), mPackageInfo.getPkg(), mPackageInfo.getUid(), true);
+ verifyZeroInteractions(mWindowManager);
+ }
+
+ @Test
+ public void testAppWindowsAreUnblockedOnScreenCaptureEnd() {
+ mMediaPorjectionCallback.onStart(mMediaProjectionInfo);
+ mSensitiveContentProtectionManagerService.setSensitiveContentProtection(
+ mPackageInfo.getWindowToken(), mPackageInfo.getPkg(), mPackageInfo.getUid(), true);
+ // when screen sharing ends, all blocked app windows should be cleared.
+ mMediaPorjectionCallback.onStop(mMediaProjectionInfo);
+ verify(mWindowManager).clearBlockedApps();
+ }
+
+ @Test
+ public void testDeveloperOptionDisableFeature() {
+ mockDisabledViaDeveloperOption();
+ mMediaProjectionCallbackCaptor.getValue().onStart(mMediaProjectionInfo);
+ mSensitiveContentProtectionManagerService.setSensitiveContentProtection(
+ mPackageInfo.getWindowToken(), mPackageInfo.getPkg(), mPackageInfo.getUid(), true);
+ verifyZeroInteractions(mWindowManager);
+ }
+
+ private void mockDisabledViaDeveloperOption() {
+ Settings.Global.putInt(
+ mContext.getContentResolver(),
+ Settings.Global.DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS,
+ 1);
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceNotificationTest.java
index 9473e572259f..08050a9b30e0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/SensitiveContentProtectionManagerServiceNotificationTest.java
@@ -16,9 +16,9 @@
package com.android.server;
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static android.permission.flags.Flags.FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION;
-import static com.android.server.notification.Flags.FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
@@ -67,7 +67,11 @@ import java.util.Set;
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@RequiresFlagsEnabled(FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION)
-public class SensitiveContentProtectionManagerServiceTest {
+/**
+ * Test {@link SensitiveContentProtectionManagerService} for sensitive notification protection,
+ * the service protects sensitive content during screen share.
+ */
+public class SensitiveContentProtectionManagerServiceNotificationTest {
private static final String NOTIFICATION_KEY_1 = "com.android.server.notification.TEST_KEY_1";
private static final String NOTIFICATION_KEY_2 = "com.android.server.notification.TEST_KEY_2";
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index a4761555384e..99752212fcbd 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -16,6 +16,8 @@
package com.android.server.alarm;
import static android.Manifest.permission.SCHEDULE_EXACT_ALARM;
+import static android.app.ActivityManager.UidFrozenStateChangedCallback.UID_FROZEN_STATE_FROZEN;
+import static android.app.ActivityManager.UidFrozenStateChangedCallback.UID_FROZEN_STATE_UNFROZEN;
import static android.app.AlarmManager.ELAPSED_REALTIME;
import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
import static android.app.AlarmManager.EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED;
@@ -42,6 +44,7 @@ import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.NULL_DEFAULT;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
@@ -135,6 +138,7 @@ import android.net.Uri;
import android.os.BatteryManager;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
@@ -145,9 +149,11 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.platform.test.flag.util.FlagSetException;
import android.provider.DeviceConfig;
-import android.provider.Settings;
import android.text.format.DateFormat;
import android.util.ArraySet;
import android.util.Log;
@@ -215,6 +221,7 @@ public final class AlarmManagerServiceTest {
private AppStateTrackerImpl.Listener mListener;
private AlarmManagerService.UninstallReceiver mPackageChangesReceiver;
private AlarmManagerService.ChargingReceiver mChargingReceiver;
+ private ActivityManager.UidFrozenStateChangedCallback mUidFrozenStateCallback;
private IAppOpsCallback mIAppOpsCallback;
private IAlarmManager mBinder;
@Mock
@@ -240,6 +247,8 @@ public final class AlarmManagerServiceTest {
@Mock
private ActivityManagerInternal mActivityManagerInternal;
@Mock
+ private ActivityManager mActivityManager;
+ @Mock
private PackageManagerInternal mPackageManagerInternal;
@Mock
private AppStateTrackerImpl mAppStateTracker;
@@ -403,15 +412,31 @@ public final class AlarmManagerServiceTest {
.mockStatic(PermissionChecker.class)
.mockStatic(PermissionManagerService.class)
.mockStatic(ServiceManager.class)
- .mockStatic(Settings.Global.class)
.mockStatic(SystemProperties.class)
.spyStatic(UserHandle.class)
.afterSessionFinished(
() -> LocalServices.removeServiceForTest(AlarmManagerInternal.class))
.build();
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(NULL_DEFAULT);
+
+ /**
+ * Have to do this to switch the {@link Flags} implementation to {@link FakeFeatureFlagsImpl}.
+ * All methods that need any flag enabled should use the
+ * {@link android.platform.test.annotations.EnableFlags} annotation, in which case disabling
+ * the flag will fail with an exception that we will swallow here.
+ */
+ private void disableFlagsNotSetByAnnotation() {
+ try {
+ mSetFlagsRule.disableFlags(Flags.FLAG_USE_FROZEN_STATE_TO_DROP_LISTENER_ALARMS);
+ } catch (FlagSetException fse) {
+ // Expected if the test about to be run requires this enabled.
+ }
+ }
+
@Before
- public final void setUp() {
+ public void setUp() {
doReturn(mIActivityManager).when(ActivityManager::getService);
doReturn(mDeviceIdleInternal).when(
() -> LocalServices.getService(DeviceIdleInternal.class));
@@ -469,6 +494,7 @@ public final class AlarmManagerServiceTest {
when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager);
when(mMockContext.getSystemService(BatteryManager.class)).thenReturn(mBatteryManager);
+ when(mMockContext.getSystemService(ActivityManager.class)).thenReturn(mActivityManager);
registerAppIds(new String[]{TEST_CALLING_PACKAGE},
new Integer[]{UserHandle.getAppId(TEST_CALLING_UID)});
@@ -479,7 +505,18 @@ public final class AlarmManagerServiceTest {
mService = new AlarmManagerService(mMockContext, mInjector);
spyOn(mService);
+ disableFlagsNotSetByAnnotation();
+
mService.onStart();
+
+ if (Flags.useFrozenStateToDropListenerAlarms()) {
+ final ArgumentCaptor<ActivityManager.UidFrozenStateChangedCallback> frozenCaptor =
+ ArgumentCaptor.forClass(ActivityManager.UidFrozenStateChangedCallback.class);
+ verify(mActivityManager).registerUidFrozenStateChangedCallback(
+ any(HandlerExecutor.class), frozenCaptor.capture());
+ mUidFrozenStateCallback = frozenCaptor.getValue();
+ }
+
// Unable to mock mMockContext to return a mock stats manager.
// So just mocking the whole MetricsHelper instance.
mService.mMetricsHelper = mock(MetricsHelper.class);
@@ -3741,9 +3778,87 @@ public final class AlarmManagerServiceTest {
mListener.handleUidCachedChanged(TEST_CALLING_UID, true);
assertAndHandleMessageSync(REMOVE_EXACT_LISTENER_ALARMS_ON_CACHED);
assertEquals(3, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
+ assertEquals(numExactListenerUid2 + 2, mService.mAlarmsPerUid.get(TEST_CALLING_UID_2));
mListener.handleUidCachedChanged(TEST_CALLING_UID_2, true);
assertAndHandleMessageSync(REMOVE_EXACT_LISTENER_ALARMS_ON_CACHED);
+ assertEquals(3, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
+ assertEquals(2, mService.mAlarmsPerUid.get(TEST_CALLING_UID_2));
+ }
+
+ private void executeUidFrozenStateCallback(int[] uids, int[] frozenStates) {
+ assertNotNull(mUidFrozenStateCallback);
+ mUidFrozenStateCallback.onUidFrozenStateChanged(uids, frozenStates);
+ }
+
+ @EnableFlags(Flags.FLAG_USE_FROZEN_STATE_TO_DROP_LISTENER_ALARMS)
+ @Test
+ public void exactListenerAlarmsRemovedOnFrozen() {
+ mockChangeEnabled(EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED, true);
+
+ setTestAlarmWithListener(ELAPSED_REALTIME, 31, getNewListener(() -> {}), WINDOW_EXACT,
+ TEST_CALLING_UID);
+ setTestAlarmWithListener(RTC, 42, getNewListener(() -> {}), 56, TEST_CALLING_UID);
+ setTestAlarm(ELAPSED_REALTIME, 54, WINDOW_EXACT, getNewMockPendingIntent(), 0, 0,
+ TEST_CALLING_UID, null);
+ setTestAlarm(RTC, 49, 154, getNewMockPendingIntent(), 0, 0, TEST_CALLING_UID, null);
+
+ setTestAlarmWithListener(ELAPSED_REALTIME, 21, getNewListener(() -> {}), WINDOW_EXACT,
+ TEST_CALLING_UID_2);
+ setTestAlarmWithListener(RTC, 412, getNewListener(() -> {}), 561, TEST_CALLING_UID_2);
+ setTestAlarm(ELAPSED_REALTIME, 26, WINDOW_EXACT, getNewMockPendingIntent(), 0, 0,
+ TEST_CALLING_UID_2, null);
+ setTestAlarm(RTC, 549, 234, getNewMockPendingIntent(), 0, 0, TEST_CALLING_UID_2, null);
+
+ assertEquals(8, mService.mAlarmStore.size());
+
+ executeUidFrozenStateCallback(
+ new int[] {TEST_CALLING_UID, TEST_CALLING_UID_2},
+ new int[] {UID_FROZEN_STATE_FROZEN, UID_FROZEN_STATE_UNFROZEN});
+ assertEquals(7, mService.mAlarmStore.size());
+
+ executeUidFrozenStateCallback(
+ new int[] {TEST_CALLING_UID_2}, new int[] {UID_FROZEN_STATE_FROZEN});
+ assertEquals(6, mService.mAlarmStore.size());
+ }
+
+ @EnableFlags(Flags.FLAG_USE_FROZEN_STATE_TO_DROP_LISTENER_ALARMS)
+ @Test
+ public void alarmCountOnListenerFrozen() {
+ mockChangeEnabled(EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED, true);
+
+ // Set some alarms for TEST_CALLING_UID.
+ final int numExactListenerUid1 = 17;
+ for (int i = 0; i < numExactListenerUid1; i++) {
+ setTestAlarmWithListener(ALARM_TYPES[i % 4], mNowElapsedTest + i,
+ getNewListener(() -> {}));
+ }
+ setTestAlarmWithListener(RTC, 42, getNewListener(() -> {}), 56, TEST_CALLING_UID);
+ setTestAlarm(ELAPSED_REALTIME, 54, getNewMockPendingIntent());
+ setTestAlarm(RTC, 49, 154, getNewMockPendingIntent(), 0, 0, TEST_CALLING_UID, null);
+
+ // Set some alarms for TEST_CALLING_UID_2.
+ final int numExactListenerUid2 = 11;
+ for (int i = 0; i < numExactListenerUid2; i++) {
+ setTestAlarmWithListener(ALARM_TYPES[i % 4], mNowElapsedTest + i,
+ getNewListener(() -> {}), WINDOW_EXACT, TEST_CALLING_UID_2);
+ }
+ setTestAlarmWithListener(RTC, 412, getNewListener(() -> {}), 561, TEST_CALLING_UID_2);
+ setTestAlarm(RTC_WAKEUP, 26, WINDOW_EXACT, getNewMockPendingIntent(), 0, 0,
+ TEST_CALLING_UID_2, null);
+
+ assertEquals(numExactListenerUid1 + 3, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
+ assertEquals(numExactListenerUid2 + 2, mService.mAlarmsPerUid.get(TEST_CALLING_UID_2));
+
+ executeUidFrozenStateCallback(
+ new int[] {TEST_CALLING_UID, TEST_CALLING_UID_2},
+ new int[] {UID_FROZEN_STATE_FROZEN, UID_FROZEN_STATE_UNFROZEN});
+ assertEquals(3, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
+ assertEquals(numExactListenerUid2 + 2, mService.mAlarmsPerUid.get(TEST_CALLING_UID_2));
+
+ executeUidFrozenStateCallback(
+ new int[] {TEST_CALLING_UID_2}, new int[] {UID_FROZEN_STATE_FROZEN});
+ assertEquals(3, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
assertEquals(2, mService.mAlarmsPerUid.get(TEST_CALLING_UID_2));
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
index dfb8fda56edf..240ddf51ffdc 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CacheOomRankerTest.java
@@ -751,7 +751,6 @@ public class CacheOomRankerTest {
app.mState.setCurAdj(setAdj);
app.setLastActivityTime(lastActivityTime);
mPidToRss.put(app.getPid(), lastRss);
- app.mState.setCached(false);
for (int i = 0; i < wentToForegroundCount; ++i) {
app.mState.setSetProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
app.mState.setSetProcState(ActivityManager.PROCESS_STATE_CACHED_RECENT);
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 d876dae29798..1226f0c0b315 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -72,6 +72,8 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.AdditionalAnswers.answer;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
@@ -157,9 +159,11 @@ public class MockingOomAdjusterTests {
private static final int MOCKAPP2_UID_OTHER = MOCKAPP2_UID + UserHandle.PER_USER_RANGE;
private static final int MOCKAPP_ISOLATED_UID = Process.FIRST_ISOLATED_UID + 321;
private static final String MOCKAPP_ISOLATED_PROCESSNAME = "isolated test #1";
+ private static final int MOCKAPP_SDK_SANDBOX_UID = Process.FIRST_SDK_SANDBOX_UID + 654;
+ private static final String MOCKAPP_SDK_SANDBOX_PROCESSNAME = "sandbox test #1";
private static int sFirstCachedAdj = ProcessList.CACHED_APP_MIN_ADJ
- + ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
+ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
private static Context sContext;
private static PackageManagerInternal sPackageManagerInternal;
private static ActivityManagerService sService;
@@ -271,7 +275,6 @@ public class MockingOomAdjusterTests {
/**
* Replace the process LRU with the given processes.
- * @param apps
*/
@SuppressWarnings("GuardedBy")
private void setProcessesToLru(ProcessRecord... apps) {
@@ -467,6 +470,7 @@ public class MockingOomAdjusterTests {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
app.mState.setCurRawAdj(CACHED_APP_MIN_ADJ);
+ app.mState.setCurAdj(CACHED_APP_MIN_ADJ);
doReturn(null).when(sService).getTopApp();
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
@@ -491,6 +495,8 @@ public class MockingOomAdjusterTests {
field.set(callback, PROCESS_STATE_TOP);
field = callback.getClass().getDeclaredField("schedGroup");
field.set(callback, SCHED_GROUP_TOP_APP);
+ field = callback.getClass().getDeclaredField("mAdjType");
+ field.set(callback, "vis-activity");
return 0;
})).when(wpc).computeOomAdjFromActivities(
any(WindowProcessController.ComputeOomAdjCallback.class));
@@ -498,6 +504,9 @@ public class MockingOomAdjusterTests {
updateOomAdj(app);
assertProcStates(app, PROCESS_STATE_TOP, VISIBLE_APP_ADJ, SCHED_GROUP_TOP_APP);
+ assertFalse(app.mState.isCached());
+ assertFalse(app.mState.isEmpty());
+ assertEquals("vis-activity", app.mState.getAdjType());
}
@SuppressWarnings("GuardedBy")
@@ -660,7 +669,7 @@ public class MockingOomAdjusterTests {
app.mState.setLastTopTime(nowUptime);
// Simulate the system starting and binding to a service in the app.
ServiceRecord s = bindService(app, system,
- null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class));
+ null, null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class));
s.lastTopAlmostPerceptibleBindRequestUptimeMs = nowUptime;
s.getConnections().clear();
app.mServices.updateHasTopStartedAlmostPerceptibleServices();
@@ -682,7 +691,7 @@ public class MockingOomAdjusterTests {
app.mState.setLastTopTime(nowUptime);
// Simulate the system starting and binding to a service in the app.
ServiceRecord s = bindService(app, system,
- null, Context.BIND_ALMOST_PERCEPTIBLE + 2, mock(IBinder.class));
+ null, null, Context.BIND_ALMOST_PERCEPTIBLE + 2, mock(IBinder.class));
s.lastTopAlmostPerceptibleBindRequestUptimeMs =
nowUptime - 2 * sService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs;
app.mServices.updateHasTopStartedAlmostPerceptibleServices();
@@ -704,7 +713,7 @@ public class MockingOomAdjusterTests {
app.mState.setLastTopTime(nowUptime);
// Simulate the system starting and binding to a service in the app.
ServiceRecord s = bindService(app, system,
- null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class));
+ null, null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class));
s.lastTopAlmostPerceptibleBindRequestUptimeMs =
nowUptime - 2 * sService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs;
s.getConnections().clear();
@@ -729,7 +738,7 @@ public class MockingOomAdjusterTests {
system.mState.setHasTopUi(true);
// Simulate the system starting and binding to a service in the app.
ServiceRecord s = bindService(app, system,
- null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class));
+ null, null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class));
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(system, app);
@@ -868,8 +877,8 @@ public class MockingOomAdjusterTests {
public void testUpdateOomAdj_DoOne_NonCachedToCached() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
- app.mState.setCached(false);
app.mState.setCurRawAdj(SERVICE_ADJ);
+ app.mState.setCurAdj(SERVICE_ADJ);
doReturn(null).when(sService).getTopApp();
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
@@ -901,7 +910,7 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- ServiceRecord s = bindService(app, client, null, Context.BIND_WAIVE_PRIORITY,
+ ServiceRecord s = bindService(app, client, null, null, Context.BIND_WAIVE_PRIORITY,
mock(IBinder.class));
s.startRequested = true;
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
@@ -921,7 +930,7 @@ public class MockingOomAdjusterTests {
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
client.mServices.setTreatLikeActivity(true);
- bindService(app, client, null, Context.BIND_WAIVE_PRIORITY
+ bindService(app, client, null, null, Context.BIND_WAIVE_PRIORITY
| Context.BIND_TREAT_LIKE_ACTIVITY, mock(IBinder.class));
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
@@ -937,7 +946,7 @@ public class MockingOomAdjusterTests {
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
IBinder binder = mock(IBinder.class);
- ServiceRecord s = bindService(app, client, null, Context.BIND_WAIVE_PRIORITY
+ ServiceRecord s = bindService(app, client, null, null, Context.BIND_WAIVE_PRIORITY
| Context.BIND_ADJUST_WITH_ACTIVITY | Context.BIND_IMPORTANT, binder);
ConnectionRecord cr = s.getConnections().get(binder).get(0);
setFieldValue(ConnectionRecord.class, cr, "activity",
@@ -955,7 +964,7 @@ public class MockingOomAdjusterTests {
public void testUpdateOomAdj_DoOne_Service_Self() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
- bindService(app, app, null, 0, mock(IBinder.class));
+ bindService(app, app, null, null, 0, mock(IBinder.class));
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
@@ -970,7 +979,7 @@ public class MockingOomAdjusterTests {
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
client.mServices.setTreatLikeActivity(true);
- bindService(app, client, null, 0, mock(IBinder.class));
+ bindService(app, client, null, null, 0, mock(IBinder.class));
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
@@ -988,7 +997,8 @@ public class MockingOomAdjusterTests {
doReturn(true).when(wpc).hasActivities();
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- bindService(app, client, null, Context.BIND_ALLOW_OOM_MANAGEMENT, mock(IBinder.class));
+ bindService(app, client, null, null, Context.BIND_ALLOW_OOM_MANAGEMENT,
+ mock(IBinder.class));
doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
doReturn(client).when(sService).getTopApp();
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
@@ -1005,7 +1015,7 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- bindService(app, client, null, Context.BIND_FOREGROUND_SERVICE, mock(IBinder.class));
+ bindService(app, client, null, null, Context.BIND_FOREGROUND_SERVICE, mock(IBinder.class));
client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
client.mState.setHasTopUi(true);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
@@ -1023,7 +1033,7 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- bindService(app, client, null, Context.BIND_IMPORTANT, mock(IBinder.class));
+ bindService(app, client, null, null, Context.BIND_IMPORTANT, mock(IBinder.class));
client.mServices.startExecutingService(mock(ServiceRecord.class));
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
@@ -1039,7 +1049,7 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- bindService(app, client, null, 0, mock(IBinder.class));
+ bindService(app, client, null, null, 0, mock(IBinder.class));
doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
doReturn(client).when(sService).getTopApp();
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
@@ -1056,7 +1066,7 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- bindService(app, client, null, Context.BIND_FOREGROUND_SERVICE, mock(IBinder.class));
+ bindService(app, client, null, null, Context.BIND_FOREGROUND_SERVICE, mock(IBinder.class));
client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
@@ -1074,7 +1084,7 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- bindService(app, client, null, Context.BIND_NOT_FOREGROUND, mock(IBinder.class));
+ bindService(app, client, null, null, Context.BIND_NOT_FOREGROUND, mock(IBinder.class));
client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
@@ -1090,7 +1100,7 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- bindService(app, client, null, 0, mock(IBinder.class));
+ bindService(app, client, null, null, 0, mock(IBinder.class));
client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
@@ -1109,7 +1119,7 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- bindService(app, client, null, 0, mock(IBinder.class));
+ bindService(app, client, null, null, 0, mock(IBinder.class));
// In order to trick OomAdjuster to think it has a short-service, we need this logic.
ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(sService);
@@ -1172,8 +1182,8 @@ public class MockingOomAdjusterTests {
ProcessRecord app1 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- bindService(app1, pers, null, Context.BIND_FOREGROUND_SERVICE, mock(IBinder.class));
- bindService(app2, app1, null, 0, mock(IBinder.class));
+ bindService(app1, pers, null, null, Context.BIND_FOREGROUND_SERVICE, mock(IBinder.class));
+ bindService(app2, app1, null, null, 0, mock(IBinder.class));
updateOomAdj(pers, app1, app2);
@@ -1192,7 +1202,7 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- bindService(app, client, null, Context.BIND_ABOVE_CLIENT, mock(IBinder.class));
+ bindService(app, client, null, null, Context.BIND_ABOVE_CLIENT, mock(IBinder.class));
BackupRecord backupTarget = new BackupRecord(null, 0, 0, 0);
backupTarget.app = client;
doReturn(backupTarget).when(sService.mBackupTargets).get(anyInt());
@@ -1218,7 +1228,7 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- bindService(app, client, null, Context.BIND_NOT_PERCEPTIBLE, mock(IBinder.class));
+ bindService(app, client, null, null, Context.BIND_NOT_PERCEPTIBLE, mock(IBinder.class));
client.mState.setRunningRemoteAnimation(true);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
@@ -1233,7 +1243,7 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- bindService(app, client, null, Context.BIND_NOT_VISIBLE, mock(IBinder.class));
+ bindService(app, client, null, null, Context.BIND_NOT_VISIBLE, mock(IBinder.class));
client.mState.setRunningRemoteAnimation(true);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
@@ -1248,7 +1258,7 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- bindService(app, client, null, 0, mock(IBinder.class));
+ bindService(app, client, null, null, 0, mock(IBinder.class));
client.mState.setHasOverlayUi(true);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
@@ -1264,7 +1274,7 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- bindService(app, client, null,
+ bindService(app, client, null, null,
Context.BIND_ALMOST_PERCEPTIBLE | Context.BIND_NOT_FOREGROUND,
mock(IBinder.class));
client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
@@ -1283,7 +1293,7 @@ public class MockingOomAdjusterTests {
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
WindowProcessController wpc = client.getWindowProcessController();
doReturn(true).when(wpc).isHeavyWeightProcess();
- bindService(app, client, null,
+ bindService(app, client, null, null,
Context.BIND_ALMOST_PERCEPTIBLE | Context.BIND_NOT_FOREGROUND,
mock(IBinder.class));
client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
@@ -1301,7 +1311,7 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- bindService(app, client, null,
+ bindService(app, client, null, null,
Context.BIND_ALMOST_PERCEPTIBLE,
mock(IBinder.class));
client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
@@ -1320,7 +1330,7 @@ public class MockingOomAdjusterTests {
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
WindowProcessController wpc = client.getWindowProcessController();
doReturn(true).when(wpc).isHeavyWeightProcess();
- bindService(app, client, null,
+ bindService(app, client, null, null,
Context.BIND_ALMOST_PERCEPTIBLE,
mock(IBinder.class));
client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
@@ -1341,7 +1351,7 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- bindService(app, client, null, 0, mock(IBinder.class));
+ bindService(app, client, null, null, 0, mock(IBinder.class));
client.mState.setRunningRemoteAnimation(true);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
@@ -1356,7 +1366,8 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- bindService(app, client, null, Context.BIND_IMPORTANT_BACKGROUND, mock(IBinder.class));
+ bindService(app, client, null, null, Context.BIND_IMPORTANT_BACKGROUND,
+ mock(IBinder.class));
client.mState.setHasOverlayUi(true);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, app);
@@ -1496,10 +1507,10 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- bindService(app, client, null, 0, mock(IBinder.class));
+ bindService(app, client, null, null, 0, mock(IBinder.class));
ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
- bindService(client, client2, null, 0, mock(IBinder.class));
+ bindService(client, client2, null, null, 0, mock(IBinder.class));
doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
doReturn(client2).when(sService).getTopApp();
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
@@ -1517,10 +1528,10 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- bindService(app, client, null, 0, mock(IBinder.class));
+ bindService(app, client, null, null, 0, mock(IBinder.class));
ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
- bindService(app, client2, null, 0, mock(IBinder.class));
+ bindService(app, client2, null, null, 0, mock(IBinder.class));
client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, client2, app);
@@ -1537,10 +1548,10 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- bindService(app, client, null, 0, mock(IBinder.class));
+ bindService(app, client, null, null, 0, mock(IBinder.class));
ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
- bindService(client, client2, null, 0, mock(IBinder.class));
+ bindService(client, client2, null, null, 0, mock(IBinder.class));
client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, client2, app);
@@ -1557,12 +1568,12 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- bindService(app, client, null, 0, mock(IBinder.class));
+ bindService(app, client, null, null, 0, mock(IBinder.class));
ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
- bindService(client, client2, null, 0, mock(IBinder.class));
+ bindService(client, client2, null, null, 0, mock(IBinder.class));
client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
- bindService(client2, app, null, 0, mock(IBinder.class));
+ bindService(client2, app, null, null, 0, mock(IBinder.class));
// Note: We add processes to LRU but still call updateOomAdjLocked() with a specific
// processes.
@@ -1599,11 +1610,11 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- bindService(app, client, null, 0, mock(IBinder.class));
- bindService(client, app, null, 0, mock(IBinder.class));
+ bindService(app, client, null, null, 0, mock(IBinder.class));
+ bindService(client, app, null, null, 0, mock(IBinder.class));
ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
- bindService(client2, client, null, 0, mock(IBinder.class));
+ bindService(client2, client, null, null, 0, mock(IBinder.class));
client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app, client, client2);
@@ -1626,11 +1637,11 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- bindService(app, client, null, 0, mock(IBinder.class));
+ bindService(app, client, null, null, 0, mock(IBinder.class));
ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
- bindService(client, client2, null, 0, mock(IBinder.class));
- bindService(client2, client, null, 0, mock(IBinder.class));
+ bindService(client, client2, null, null, 0, mock(IBinder.class));
+ bindService(client2, client, null, null, 0, mock(IBinder.class));
client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app, client, client2);
@@ -1653,18 +1664,18 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- bindService(app, client, null, 0, mock(IBinder.class));
+ bindService(app, client, null, null, 0, mock(IBinder.class));
ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
- bindService(client, client2, null, 0, mock(IBinder.class));
- bindService(client2, client, null, 0, mock(IBinder.class));
+ bindService(client, client2, null, null, 0, mock(IBinder.class));
+ bindService(client2, client, null, null, 0, mock(IBinder.class));
ProcessRecord client3 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
- bindService(client3, client, null, 0, mock(IBinder.class));
+ bindService(client3, client, null, null, 0, mock(IBinder.class));
ProcessRecord client4 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID,
MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
- bindService(client3, client4, null, 0, mock(IBinder.class));
- bindService(client4, client3, null, 0, mock(IBinder.class));
+ bindService(client3, client4, null, null, 0, mock(IBinder.class));
+ bindService(client4, client3, null, null, 0, mock(IBinder.class));
client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app, client, client2, client3, client4);
@@ -1693,16 +1704,16 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- bindService(app, client, null, 0, mock(IBinder.class));
+ bindService(app, client, null, null, 0, mock(IBinder.class));
ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
- bindService(client, client2, null, 0, mock(IBinder.class));
+ bindService(client, client2, null, null, 0, mock(IBinder.class));
client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
- bindService(client2, app, null, 0, mock(IBinder.class));
+ bindService(client2, app, null, null, 0, mock(IBinder.class));
ProcessRecord client3 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
client3.mState.setForcingToImportant(new Object());
- bindService(app, client3, null, 0, mock(IBinder.class));
+ bindService(app, client3, null, null, 0, mock(IBinder.class));
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app, client, client2, client3);
@@ -1718,17 +1729,17 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- bindService(app, client, null, 0, mock(IBinder.class));
+ bindService(app, client, null, null, 0, mock(IBinder.class));
ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
- bindService(client, client2, null, 0, mock(IBinder.class));
- bindService(client2, app, null, 0, mock(IBinder.class));
+ bindService(client, client2, null, null, 0, mock(IBinder.class));
+ bindService(client2, app, null, null, 0, mock(IBinder.class));
WindowProcessController wpc = client2.getWindowProcessController();
doReturn(true).when(wpc).isHomeProcess();
ProcessRecord client3 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
client3.mState.setForcingToImportant(new Object());
- bindService(app, client3, null, 0, mock(IBinder.class));
+ bindService(app, client3, null, null, 0, mock(IBinder.class));
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app, client, client2, client3);
@@ -1743,11 +1754,11 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- bindService(app, client, null, 0, mock(IBinder.class));
+ bindService(app, client, null, null, 0, mock(IBinder.class));
ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
- bindService(client, client2, null, 0, mock(IBinder.class));
- bindService(client2, app, null, 0, mock(IBinder.class));
+ bindService(client, client2, null, null, 0, mock(IBinder.class));
+ bindService(client2, app, null, null, 0, mock(IBinder.class));
WindowProcessController wpc = client2.getWindowProcessController();
doReturn(true).when(wpc).isHomeProcess();
ProcessRecord client3 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
@@ -1755,7 +1766,7 @@ public class MockingOomAdjusterTests {
ProcessRecord client4 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID,
MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
client4.mState.setForcingToImportant(new Object());
- bindService(app, client4, null, 0, mock(IBinder.class));
+ bindService(app, client4, null, null, 0, mock(IBinder.class));
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app, client, client2, client3, client4);
@@ -1770,21 +1781,21 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- bindService(app, client, null, 0, mock(IBinder.class));
+ bindService(app, client, null, null, 0, mock(IBinder.class));
ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
- bindService(client, client2, null, 0, mock(IBinder.class));
- bindService(client2, app, null, 0, mock(IBinder.class));
+ bindService(client, client2, null, null, 0, mock(IBinder.class));
+ bindService(client2, app, null, null, 0, mock(IBinder.class));
WindowProcessController wpc = client2.getWindowProcessController();
doReturn(true).when(wpc).isHomeProcess();
ProcessRecord client3 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
client3.mState.setForcingToImportant(new Object());
- bindService(app, client3, null, 0, mock(IBinder.class));
+ bindService(app, client3, null, null, 0, mock(IBinder.class));
ProcessRecord client4 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID,
MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
client4.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
- bindService(app, client4, null, 0, mock(IBinder.class));
+ bindService(app, client4, null, null, 0, mock(IBinder.class));
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app, client, client2, client3, client4);
@@ -1802,15 +1813,15 @@ public class MockingOomAdjusterTests {
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
WindowProcessController wpc = client.getWindowProcessController();
doReturn(true).when(wpc).isHomeProcess();
- bindService(app, client, null, 0, mock(IBinder.class));
+ bindService(app, client, null, null, 0, mock(IBinder.class));
ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
- bindService(app, client2, null, 0, mock(IBinder.class));
+ bindService(app, client2, null, null, 0, mock(IBinder.class));
client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
ProcessRecord client3 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
client3.mState.setForcingToImportant(new Object());
- bindService(app, client3, null, 0, mock(IBinder.class));
+ bindService(app, client3, null, null, 0, mock(IBinder.class));
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(client, client2, client3, app);
@@ -1826,7 +1837,7 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- bindService(app, client, null, 0, mock(IBinder.class));
+ bindService(app, client, null, null, 0, mock(IBinder.class));
ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
bindProvider(client, client2, null, null, false);
@@ -1846,12 +1857,12 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- bindService(app, client, null, 0, mock(IBinder.class));
+ bindService(app, client, null, null, 0, mock(IBinder.class));
ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
bindProvider(client, client2, null, null, false);
client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
- bindService(client2, app, null, 0, mock(IBinder.class));
+ bindService(client2, app, null, null, 0, mock(IBinder.class));
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app, client, client2);
@@ -1912,9 +1923,9 @@ public class MockingOomAdjusterTests {
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
final ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
- bindService(app1, client1, null, Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
+ bindService(app1, client1, null, null, Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
mock(IBinder.class));
- bindService(app2, client2, null, Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
+ bindService(app2, client2, null, null, Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
mock(IBinder.class));
client1.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
@@ -1929,8 +1940,10 @@ public class MockingOomAdjusterTests {
assertBfsl(app1);
assertBfsl(app2);
- bindService(app1, client1, null, Context.BIND_SCHEDULE_LIKE_TOP_APP, mock(IBinder.class));
- bindService(app2, client2, null, Context.BIND_SCHEDULE_LIKE_TOP_APP, mock(IBinder.class));
+ bindService(app1, client1, null, null, Context.BIND_SCHEDULE_LIKE_TOP_APP,
+ mock(IBinder.class));
+ bindService(app2, client2, null, null, Context.BIND_SCHEDULE_LIKE_TOP_APP,
+ mock(IBinder.class));
updateOomAdj(client1, client2, app1, app2);
assertProcStates(app1, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, VISIBLE_APP_ADJ,
@@ -1946,8 +1959,8 @@ public class MockingOomAdjusterTests {
SCHED_GROUP_DEFAULT);
assertBfsl(app2);
- bindService(client2, app1, null, 0, mock(IBinder.class));
- bindService(app1, client2, null, 0, mock(IBinder.class));
+ bindService(client2, app1, null, null, 0, mock(IBinder.class));
+ bindService(app1, client2, null, null, 0, mock(IBinder.class));
client2.mServices.setHasForegroundServices(false, 0, /* hasNoneType=*/false);
updateOomAdj(app1, client1, client2);
assertProcStates(app1, PROCESS_STATE_IMPORTANT_FOREGROUND, VISIBLE_APP_ADJ,
@@ -1968,9 +1981,9 @@ public class MockingOomAdjusterTests {
client1.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
client2.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
- final ServiceRecord s1 = bindService(app1, client1, null,
+ final ServiceRecord s1 = bindService(app1, client1, null, null,
Context.BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE, mock(IBinder.class));
- final ServiceRecord s2 = bindService(app2, client2, null,
+ final ServiceRecord s2 = bindService(app2, client2, null, null,
Context.BIND_IMPORTANT, mock(IBinder.class));
updateOomAdj(client1, client2, app1, app2);
@@ -1980,7 +1993,7 @@ public class MockingOomAdjusterTests {
assertProcStates(app2, PROCESS_STATE_PERSISTENT, PERSISTENT_SERVICE_ADJ,
SCHED_GROUP_DEFAULT);
- bindService(app2, client1, s2, Context.BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE,
+ bindService(app2, client1, null, s2, Context.BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE,
mock(IBinder.class));
updateOomAdj(app2);
assertProcStates(app2, PROCESS_STATE_PERSISTENT, PERSISTENT_SERVICE_ADJ,
@@ -1995,9 +2008,9 @@ public class MockingOomAdjusterTests {
client1.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
client2.mState.setHasOverlayUi(true);
- bindService(app1, client1, s1, Context.BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE,
+ bindService(app1, client1, null, s1, Context.BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE,
mock(IBinder.class));
- bindService(app2, client2, s2, Context.BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE,
+ bindService(app2, client2, null, s2, Context.BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE,
mock(IBinder.class));
updateOomAdj(client1, client2, app1, app2);
@@ -2030,7 +2043,7 @@ public class MockingOomAdjusterTests {
app1.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
- bindService(app1, client1, null, Context.BIND_NOT_PERCEPTIBLE, mock(IBinder.class));
+ bindService(app1, client1, null, null, Context.BIND_NOT_PERCEPTIBLE, mock(IBinder.class));
updateOomAdj(client1, app1);
@@ -2051,7 +2064,8 @@ public class MockingOomAdjusterTests {
app1.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
- bindService(app1, client1, null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class));
+ bindService(app1, client1, null, null, Context.BIND_ALMOST_PERCEPTIBLE,
+ mock(IBinder.class));
updateOomAdj(client1, app1);
@@ -2121,19 +2135,19 @@ public class MockingOomAdjusterTests {
final ComponentName cn1 = ComponentName.unflattenFromString(
MOCKAPP_PACKAGENAME + "/.TestService");
- final ServiceRecord s1 = bindService(app1, client1, null, 0, mock(IBinder.class));
+ final ServiceRecord s1 = bindService(app1, client1, null, null, 0, mock(IBinder.class));
setFieldValue(ServiceRecord.class, s1, "name", cn1);
s1.startRequested = true;
final ComponentName cn2 = ComponentName.unflattenFromString(
MOCKAPP2_PACKAGENAME + "/.TestService");
- final ServiceRecord s2 = bindService(app2, client2, null, 0, mock(IBinder.class));
+ final ServiceRecord s2 = bindService(app2, client2, null, null, 0, mock(IBinder.class));
setFieldValue(ServiceRecord.class, s2, "name", cn2);
s2.startRequested = true;
final ComponentName cn3 = ComponentName.unflattenFromString(
MOCKAPP5_PACKAGENAME + "/.TestService");
- final ServiceRecord s3 = bindService(app3, client1, null, 0, mock(IBinder.class));
+ final ServiceRecord s3 = bindService(app3, client1, null, null, 0, mock(IBinder.class));
setFieldValue(ServiceRecord.class, s3, "name", cn3);
s3.startRequested = true;
@@ -2177,7 +2191,7 @@ public class MockingOomAdjusterTests {
clientUidRecord.setIdle(true);
doReturn(ActivityManager.APP_START_MODE_DELAYED).when(sService)
.getAppStartModeLOSP(anyInt(), any(String.class), anyInt(),
- anyInt(), anyBoolean(), anyBoolean(), anyBoolean());
+ anyInt(), anyBoolean(), anyBoolean(), anyBoolean());
doNothing().when(sService.mServices)
.scheduleServiceTimeoutLocked(any(ProcessRecord.class));
updateOomAdj(client1, client2, app1, app2, app3);
@@ -2188,7 +2202,7 @@ public class MockingOomAdjusterTests {
} finally {
doCallRealMethod().when(sService)
.getAppStartModeLOSP(anyInt(), any(String.class), anyInt(),
- anyInt(), anyBoolean(), anyBoolean(), anyBoolean());
+ anyInt(), anyBoolean(), anyBoolean(), anyBoolean());
sService.mServices.mServiceMap.clear();
sService.mOomAdjuster.mActiveUids.clear();
}
@@ -2223,7 +2237,7 @@ public class MockingOomAdjusterTests {
ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
app2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
- bindService(app, app2, null, 0, mock(IBinder.class));
+ bindService(app, app2, null, null, 0, mock(IBinder.class));
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app, app2);
@@ -2242,12 +2256,12 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- bindService(app, app2, null, 0, mock(IBinder.class));
+ bindService(app, app2, null, null, 0, mock(IBinder.class));
ProcessRecord app3 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
- bindService(app2, app3, null, 0, mock(IBinder.class));
+ bindService(app2, app3, null, null, 0, mock(IBinder.class));
app3.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
- bindService(app3, app, null, 0, mock(IBinder.class));
+ bindService(app3, app, null, null, 0, mock(IBinder.class));
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app, app2, app3);
@@ -2278,21 +2292,21 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- ServiceRecord s = bindService(app, app2, null, 0, mock(IBinder.class));
+ ServiceRecord s = bindService(app, app2, null, null, 0, mock(IBinder.class));
ProcessRecord app3 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
- bindService(app2, app3, null, 0, mock(IBinder.class));
- bindService(app3, app, null, 0, mock(IBinder.class));
+ bindService(app2, app3, null, null, 0, mock(IBinder.class));
+ bindService(app3, app, null, null, 0, mock(IBinder.class));
WindowProcessController wpc = app3.getWindowProcessController();
doReturn(true).when(wpc).isHomeProcess();
ProcessRecord app4 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
app4.mState.setHasOverlayUi(true);
- bindService(app, app4, s, 0, mock(IBinder.class));
+ bindService(app, app4, null, s, 0, mock(IBinder.class));
ProcessRecord app5 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID,
MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
app5.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
- bindService(app, app5, s, 0, mock(IBinder.class));
+ bindService(app, app5, null, s, 0, mock(IBinder.class));
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app, app2, app3, app4, app5);
@@ -2320,21 +2334,21 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- ServiceRecord s = bindService(app, app2, null, 0, mock(IBinder.class));
+ ServiceRecord s = bindService(app, app2, null, null, 0, mock(IBinder.class));
ProcessRecord app3 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
- bindService(app2, app3, null, 0, mock(IBinder.class));
- bindService(app3, app, null, 0, mock(IBinder.class));
+ bindService(app2, app3, null, null, 0, mock(IBinder.class));
+ bindService(app3, app, null, null, 0, mock(IBinder.class));
WindowProcessController wpc = app3.getWindowProcessController();
doReturn(true).when(wpc).isHomeProcess();
ProcessRecord app4 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
app4.mState.setHasOverlayUi(true);
- bindService(app, app4, s, 0, mock(IBinder.class));
+ bindService(app, app4, null, s, 0, mock(IBinder.class));
ProcessRecord app5 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID,
MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
app5.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
- bindService(app, app5, s, 0, mock(IBinder.class));
+ bindService(app, app5, null, s, 0, mock(IBinder.class));
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app5, app4, app3, app2, app);
@@ -2362,21 +2376,21 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- ServiceRecord s = bindService(app, app2, null, 0, mock(IBinder.class));
+ ServiceRecord s = bindService(app, app2, null, null, 0, mock(IBinder.class));
ProcessRecord app3 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
- bindService(app2, app3, null, 0, mock(IBinder.class));
- bindService(app3, app, null, 0, mock(IBinder.class));
+ bindService(app2, app3, null, null, 0, mock(IBinder.class));
+ bindService(app3, app, null, null, 0, mock(IBinder.class));
WindowProcessController wpc = app3.getWindowProcessController();
doReturn(true).when(wpc).isHomeProcess();
ProcessRecord app4 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
app4.mState.setHasOverlayUi(true);
- bindService(app, app4, s, 0, mock(IBinder.class));
+ bindService(app, app4, null, s, 0, mock(IBinder.class));
ProcessRecord app5 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID,
MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
app5.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
- bindService(app, app5, s, 0, mock(IBinder.class));
+ bindService(app, app5, null, s, 0, mock(IBinder.class));
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app3, app4, app2, app, app5);
@@ -2404,15 +2418,19 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
- bindService(app, client, null, Context.BIND_INCLUDE_CAPABILITIES, mock(IBinder.class));
+ bindService(app, client, null, null, Context.BIND_INCLUDE_CAPABILITIES,
+ mock(IBinder.class));
ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
- bindService(client, client2, null, Context.BIND_INCLUDE_CAPABILITIES, mock(IBinder.class));
- bindService(client2, app, null, Context.BIND_INCLUDE_CAPABILITIES, mock(IBinder.class));
+ bindService(client, client2, null, null, Context.BIND_INCLUDE_CAPABILITIES,
+ mock(IBinder.class));
+ bindService(client2, app, null, null, Context.BIND_INCLUDE_CAPABILITIES,
+ mock(IBinder.class));
ProcessRecord client3 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
client3.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
- bindService(app, client3, null, Context.BIND_INCLUDE_CAPABILITIES, mock(IBinder.class));
+ bindService(app, client3, null, null, Context.BIND_INCLUDE_CAPABILITIES,
+ mock(IBinder.class));
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app, client, client2, client3);
@@ -2472,10 +2490,10 @@ public class MockingOomAdjusterTests {
ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
long now = SystemClock.uptimeMillis();
- ServiceRecord s = bindService(app, app2, null, 0, mock(IBinder.class));
+ ServiceRecord s = bindService(app, app2, null, null, 0, mock(IBinder.class));
s.startRequested = true;
s.lastActivity = now;
- s = bindService(app2, app, null, 0, mock(IBinder.class));
+ s = bindService(app2, app, null, null, 0, mock(IBinder.class));
s.startRequested = true;
s.lastActivity = now;
ProcessRecord app3 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
@@ -2507,11 +2525,11 @@ public class MockingOomAdjusterTests {
final int userOwner = 0;
final int userOther = 1;
final int cachedAdj1 = sService.mConstants.USE_TIERED_CACHED_ADJ
- ? CACHED_APP_MIN_ADJ + 10
- : CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
+ ? CACHED_APP_MIN_ADJ + 10
+ : CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
final int cachedAdj2 = sService.mConstants.USE_TIERED_CACHED_ADJ
- ? CACHED_APP_MIN_ADJ + 10
- : cachedAdj1 + ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;
+ ? CACHED_APP_MIN_ADJ + 10
+ : cachedAdj1 + ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;
doReturn(userOwner).when(sService.mUserController).getCurrentUserId();
final ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();
@@ -2534,7 +2552,6 @@ public class MockingOomAdjusterTests {
s.startRequested = true;
s.lastActivity = now;
- app.mState.setCached(false);
app.mServices.startService(s);
app.mState.setHasShownUi(true);
@@ -2547,7 +2564,6 @@ public class MockingOomAdjusterTests {
s2.startRequested = true;
s2.lastActivity = now - sService.mConstants.MAX_SERVICE_INACTIVITY - 1;
- app2.mState.setCached(false);
app2.mServices.startService(s2);
app2.mState.setHasShownUi(false);
@@ -2565,7 +2581,6 @@ public class MockingOomAdjusterTests {
assertProcStates(app, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services");
- app.mState.setCached(false);
app.mState.setSetProcState(PROCESS_STATE_NONEXISTENT);
app.mState.setAdjType(null);
app.mState.setSetAdj(UNKNOWN_ADJ);
@@ -2593,7 +2608,6 @@ public class MockingOomAdjusterTests {
assertProcStates(app, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services");
assertProcStates(app2, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services");
- app.mState.setCached(true);
app.mState.setSetProcState(PROCESS_STATE_NONEXISTENT);
app.mState.setAdjType(null);
app.mState.setSetAdj(UNKNOWN_ADJ);
@@ -2626,7 +2640,7 @@ public class MockingOomAdjusterTests {
// Simulate binding to a service in the same process using BIND_ABOVE_CLIENT and
// verify that its OOM adjustment level is unaffected.
- bindService(app, app, null, Context.BIND_ABOVE_CLIENT, mock(IBinder.class));
+ bindService(app, app, null, null, Context.BIND_ABOVE_CLIENT, mock(IBinder.class));
app.mServices.updateHasAboveClientLocked();
assertFalse(app.mServices.hasAboveClient());
@@ -2644,12 +2658,12 @@ public class MockingOomAdjusterTests {
final ProcessRecord app3 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
long now = SystemClock.uptimeMillis();
- ServiceRecord s = bindService(app, app2, null, 0, mock(IBinder.class));
+ ServiceRecord s = bindService(app, app2, null, null, 0, mock(IBinder.class));
s.startRequested = true;
s.lastActivity = now;
- s = bindService(app2, app3, null, 0, mock(IBinder.class));
+ s = bindService(app2, app3, null, null, 0, mock(IBinder.class));
s.lastActivity = now;
- s = bindService(app3, app2, null, 0, mock(IBinder.class));
+ s = bindService(app3, app2, null, null, 0, mock(IBinder.class));
s.lastActivity = now;
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
@@ -2678,7 +2692,7 @@ public class MockingOomAdjusterTests {
// Start binding to a service that isn't running yet.
ServiceRecord sr = makeServiceRecord(app);
sr.app = null;
- bindService(null, app, sr, Context.BIND_ABOVE_CLIENT, mock(IBinder.class));
+ bindService(null, app, null, sr, Context.BIND_ABOVE_CLIENT, mock(IBinder.class));
// Since sr.app is null, this service cannot be in the same process as the
// client so we expect the BIND_ABOVE_CLIENT adjustment to take effect.
@@ -2772,91 +2786,37 @@ public class MockingOomAdjusterTests {
ApplicationExitInfo.SUBREASON_ISOLATED_NOT_NEEDED, true);
}
+ @SuppressWarnings("GuardedBy")
+ @Test
+ public void testUpdateOomAdj_DoAll_SdkSandbox_attributedClient() {
+ ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
+ MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
+ ProcessRecord attributedClient = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
+ MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, true));
+ ProcessRecord sandboxService = spy(new ProcessRecordBuilder(MOCKAPP_PID,
+ MOCKAPP_SDK_SANDBOX_UID, MOCKAPP_SDK_SANDBOX_PROCESSNAME, MOCKAPP_PACKAGENAME)
+ .setSdkSandboxClientAppPackage(MOCKAPP3_PACKAGENAME)
+ .build());
+
+ setProcessesToLru(sandboxService, client, attributedClient);
+
+ client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
+ attributedClient.mServices.setHasForegroundServices(true, 0, true);
+ bindService(sandboxService, client, attributedClient, null, 0, mock(IBinder.class));
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ updateOomAdj();
+ assertProcStates(client, PROCESS_STATE_PERSISTENT, PERSISTENT_PROC_ADJ,
+ SCHED_GROUP_DEFAULT);
+ assertProcStates(attributedClient, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
+ SCHED_GROUP_DEFAULT);
+ assertProcStates(sandboxService, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
+ SCHED_GROUP_DEFAULT);
+ }
+
private ProcessRecord makeDefaultProcessRecord(int pid, int uid, String processName,
String packageName, boolean hasShownUi) {
- long now = SystemClock.uptimeMillis();
- return makeProcessRecord(sService, pid, uid, processName,
- packageName, 12345, Build.VERSION_CODES.CUR_DEVELOPMENT,
- now, now, now, 12345, UNKNOWN_ADJ, UNKNOWN_ADJ,
- UNKNOWN_ADJ, CACHED_APP_MAX_ADJ,
- SCHED_GROUP_DEFAULT, SCHED_GROUP_DEFAULT,
- PROCESS_STATE_NONEXISTENT, PROCESS_STATE_NONEXISTENT,
- 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, Long.MIN_VALUE, Long.MIN_VALUE, true, 0, null, false);
- }
-
- private ProcessRecord makeProcessRecord(ActivityManagerService service, int pid, int uid,
- String processName, String packageName, long versionCode, int targetSdkVersion,
- long lastActivityTime, long lastPssTime, long nextPssTime, long lastPss, int maxAdj,
- int setRawAdj, int curAdj, int setAdj, int curSchedGroup, int setSchedGroup,
- int curProcState, int repProcState, int curRawProcState, int setProcState,
- int connectionGroup, int connectionImportance, boolean serviceb,
- boolean hasClientActivities, boolean hasForegroundServices, int fgServiceTypes,
- boolean hasForegroundActivities, boolean repForegroundActivities, boolean systemNoUi,
- boolean hasShownUi, boolean hasTopUi, boolean hasOverlayUi,
- boolean runningRemoteAnimation, boolean hasAboveClient, boolean treatLikeActivity,
- boolean killedByAm, Object forcingToImportant, int numOfCurReceivers,
- long lastProviderTime, long lastTopTime, boolean cached, int numOfExecutingServices,
- String isolatedEntryPoint, boolean execServicesFg) {
- ApplicationInfo ai = spy(new ApplicationInfo());
- ai.uid = uid;
- ai.packageName = packageName;
- ai.longVersionCode = versionCode;
- ai.targetSdkVersion = targetSdkVersion;
- ProcessRecord app = new ProcessRecord(service, ai, processName, uid);
- final ProcessStateRecord state = app.mState;
- final ProcessServiceRecord services = app.mServices;
- final ProcessReceiverRecord receivers = app.mReceivers;
- final ProcessProfileRecord profile = app.mProfile;
- final ProcessProviderRecord providers = app.mProviders;
- app.makeActive(mock(IApplicationThread.class), sService.mProcessStats);
- app.setLastActivityTime(lastActivityTime);
- app.setKilledByAm(killedByAm);
- app.setIsolatedEntryPoint(isolatedEntryPoint);
- setFieldValue(ProcessRecord.class, app, "mWindowProcessController",
- mock(WindowProcessController.class));
- profile.setLastPssTime(lastPssTime);
- profile.setNextPssTime(nextPssTime);
- profile.setLastPss(lastPss);
- state.setMaxAdj(maxAdj);
- state.setSetRawAdj(setRawAdj);
- state.setCurAdj(curAdj);
- state.setSetAdj(setAdj);
- state.setCurrentSchedulingGroup(curSchedGroup);
- state.setSetSchedGroup(setSchedGroup);
- state.setCurProcState(curProcState);
- state.setReportedProcState(repProcState);
- state.setCurRawProcState(curRawProcState);
- state.setSetProcState(setProcState);
- state.setServiceB(serviceb);
- state.setRepForegroundActivities(repForegroundActivities);
- state.setHasForegroundActivities(hasForegroundActivities);
- state.setSystemNoUi(systemNoUi);
- state.setHasShownUi(hasShownUi);
- state.setHasTopUi(hasTopUi);
- state.setRunningRemoteAnimation(runningRemoteAnimation);
- state.setHasOverlayUi(hasOverlayUi);
- state.setCached(cached);
- state.setLastTopTime(lastTopTime);
- state.setForcingToImportant(forcingToImportant);
- services.setConnectionGroup(connectionGroup);
- services.setConnectionImportance(connectionImportance);
- services.setHasClientActivities(hasClientActivities);
- services.setHasForegroundServices(hasForegroundServices, fgServiceTypes,
- /* hasNoneType=*/false);
- services.setHasAboveClient(hasAboveClient);
- services.setTreatLikeActivity(treatLikeActivity);
- services.setExecServicesFg(execServicesFg);
- for (int i = 0; i < numOfExecutingServices; i++) {
- services.startExecutingService(mock(ServiceRecord.class));
- }
- for (int i = 0; i < numOfCurReceivers; i++) {
- receivers.addCurReceiver(mock(BroadcastRecord.class));
- }
- providers.setLastProviderTime(lastProviderTime);
- return app;
+ return new ProcessRecordBuilder(pid, uid, processName, packageName).setHasShownUi(
+ hasShownUi).build();
}
private ServiceRecord makeServiceRecord(ProcessRecord app) {
@@ -2870,6 +2830,7 @@ public class MockingOomAdjusterTests {
record.appInfo = app.info;
setFieldValue(ServiceRecord.class, record, "bindings", new ArrayMap<>());
setFieldValue(ServiceRecord.class, record, "pendingStarts", new ArrayList<>());
+ setFieldValue(ServiceRecord.class, record, "isSdkSandbox", app.isSdkSandbox);
return record;
}
@@ -2892,11 +2853,11 @@ public class MockingOomAdjusterTests {
}
private ServiceRecord bindService(ProcessRecord service, ProcessRecord client,
- ServiceRecord record, long bindFlags, IBinder binder) {
+ ProcessRecord attributedClient, ServiceRecord record, long bindFlags, IBinder binder) {
if (record == null) {
record = makeServiceRecord(service);
}
- AppBindRecord binding = new AppBindRecord(record, null, client, null);
+ AppBindRecord binding = new AppBindRecord(record, null, client, attributedClient);
ConnectionRecord cr = spy(new ConnectionRecord(binding,
mock(ActivityServiceConnectionsHolder.class),
mock(IServiceConnection.class), bindFlags,
@@ -2961,4 +2922,139 @@ public class MockingOomAdjusterTests {
assertBfsl(app);
}
}
+
+ private static class ProcessRecordBuilder {
+ @SuppressWarnings("UnusedVariable")
+ int mPid;
+ int mUid;
+ String mProcessName;
+ String mPackageName;
+ long mVersionCode = 12345;
+ int mTargetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
+ long mLastActivityTime;
+ long mLastPssTime;
+ long mNextPssTime;
+ long mLastPss = 12345;
+ int mMaxAdj = UNKNOWN_ADJ;
+ int mSetRawAdj = UNKNOWN_ADJ;
+ int mCurAdj = UNKNOWN_ADJ;
+ int mSetAdj = CACHED_APP_MAX_ADJ;
+ int mCurSchedGroup = SCHED_GROUP_DEFAULT;
+ int mSetSchedGroup = SCHED_GROUP_DEFAULT;
+ int mCurProcState = PROCESS_STATE_NONEXISTENT;
+ int mRepProcState = PROCESS_STATE_NONEXISTENT;
+ int mCurRawProcState = PROCESS_STATE_NONEXISTENT;
+ int mSetProcState = PROCESS_STATE_NONEXISTENT;
+ int mConnectionGroup = 0;
+ int mConnectionImportance = 0;
+ boolean mServiceb = false;
+ boolean mHasClientActivities = false;
+ boolean mHasForegroundServices = false;
+ int mFgServiceTypes = ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE;
+ boolean mHasForegroundActivities = false;
+ boolean mRepForegroundActivities = false;
+ boolean mSystemNoUi = false;
+ boolean mHasShownUi = false;
+ boolean mHasTopUi = false;
+ boolean mHasOverlayUi = false;
+ boolean mRunningRemoteAnimation = false;
+ boolean mHasAboveClient = false;
+ boolean mTreatLikeActivity = false;
+ boolean mKilledByAm = false;
+ Object mForcingToImportant;
+ int mNumOfCurReceivers = 0;
+ long mLastProviderTime = Long.MIN_VALUE;
+ long mLastTopTime = Long.MIN_VALUE;
+ boolean mCached = true;
+ int mNumOfExecutingServices = 0;
+ String mIsolatedEntryPoint = null;
+ boolean mExecServicesFg = false;
+ String mSdkSandboxClientAppPackage = null;
+
+ ProcessRecordBuilder(int pid, int uid, String processName, String packageName) {
+ mPid = pid;
+ mUid = uid;
+ mProcessName = processName;
+ mPackageName = packageName;
+
+ long now = SystemClock.uptimeMillis();
+ mLastActivityTime = now;
+ mLastPssTime = now;
+ mNextPssTime = now;
+ }
+
+ ProcessRecordBuilder setHasShownUi(boolean hasShownUi) {
+ mHasShownUi = hasShownUi;
+ return this;
+ }
+
+ ProcessRecordBuilder setSdkSandboxClientAppPackage(String sdkSandboxClientAppPackage) {
+ mSdkSandboxClientAppPackage = sdkSandboxClientAppPackage;
+ return this;
+ }
+
+ @SuppressWarnings("GuardedBy")
+ public ProcessRecord build() {
+ ApplicationInfo ai = spy(new ApplicationInfo());
+ ai.uid = mUid;
+ ai.packageName = mPackageName;
+ ai.longVersionCode = mVersionCode;
+ ai.targetSdkVersion = mTargetSdkVersion;
+ doCallRealMethod().when(sService).getPackageManagerInternal();
+ doReturn(null).when(sPackageManagerInternal).getApplicationInfo(
+ eq(mSdkSandboxClientAppPackage), anyLong(), anyInt(), anyInt());
+ ProcessRecord app = new ProcessRecord(sService, ai, mProcessName, mUid,
+ mSdkSandboxClientAppPackage, -1, null);
+ final ProcessStateRecord state = app.mState;
+ final ProcessServiceRecord services = app.mServices;
+ final ProcessReceiverRecord receivers = app.mReceivers;
+ final ProcessProfileRecord profile = app.mProfile;
+ final ProcessProviderRecord providers = app.mProviders;
+ app.makeActive(mock(IApplicationThread.class), sService.mProcessStats);
+ app.setLastActivityTime(mLastActivityTime);
+ app.setKilledByAm(mKilledByAm);
+ app.setIsolatedEntryPoint(mIsolatedEntryPoint);
+ setFieldValue(ProcessRecord.class, app, "mWindowProcessController",
+ mock(WindowProcessController.class));
+ profile.setLastPssTime(mLastPssTime);
+ profile.setNextPssTime(mNextPssTime);
+ profile.setLastPss(mLastPss);
+ state.setMaxAdj(mMaxAdj);
+ state.setSetRawAdj(mSetRawAdj);
+ state.setCurAdj(mCurAdj);
+ state.setSetAdj(mSetAdj);
+ state.setCurrentSchedulingGroup(mCurSchedGroup);
+ state.setSetSchedGroup(mSetSchedGroup);
+ state.setCurProcState(mCurProcState);
+ state.setReportedProcState(mRepProcState);
+ state.setCurRawProcState(mCurRawProcState);
+ state.setSetProcState(mSetProcState);
+ state.setServiceB(mServiceb);
+ state.setRepForegroundActivities(mRepForegroundActivities);
+ state.setHasForegroundActivities(mHasForegroundActivities);
+ state.setSystemNoUi(mSystemNoUi);
+ state.setHasShownUi(mHasShownUi);
+ state.setHasTopUi(mHasTopUi);
+ state.setRunningRemoteAnimation(mRunningRemoteAnimation);
+ state.setHasOverlayUi(mHasOverlayUi);
+ state.setLastTopTime(mLastTopTime);
+ state.setForcingToImportant(mForcingToImportant);
+ services.setConnectionGroup(mConnectionGroup);
+ services.setConnectionImportance(mConnectionImportance);
+ services.setHasClientActivities(mHasClientActivities);
+ services.setHasForegroundServices(mHasForegroundServices, mFgServiceTypes,
+ /* hasNoneType=*/false);
+ services.setHasAboveClient(mHasAboveClient);
+ services.setTreatLikeActivity(mTreatLikeActivity);
+ services.setExecServicesFg(mExecServicesFg);
+ for (int i = 0; i < mNumOfExecutingServices; i++) {
+ services.startExecutingService(mock(ServiceRecord.class));
+ }
+ for (int i = 0; i < mNumOfCurReceivers; i++) {
+ receivers.addCurReceiver(mock(BroadcastRecord.class));
+ }
+ providers.setLastProviderTime(mLastProviderTime);
+ return app;
+ }
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java
index 2f12a3b858d2..709a8049719c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java
@@ -21,7 +21,6 @@ import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE;
import static android.app.ActivityManager.PROCESS_STATE_CACHED_EMPTY;
import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
import static android.app.ActivityManager.PROCESS_STATE_HOME;
-import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
import static android.app.ActivityManager.PROCESS_STATE_SERVICE;
import static android.content.Context.BIND_AUTO_CREATE;
import static android.content.Context.BIND_INCLUDE_CAPABILITIES;
@@ -30,10 +29,10 @@ import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SYSTEM_EXEM
import static android.os.UserHandle.USER_SYSTEM;
import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+import static com.android.server.am.ProcessList.CACHED_APP_MIN_ADJ;
import static com.android.server.am.ProcessList.HOME_APP_ADJ;
import static com.android.server.am.ProcessList.PERCEPTIBLE_APP_ADJ;
import static com.android.server.am.ProcessList.SERVICE_ADJ;
-import static com.android.server.am.ProcessList.CACHED_APP_MIN_ADJ;
import static org.junit.Assert.assertNotEquals;
import static org.mockito.ArgumentMatchers.any;
@@ -47,8 +46,8 @@ import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -582,7 +581,6 @@ public final class ServiceBindingOomAdjPolicyTest {
app.mState.setSetAdj(adj);
app.mState.setCurCapability(cap);
app.mState.setSetCapability(cap);
- app.mState.setCached(procState >= PROCESS_STATE_LAST_ACTIVITY || adj >= CACHED_APP_MIN_ADJ);
return app;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
index 940469f89b16..414532b88e22 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
@@ -29,16 +29,20 @@ import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
import android.app.backup.BackupTransport;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
+import android.os.Build;
import android.os.Message;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.DeviceConfig;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.modules.utils.testing.TestableDeviceConfig;
+import com.android.server.backup.Flags;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.internal.BackupHandler;
import com.android.server.backup.transport.BackupTransportClient;
@@ -56,10 +60,12 @@ import org.mockito.MockitoAnnotations;
import org.mockito.stubbing.Answer;
import java.util.ArrayDeque;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
@@ -74,10 +80,17 @@ public class PerformUnifiedRestoreTaskTest {
private static final String SYSTEM_PACKAGE_NAME = "android";
private static final String NON_SYSTEM_PACKAGE_NAME = "package";
- @Mock private BackupDataInput mBackupDataInput;
- @Mock private BackupDataOutput mBackupDataOutput;
- @Mock private UserBackupManagerService mBackupManagerService;
- @Mock private TransportConnection mTransportConnection;
+ private static final String V_TO_U_ALLOWLIST = "pkg1";
+ private static final String V_TO_U_DENYLIST = "pkg2";
+
+ @Mock
+ private BackupDataInput mBackupDataInput;
+ @Mock
+ private BackupDataOutput mBackupDataOutput;
+ @Mock
+ private UserBackupManagerService mBackupManagerService;
+ @Mock
+ private TransportConnection mTransportConnection;
private Set<String> mExcludedkeys = new HashSet<>();
private Map<String, String> mBackupData = new HashMap<>();
@@ -91,6 +104,10 @@ public class PerformUnifiedRestoreTaskTest {
public TestableDeviceConfig.TestableDeviceConfigRule mDeviceConfigRule =
new TestableDeviceConfig.TestableDeviceConfigRule();
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+
private Context mContext;
@Before
@@ -118,7 +135,8 @@ public class PerformUnifiedRestoreTaskTest {
return null;
});
- mRestoreTask = new PerformUnifiedRestoreTask(mBackupManagerService, mTransportConnection);
+ mRestoreTask = new PerformUnifiedRestoreTask(mBackupManagerService, mTransportConnection,
+ V_TO_U_ALLOWLIST, V_TO_U_DENYLIST);
}
private void populateTestData() {
@@ -235,6 +253,122 @@ public class PerformUnifiedRestoreTaskTest {
== UnifiedRestoreState.FINAL);
}
+ @Test
+ public void testCreateVToUList_listSettingIsNull_returnEmptyList() {
+ List<String> expectedEmptyList = new ArrayList<>();
+
+ List<String> list = mRestoreTask.createVToUList(null);
+
+ assertEquals(list, expectedEmptyList);
+ }
+
+ @Test
+ public void testCreateVToUList_listIsNotNull_returnCorrectList() {
+ List<String> expectedList = Arrays.asList("a", "b", "c");
+ String listString = "a,b,c";
+
+ List<String> list = mRestoreTask.createVToUList(listString);
+
+ assertEquals(list, expectedList);
+ }
+
+ @Test
+ public void testIsVToUDowngrade_vToUFlagIsOffAndTargetIsUSourceIsV_returnFalse() {
+ mSetFlagsRule.disableFlags(
+ Flags.FLAG_ENABLE_V_TO_U_RESTORE_FOR_SYSTEM_COMPONENTS_IN_ALLOWLIST);
+
+ boolean isVToUDowngrade = mRestoreTask.isVToUDowngrade(
+ Build.VERSION_CODES.VANILLA_ICE_CREAM, Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
+
+ assertFalse(isVToUDowngrade);
+ }
+
+ @Test
+ public void testIsVToUDowngrade_vToUFlagIsOnAndTargetIsUSourceIsV_returnTrue() {
+ mSetFlagsRule.enableFlags(
+ Flags.FLAG_ENABLE_V_TO_U_RESTORE_FOR_SYSTEM_COMPONENTS_IN_ALLOWLIST);
+
+ boolean isVToUDowngrade = mRestoreTask.isVToUDowngrade(
+ Build.VERSION_CODES.VANILLA_ICE_CREAM, Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
+
+ assertTrue(isVToUDowngrade);
+ }
+
+ @Test
+ public void testIsVToUDowngrade_vToUFlagIsOnAndSourceIsNotV_returnFalse() {
+ mSetFlagsRule.enableFlags(
+ Flags.FLAG_ENABLE_V_TO_U_RESTORE_FOR_SYSTEM_COMPONENTS_IN_ALLOWLIST);
+
+ boolean isVToUDowngrade = mRestoreTask.isVToUDowngrade(Build.VERSION_CODES.UPSIDE_DOWN_CAKE,
+ Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
+
+ assertFalse(isVToUDowngrade);
+ }
+
+ @Test
+ public void testIsVToUDowngrade_vToUFlagIsOnAndTargetIsNotU_returnFalse() {
+ mSetFlagsRule.enableFlags(
+ Flags.FLAG_ENABLE_V_TO_U_RESTORE_FOR_SYSTEM_COMPONENTS_IN_ALLOWLIST);
+
+ boolean isVToUDowngrade = mRestoreTask.isVToUDowngrade(
+ Build.VERSION_CODES.VANILLA_ICE_CREAM, Build.VERSION_CODES.VANILLA_ICE_CREAM);
+
+ assertFalse(isVToUDowngrade);
+ }
+
+
+ @Test
+ public void testIsEligibleForVToUDowngrade_pkgIsNotOnAllowlist_returnFalse() {
+ PackageInfo testPackageInfo = new PackageInfo();
+ testPackageInfo.packageName = "pkg";
+ testPackageInfo.applicationInfo = new ApplicationInfo();
+ // restoreAnyVersion flag is off
+ testPackageInfo.applicationInfo.flags = 0;
+
+ boolean eligibilityCriteria = mRestoreTask.isPackageEligibleForVToURestore(testPackageInfo);
+
+ assertFalse(eligibilityCriteria);
+ }
+
+ @Test
+ public void testIsEligibleForVToUDowngrade_pkgIsOnAllowlist_returnTrue() {
+ PackageInfo testPackageInfo = new PackageInfo();
+ testPackageInfo.packageName = "pkg1";
+ testPackageInfo.applicationInfo = new ApplicationInfo();
+ // restoreAnyVersion flag is off
+ testPackageInfo.applicationInfo.flags = 0;
+
+ boolean eligibilityCriteria = mRestoreTask.isPackageEligibleForVToURestore(testPackageInfo);
+
+ assertTrue(eligibilityCriteria);
+ }
+
+ @Test
+ public void testIsEligibleForVToUDowngrade_pkgIsNotOnDenyList_returnTrue() {
+ PackageInfo testPackageInfo = new PackageInfo();
+ testPackageInfo.packageName = "pkg";
+ testPackageInfo.applicationInfo = new ApplicationInfo();
+ // restoreAnyVersion flag is on
+ testPackageInfo.applicationInfo.flags = ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
+
+ boolean eligibilityCriteria = mRestoreTask.isPackageEligibleForVToURestore(testPackageInfo);
+
+ assertTrue(eligibilityCriteria);
+ }
+
+ @Test
+ public void testIsEligibleForVToUDowngrade_pkgIsOnDenyList_returnFalse() {
+ PackageInfo testPackageInfo = new PackageInfo();
+ testPackageInfo.packageName = "pkg2";
+ testPackageInfo.applicationInfo = new ApplicationInfo();
+ // restoreAnyVersion flag is on
+ testPackageInfo.applicationInfo.flags = ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
+
+ boolean eligibilityCriteria = mRestoreTask.isPackageEligibleForVToURestore(testPackageInfo);
+
+ assertFalse(eligibilityCriteria);
+ }
+
private void setupForRestoreKeyValueState(int transportStatus)
throws RemoteException, TransportNotAvailableException {
// Mock BackupHandler to do nothing when executeNextState() is called
diff --git a/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java b/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
index a8faa54fe9d6..ad68de84eace 100644
--- a/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
@@ -196,6 +196,9 @@ public class BatterySaverStateMachineTest {
when(mMockResources.getBoolean(
com.android.internal.R.bool.config_batterySaverStickyBehaviourDisabled))
.thenReturn(false);
+ when(mMockResources.getBoolean(
+ com.android.internal.R.bool.config_batterySaverTurnedOffNotificationEnabled))
+ .thenReturn(true);
when(mMockResources.getInteger(
com.android.internal.R.integer.config_dynamicPowerSavingsDefaultDisableThreshold))
.thenReturn(80);
diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java b/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
index a14073006c31..d6e246fc7ee1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
@@ -23,7 +23,13 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -33,14 +39,17 @@ import android.content.pm.VersionedPackage;
import android.content.rollback.PackageRollbackInfo;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
-import android.util.Log;
-import android.util.Xml;
+import android.crashrecovery.flags.Flags;
+import android.os.Handler;
+import android.os.MessageQueue;
+import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.runner.AndroidJUnit4;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.server.PackageWatchdog;
import com.android.server.SystemConfig;
+import com.android.server.pm.ApexManager;
import org.junit.After;
import org.junit.Before;
@@ -49,18 +58,16 @@ import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.mockito.Answers;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;
-import org.xmlpull.v1.XmlPullParser;
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
+import java.time.Duration;
import java.util.List;
-import java.util.Scanner;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
@@ -78,10 +85,18 @@ public class RollbackPackageHealthObserverTest {
@Mock
PackageManager mMockPackageManager;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private ApexManager mApexManager;
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private MockitoSession mSession;
private static final String APP_A = "com.package.a";
private static final String APP_B = "com.package.b";
+ private static final String APP_C = "com.package.c";
private static final long VERSION_CODE = 1L;
+ private static final long VERSION_CODE_2 = 2L;
private static final String LOG_TAG = "RollbackPackageHealthObserverTest";
private SystemConfig mSysConfig;
@@ -101,7 +116,6 @@ public class RollbackPackageHealthObserverTest {
// Mock PackageWatchdog
doAnswer((Answer<PackageWatchdog>) invocationOnMock -> mMockPackageWatchdog)
.when(() -> PackageWatchdog.getInstance(mMockContext));
-
}
@After
@@ -121,7 +135,7 @@ public class RollbackPackageHealthObserverTest {
@Test
public void testHealthCheckLevels() {
RollbackPackageHealthObserver observer =
- spy(new RollbackPackageHealthObserver(mMockContext));
+ spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
VersionedPackage testFailedPackage = new VersionedPackage(APP_A, VERSION_CODE);
VersionedPackage secondFailedPackage = new VersionedPackage(APP_B, VERSION_CODE);
@@ -165,14 +179,14 @@ public class RollbackPackageHealthObserverTest {
@Test
public void testIsPersistent() {
RollbackPackageHealthObserver observer =
- spy(new RollbackPackageHealthObserver(mMockContext));
+ spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
assertTrue(observer.isPersistent());
}
@Test
public void testMayObservePackage_withoutAnyRollback() {
RollbackPackageHealthObserver observer =
- spy(new RollbackPackageHealthObserver(mMockContext));
+ spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of());
assertFalse(observer.mayObservePackage(APP_A));
@@ -182,7 +196,7 @@ public class RollbackPackageHealthObserverTest {
public void testMayObservePackage_forPersistentApp()
throws PackageManager.NameNotFoundException {
RollbackPackageHealthObserver observer =
- spy(new RollbackPackageHealthObserver(mMockContext));
+ spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
ApplicationInfo info = new ApplicationInfo();
info.flags = ApplicationInfo.FLAG_PERSISTENT | ApplicationInfo.FLAG_SYSTEM;
when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
@@ -197,7 +211,7 @@ public class RollbackPackageHealthObserverTest {
public void testMayObservePackage_forNonPersistentApp()
throws PackageManager.NameNotFoundException {
RollbackPackageHealthObserver observer =
- spy(new RollbackPackageHealthObserver(mMockContext));
+ spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(mRollbackInfo));
when(mRollbackInfo.getPackages()).thenReturn(List.of(mPackageRollbackInfo));
@@ -208,96 +222,720 @@ public class RollbackPackageHealthObserverTest {
}
/**
- * Test that isAutomaticRollbackDenied works correctly when packages that are not
- * denied are sent.
+ * Test that when impactLevel is low returns user impact level 70
*/
@Test
- public void isRollbackAllowedTest_false() throws IOException {
- final String contents =
- "<config>\n"
- + " <automatic-rollback-denylisted-app package=\"com.android.vending\" />\n"
- + "</config>";
- final File folder = createTempSubfolder("folder");
- createTempFile(folder, "automatic-rollback-denylisted-app.xml", contents);
-
- readPermissions(folder, /* Grant all permission flags */ ~0);
-
- assertThat(RollbackPackageHealthObserver.isAutomaticRollbackDenied(mSysConfig,
- new VersionedPackage("com.test.package", 1))).isEqualTo(false);
+ public void healthCheckFailed_impactLevelLow_onePackage()
+ throws PackageManager.NameNotFoundException {
+ mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
+ VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
+ VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
+ PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo,
+ null, null , false, false,
+ null);
+ RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo),
+ false, null, 111,
+ PackageManager.ROLLBACK_USER_IMPACT_LOW);
+ RollbackPackageHealthObserver observer =
+ spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ VersionedPackage secondFailedPackage = new VersionedPackage(APP_B, VERSION_CODE);
+
+ when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
+ // Make the rollbacks available
+ when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1));
+ when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+ when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null);
+
+ assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_70,
+ observer.onHealthCheckFailed(secondFailedPackage,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 1));
}
/**
- * Test that isAutomaticRollbackDenied works correctly when packages that are
- * denied are sent.
+ * HealthCheckFailed should only return low impact rollbacks. High impact rollbacks are only
+ * for bootloop.
*/
@Test
- public void isRollbackAllowedTest_true() throws IOException {
- final String contents =
- "<config>\n"
- + " <automatic-rollback-denylisted-app package=\"com.android.vending\" />\n"
- + "</config>";
- final File folder = createTempSubfolder("folder");
- createTempFile(folder, "automatic-rollback-denylisted-app.xml", contents);
-
- readPermissions(folder, /* Grant all permission flags */ ~0);
-
- assertThat(RollbackPackageHealthObserver.isAutomaticRollbackDenied(mSysConfig,
- new VersionedPackage("com.android.vending", 1))).isEqualTo(true);
+ public void healthCheckFailed_impactLevelHigh_onePackage()
+ throws PackageManager.NameNotFoundException {
+ mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
+ VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
+ VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
+ PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo,
+ null, null, false, false,
+ null);
+ RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo),
+ false, null, 111,
+ PackageManager.ROLLBACK_USER_IMPACT_HIGH);
+ RollbackPackageHealthObserver observer =
+ spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ VersionedPackage secondFailedPackage = new VersionedPackage(APP_B, VERSION_CODE);
+
+ when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
+ // Make the rollbacks available
+ when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1));
+ when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+ when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null);
+
+ assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_0,
+ observer.onHealthCheckFailed(secondFailedPackage,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 1));
}
/**
- * Test that isAutomaticRollbackDenied works correctly when no config is present
+ * When the rollback impact level is manual only return user impact level 0. (User impact level
+ * 0 is ignored by package watchdog)
*/
@Test
- public void isRollbackAllowedTest_noConfig() throws IOException {
- final File folder = createTempSubfolder("folder");
+ public void healthCheckFailed_impactLevelManualOnly_onePackage()
+ throws PackageManager.NameNotFoundException {
+ mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
+ VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
+ VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
+ PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo,
+ null, null, false, false,
+ null);
+ RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo),
+ false, null, 111,
+ PackageManager.ROLLBACK_USER_IMPACT_ONLY_MANUAL);
+ RollbackPackageHealthObserver observer =
+ spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ VersionedPackage secondFailedPackage = new VersionedPackage(APP_B, VERSION_CODE);
- readPermissions(folder, /* Grant all permission flags */ ~0);
+ when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
+ // Make the rollbacks available
+ when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1));
+ when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+ when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null);
- assertThat(RollbackPackageHealthObserver.isAutomaticRollbackDenied(mSysConfig,
- new VersionedPackage("com.android.vending", 1))).isEqualTo(false);
+ assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_0,
+ observer.onHealthCheckFailed(secondFailedPackage,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 1));
}
/**
- * Creates folderName/fileName in the mTemporaryFolder and fills it with the contents.
- *
- * @param folder pre-existing subdirectory of mTemporaryFolder to put the file
- * @param fileName name of the file (e.g. filename.xml) to create
- * @param contents contents to write to the file
- * @return the newly created file
+ * When both low impact and high impact are present, return 70.
*/
- private File createTempFile(File folder, String fileName, String contents)
- throws IOException {
- File file = new File(folder, fileName);
- BufferedWriter bw = new BufferedWriter(new FileWriter(file));
- bw.write(contents);
- bw.close();
-
- // Print to logcat for test debugging.
- Log.d(LOG_TAG, "Contents of file " + file.getAbsolutePath());
- Scanner input = new Scanner(file);
- while (input.hasNextLine()) {
- Log.d(LOG_TAG, input.nextLine());
- }
+ @Test
+ public void healthCheckFailed_impactLevelLowAndHigh_onePackage()
+ throws PackageManager.NameNotFoundException {
+ mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
+ VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
+ VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
+ PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo,
+ null, null, false, false,
+ null);
+ RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo),
+ false, null, 111,
+ PackageManager.ROLLBACK_USER_IMPACT_LOW);
+ VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2);
+ VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE);
+ PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo,
+ null, null, false, false,
+ null);
+ RollbackInfo rollbackInfo2 = new RollbackInfo(2, List.of(packageRollbackInfoB),
+ false, null, 222,
+ PackageManager.ROLLBACK_USER_IMPACT_HIGH);
+ RollbackPackageHealthObserver observer =
+ spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE);
+
+ when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
+ // Make the rollbacks available
+ when(mRollbackManager.getAvailableRollbacks()).thenReturn(
+ List.of(rollbackInfo1, rollbackInfo2));
+ when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+ when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null);
+
+ assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_70,
+ observer.onHealthCheckFailed(failedPackage,
+ PackageWatchdog.FAILURE_REASON_APP_CRASH, 1));
+ }
+
+ /**
+ * When low impact rollback is available roll it back.
+ */
+ @Test
+ public void execute_impactLevelLow_nativeCrash_rollback()
+ throws PackageManager.NameNotFoundException {
+ mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
+ int rollbackId = 1;
+ VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
+ VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
+ PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo,
+ null, null , false, false,
+ null);
+ RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId, List.of(packageRollbackInfo),
+ false, null, 111,
+ PackageManager.ROLLBACK_USER_IMPACT_LOW);
+ VersionedPackage secondFailedPackage = new VersionedPackage(APP_B, VERSION_CODE);
+ RollbackPackageHealthObserver observer =
+ spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+
+ when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
+ // Make the rollbacks available
+ when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1));
+ when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+ when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null);
+
+ observer.execute(secondFailedPackage,
+ PackageWatchdog.FAILURE_REASON_NATIVE_CRASH, 1);
+ waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10));
+
+ verify(mRollbackManager).getAvailableRollbacks();
+ verify(mRollbackManager).commitRollback(eq(rollbackId), any(), any());
+ }
+
+ /**
+ * Rollback the failing package if rollback is available for it
+ */
+ @Test
+ public void execute_impactLevelLow_rollbackFailedPackage()
+ throws PackageManager.NameNotFoundException {
+ mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
+ int rollbackId1 = 1;
+ VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
+ VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
+ PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo,
+ null, null , false, false,
+ null);
+ RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoA),
+ false, null, 111,
+ PackageManager.ROLLBACK_USER_IMPACT_LOW);
+ int rollbackId2 = 2;
+ VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2);
+ VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE);
+ PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo,
+ null, null , false, false,
+ null);
+ RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoB),
+ false, null, 222,
+ PackageManager.ROLLBACK_USER_IMPACT_LOW);
+ RollbackPackageHealthObserver observer =
+ spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class);
+
+ when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
+ // Make the rollbacks available
+ when(mRollbackManager.getAvailableRollbacks()).thenReturn(
+ List.of(rollbackInfo1, rollbackInfo2));
+ when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+ when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null);
+
+ observer.execute(appBFrom, PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+ waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10));
+
+ verify(mRollbackManager).commitRollback(argument.capture(), any(), any());
+ // Rollback package App B as the failing package is B
+ assertThat(argument.getValue()).isEqualTo(rollbackId2);
+ }
+
+ /**
+ * Rollback all available rollbacks if the rollback is not available for failing package.
+ */
+ @Test
+ public void execute_impactLevelLow_rollbackAll()
+ throws PackageManager.NameNotFoundException {
+ mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
+ int rollbackId1 = 1;
+ VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
+ VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
+ PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo,
+ null, null , false, false,
+ null);
+ RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoA),
+ false, null, 111,
+ PackageManager.ROLLBACK_USER_IMPACT_LOW);
+ int rollbackId2 = 2;
+ VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2);
+ VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE);
+ PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo,
+ null, null , false, false,
+ null);
+ RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoB),
+ false, null, 222,
+ PackageManager.ROLLBACK_USER_IMPACT_LOW);
+ VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE);
+ RollbackPackageHealthObserver observer =
+ spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class);
+
+ when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
+ // Make the rollbacks available
+ when(mRollbackManager.getAvailableRollbacks()).thenReturn(
+ List.of(rollbackInfo1, rollbackInfo2));
+ when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+ when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null);
+
+ observer.execute(failedPackage, PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+ waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10));
+
+ verify(mRollbackManager, times(2)).commitRollback(
+ argument.capture(), any(), any());
+ // Rollback A and B when the failing package doesn't have a rollback
+ assertThat(argument.getAllValues()).isEqualTo(List.of(rollbackId1, rollbackId2));
+ }
+
+ /**
+ * rollback low impact package if both low and high impact packages are available
+ */
+ @Test
+ public void execute_impactLevelLowAndHigh_rollbackLow()
+ throws PackageManager.NameNotFoundException {
+ mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
+ int rollbackId1 = 1;
+ VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
+ VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
+ PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo,
+ null, null , false, false,
+ null);
+ RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoA),
+ false, null, 111,
+ PackageManager.ROLLBACK_USER_IMPACT_LOW);
+ int rollbackId2 = 2;
+ VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2);
+ VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE);
+ PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo,
+ null, null , false, false,
+ null);
+ RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoB),
+ false, null, 222,
+ PackageManager.ROLLBACK_USER_IMPACT_HIGH);
+ VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE);
+ RollbackPackageHealthObserver observer =
+ spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class);
+
+ when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
+ // Make the rollbacks available
+ when(mRollbackManager.getAvailableRollbacks()).thenReturn(
+ List.of(rollbackInfo1, rollbackInfo2));
+ when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+ when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null);
+
+ observer.execute(failedPackage, PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+ waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10));
+
+ verify(mRollbackManager, times(1)).commitRollback(
+ argument.capture(), any(), any());
+ // Rollback A and B when the failing package doesn't have a rollback
+ assertThat(argument.getAllValues()).isEqualTo(List.of(rollbackId1));
+ }
+
+ /**
+ * Don't roll back high impact package if only high impact package is available. high impact
+ * rollback to be rolled back only on bootloop.
+ */
+ @Test
+ public void execute_impactLevelHigh_rollbackHigh()
+ throws PackageManager.NameNotFoundException {
+ mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
+ int rollbackId2 = 2;
+ VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2);
+ VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE);
+ PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo,
+ null, null , false, false,
+ null);
+ RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoB),
+ false, null, 111,
+ PackageManager.ROLLBACK_USER_IMPACT_HIGH);
+ VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE);
+ RollbackPackageHealthObserver observer =
+ spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class);
+
+ when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
+ // Make the rollbacks available
+ when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo2));
+ when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+ when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null);
+
+ observer.execute(failedPackage, PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+ waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10));
+
+ verify(mRollbackManager, never()).commitRollback(argument.capture(), any(), any());
+
+ }
+
+ /**
+ * Test that when impactLevel is low returns user impact level 70
+ */
+ @Test
+ public void onBootLoop_impactLevelLow_onePackage() throws PackageManager.NameNotFoundException {
+ mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
+ VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
+ VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
+ PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo,
+ null, null , false, false,
+ null);
+ RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo),
+ false, null, 111,
+ PackageManager.ROLLBACK_USER_IMPACT_LOW);
+ RollbackPackageHealthObserver observer =
+ spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+
+ when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
+ // Make the rollbacks available
+ when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1));
+ when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+ when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null);
+
+ assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_70,
+ observer.onBootLoop(1));
+ }
+
+ @Test
+ public void onBootLoop_impactLevelHigh_onePackage()
+ throws PackageManager.NameNotFoundException {
+ mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
+ VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
+ VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
+ PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo,
+ null, null, false, false,
+ null);
+ RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo),
+ false, null, 111,
+ PackageManager.ROLLBACK_USER_IMPACT_HIGH);
+ RollbackPackageHealthObserver observer =
+ spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+
+ when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
+ // Make the rollbacks available
+ when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1));
+ when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+ when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null);
- return file;
+ assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_90,
+ observer.onBootLoop(1));
}
- private void readPermissions(File libraryDir, int permissionFlag) {
- final XmlPullParser parser = Xml.newPullParser();
- mSysConfig.readPermissions(parser, libraryDir, permissionFlag);
+ /**
+ * When the rollback impact level is manual only return user impact level 0. (User impact level
+ * 0 is ignored by package watchdog)
+ */
+ @Test
+ public void onBootLoop_impactLevelManualOnly_onePackage()
+ throws PackageManager.NameNotFoundException {
+ mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
+ VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
+ VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
+ PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo,
+ null, null, false, false,
+ null);
+ RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo),
+ false, null, 111,
+ PackageManager.ROLLBACK_USER_IMPACT_ONLY_MANUAL);
+ RollbackPackageHealthObserver observer =
+ spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+
+ when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
+ // Make the rollbacks available
+ when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1));
+ when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+ when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null);
+
+ assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_0,
+ observer.onBootLoop(1));
}
/**
- * Creates folderName/fileName in the mTemporaryFolder and fills it with the contents.
- *
- * @param folderName subdirectory of mTemporaryFolder to put the file, creating if needed
- * @return the folder
+ * When both low impact and high impact are present, return 70.
*/
- private File createTempSubfolder(String folderName)
- throws IOException {
- File folder = new File(mTemporaryFolder.getRoot(), folderName);
- folder.mkdirs();
- return folder;
+ @Test
+ public void onBootLoop_impactLevelLowAndHigh_onePackage()
+ throws PackageManager.NameNotFoundException {
+ mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
+ VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
+ VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
+ PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo,
+ null, null, false, false,
+ null);
+ RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo),
+ false, null, 111,
+ PackageManager.ROLLBACK_USER_IMPACT_LOW);
+ VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2);
+ VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE);
+ PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo,
+ null, null, false, false,
+ null);
+ RollbackInfo rollbackInfo2 = new RollbackInfo(2, List.of(packageRollbackInfoB),
+ false, null, 222,
+ PackageManager.ROLLBACK_USER_IMPACT_HIGH);
+ RollbackPackageHealthObserver observer =
+ spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+
+ when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
+ // Make the rollbacks available
+ when(mRollbackManager.getAvailableRollbacks()).thenReturn(
+ List.of(rollbackInfo1, rollbackInfo2));
+ when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+ when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null);
+
+ assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_70,
+ observer.onBootLoop(1));
+ }
+
+ /**
+ * Rollback all available rollbacks if the rollback is not available for failing package.
+ */
+ @Test
+ public void executeBootLoopMitigation_impactLevelLow_rollbackAll()
+ throws PackageManager.NameNotFoundException {
+ mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
+ int rollbackId1 = 1;
+ VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
+ VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
+ PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo,
+ null, null , false, false,
+ null);
+ RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoA),
+ false, null, 111,
+ PackageManager.ROLLBACK_USER_IMPACT_LOW);
+ int rollbackId2 = 2;
+ VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2);
+ VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE);
+ PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo,
+ null, null , false, false,
+ null);
+ RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoB),
+ false, null, 222,
+ PackageManager.ROLLBACK_USER_IMPACT_LOW);
+ RollbackPackageHealthObserver observer =
+ spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class);
+
+ when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
+ // Make the rollbacks available
+ when(mRollbackManager.getAvailableRollbacks()).thenReturn(
+ List.of(rollbackInfo1, rollbackInfo2));
+ when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+ when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null);
+
+ observer.executeBootLoopMitigation(1);
+ waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10));
+
+ verify(mRollbackManager, times(2)).commitRollback(
+ argument.capture(), any(), any());
+ // Rollback A and B when the failing package doesn't have a rollback
+ assertThat(argument.getAllValues()).isEqualTo(List.of(rollbackId1, rollbackId2));
+ }
+
+ /**
+ * rollback low impact package if both low and high impact packages are available
+ */
+ @Test
+ public void executeBootLoopMitigation_impactLevelLowAndHigh_rollbackLow()
+ throws PackageManager.NameNotFoundException {
+ mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
+ int rollbackId1 = 1;
+ VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
+ VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
+ PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo,
+ null, null , false, false,
+ null);
+ RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoA),
+ false, null, 111,
+ PackageManager.ROLLBACK_USER_IMPACT_LOW);
+ int rollbackId2 = 2;
+ VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2);
+ VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE);
+ PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo,
+ null, null , false, false,
+ null);
+ RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoB),
+ false, null, 222,
+ PackageManager.ROLLBACK_USER_IMPACT_HIGH);
+ RollbackPackageHealthObserver observer =
+ spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class);
+
+ when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
+ // Make the rollbacks available
+ when(mRollbackManager.getAvailableRollbacks()).thenReturn(
+ List.of(rollbackInfo1, rollbackInfo2));
+ when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+ when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null);
+
+ observer.executeBootLoopMitigation(1);
+ waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10));
+
+ verify(mRollbackManager, times(1)).commitRollback(
+ argument.capture(), any(), any());
+ // Rollback A and B when the failing package doesn't have a rollback
+ assertThat(argument.getAllValues()).isEqualTo(List.of(rollbackId1));
+ }
+
+ /**
+ * Rollback high impact package if only high impact package is available
+ */
+ @Test
+ public void executeBootLoopMitigation_impactLevelHigh_rollbackHigh()
+ throws PackageManager.NameNotFoundException {
+ mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
+ int rollbackId2 = 2;
+ VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2);
+ VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE);
+ PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo,
+ null, null , false, false,
+ null);
+ RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoB),
+ false, null, 111,
+ PackageManager.ROLLBACK_USER_IMPACT_HIGH);
+ RollbackPackageHealthObserver observer =
+ spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class);
+
+ when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
+ // Make the rollbacks available
+ when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo2));
+ when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+ when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null);
+
+ observer.executeBootLoopMitigation(1);
+ waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10));
+
+ verify(mRollbackManager, times(1)).commitRollback(
+ argument.capture(), any(), any());
+ // Rollback high impact packages when no other rollback available
+ assertThat(argument.getAllValues()).isEqualTo(List.of(rollbackId2));
+ }
+
+ /**
+ * Rollback only low impact available rollbacks if both low and manual only are available.
+ */
+ @Test
+ public void execute_impactLevelLowAndManual_rollbackLowImpactOnly()
+ throws PackageManager.NameNotFoundException, InterruptedException {
+ mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
+ int rollbackId1 = 1;
+ VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
+ VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
+ PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo,
+ null, null , false, false,
+ null);
+ RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoA),
+ false, null, 111,
+ PackageManager.ROLLBACK_USER_IMPACT_LOW);
+ int rollbackId2 = 2;
+ VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2);
+ VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE);
+ PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo,
+ null, null , false, false,
+ null);
+ RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoB),
+ false, null, 222,
+ PackageManager.ROLLBACK_USER_IMPACT_ONLY_MANUAL);
+ VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE);
+ RollbackPackageHealthObserver observer =
+ spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class);
+
+ when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
+ // Make the rollbacks available
+ when(mRollbackManager.getAvailableRollbacks()).thenReturn(
+ List.of(rollbackInfo1, rollbackInfo2));
+ when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+ when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null);
+
+ observer.execute(failedPackage, PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+ waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10));
+
+ verify(mRollbackManager, times(1)).commitRollback(
+ argument.capture(), any(), any());
+ assertThat(argument.getAllValues()).isEqualTo(List.of(rollbackId1));
+ }
+
+ /**
+ * Do not roll back if only manual rollback is available.
+ */
+ @Test
+ public void execute_impactLevelManual_rollbackLowImpactOnly()
+ throws PackageManager.NameNotFoundException {
+ mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
+ int rollbackId1 = 1;
+ VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
+ VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
+ PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo,
+ null, null , false, false,
+ null);
+ RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoA),
+ false, null, 111,
+ PackageManager.ROLLBACK_USER_IMPACT_ONLY_MANUAL);
+ VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE);
+ RollbackPackageHealthObserver observer =
+ spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class);
+
+ when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
+ // Make the rollbacks available
+ when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1));
+ when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+ when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null);
+
+ observer.execute(failedPackage, PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+ waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10));
+
+ verify(mRollbackManager, never()).commitRollback(argument.capture(), any(), any());
+ }
+
+ /**
+ * Rollback alphabetically first package if multiple high impact rollbacks are available.
+ */
+ @Test
+ public void executeBootLoopMitigation_impactLevelHighMultiplePackage_rollbackHigh()
+ throws PackageManager.NameNotFoundException {
+ mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION);
+ int rollbackId1 = 1;
+ VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2);
+ VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE);
+ PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo,
+ null, null , false, false,
+ null);
+ RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoB),
+ false, null, 111,
+ PackageManager.ROLLBACK_USER_IMPACT_HIGH);
+ int rollbackId2 = 2;
+ VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2);
+ VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE);
+ PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo,
+ null, null , false, false,
+ null);
+ RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoA),
+ false, null, 111,
+ PackageManager.ROLLBACK_USER_IMPACT_HIGH);
+ VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE);
+ RollbackPackageHealthObserver observer =
+ spy(new RollbackPackageHealthObserver(mMockContext, mApexManager));
+ ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class);
+
+ when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager);
+ // Make the rollbacks available
+ when(mRollbackManager.getAvailableRollbacks()).thenReturn(
+ List.of(rollbackInfo1, rollbackInfo2));
+ when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+ when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null);
+
+ observer.executeBootLoopMitigation(1);
+ waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10));
+
+ verify(mRollbackManager, times(1)).commitRollback(
+ argument.capture(), any(), any());
+ // Rollback APP_A because it is first alphabetically
+ assertThat(argument.getAllValues()).isEqualTo(List.of(rollbackId2));
+ }
+
+ private void waitForIdleHandler(Handler handler, Duration timeout) {
+ final MessageQueue queue = handler.getLooper().getQueue();
+ final CountDownLatch latch = new CountDownLatch(1);
+ queue.addIdleHandler(() -> {
+ latch.countDown();
+ // Remove idle handler
+ return false;
+ });
+ try {
+ latch.await(timeout.toMillis(), TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ fail("Interrupted unexpectedly: " + e);
+ }
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/TEST_MAPPING b/services/tests/mockingservicestests/src/com/android/server/rollback/TEST_MAPPING
new file mode 100644
index 000000000000..e42bdad97730
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/rollback/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "postsubmit": [
+ {
+ "name": "FrameworksMockingServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.rollback"
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/services/tests/powerservicetests/Android.bp b/services/tests/powerservicetests/Android.bp
index 8d455fedf9b2..729dcbd11f42 100644
--- a/services/tests/powerservicetests/Android.bp
+++ b/services/tests/powerservicetests/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_powermanager_framework",
// See: http://go/android-license-faq
default_applicable_licenses: ["frameworks_base_license"],
}
diff --git a/services/tests/powerstatstests/Android.bp b/services/tests/powerstatstests/Android.bp
index 1de049eaf263..51c9d0ae5e5d 100644
--- a/services/tests/powerstatstests/Android.bp
+++ b/services/tests/powerstatstests/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_backstage_power",
// See: http://go/android-license-faq
default_applicable_licenses: ["frameworks_base_license"],
}
diff --git a/services/tests/powerstatstests/BstatsTestApp/Android.bp b/services/tests/powerstatstests/BstatsTestApp/Android.bp
index c82da9e7b449..7408d13b2811 100644
--- a/services/tests/powerstatstests/BstatsTestApp/Android.bp
+++ b/services/tests/powerstatstests/BstatsTestApp/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_framework_backstage_power",
// 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"
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java
index 548fae7a0b01..a1101cd0f0bc 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java
@@ -578,45 +578,136 @@ public class BatteryStatsImplTest {
// First wakelock, acquired once, not currently held
mMockClock.realtime = 1000;
- mBatteryStatsImpl.noteStartWakeLocked(10100, 100, null, "wakeLock1", null,
- BatteryStats.WAKE_TYPE_PARTIAL, false);
+ mBatteryStatsImpl.noteStartWakeLocked(
+ 10100, 100, null, "wakeLock1", null, BatteryStats.WAKE_TYPE_PARTIAL, false);
mMockClock.realtime = 3000;
- mBatteryStatsImpl.noteStopWakeLocked(10100, 100, null, "wakeLock1", null,
- BatteryStats.WAKE_TYPE_PARTIAL);
+ mBatteryStatsImpl.noteStopWakeLocked(
+ 10100, 100, null, "wakeLock1", null, BatteryStats.WAKE_TYPE_PARTIAL);
// Second wakelock, acquired twice, still held
mMockClock.realtime = 4000;
- mBatteryStatsImpl.noteStartWakeLocked(10200, 101, null, "wakeLock2", null,
- BatteryStats.WAKE_TYPE_PARTIAL, false);
+ mBatteryStatsImpl.noteStartWakeLocked(
+ 10200, 101, null, "wakeLock2", null, BatteryStats.WAKE_TYPE_PARTIAL, false);
mMockClock.realtime = 5000;
- mBatteryStatsImpl.noteStopWakeLocked(10200, 101, null, "wakeLock2", null,
- BatteryStats.WAKE_TYPE_PARTIAL);
+ mBatteryStatsImpl.noteStopWakeLocked(
+ 10200, 101, null, "wakeLock2", null, BatteryStats.WAKE_TYPE_PARTIAL);
mMockClock.realtime = 6000;
- mBatteryStatsImpl.noteStartWakeLocked(10200, 101, null, "wakeLock2", null,
- BatteryStats.WAKE_TYPE_PARTIAL, false);
+ mBatteryStatsImpl.noteStartWakeLocked(
+ 10200, 101, null, "wakeLock2", null, BatteryStats.WAKE_TYPE_PARTIAL, false);
- mMockClock.realtime = 9000;
-
- List<WakeLockStats.WakeLock> wakeLockStats =
- mBatteryStatsImpl.getWakeLockStats().getWakeLocks();
- assertThat(wakeLockStats).hasSize(2);
+ // Third and fourth wakelocks, overlapped with each other.
+ mMockClock.realtime = 7000;
+ mBatteryStatsImpl.noteStartWakeLocked(
+ 10300, 102, null, "wakeLock3", null, BatteryStats.WAKE_TYPE_PARTIAL, false);
- WakeLockStats.WakeLock wakeLock1 = wakeLockStats.stream()
- .filter(wl -> wl.uid == 10100 && wl.name.equals("wakeLock1")).findFirst().get();
+ mMockClock.realtime = 8000;
+ mBatteryStatsImpl.noteStartWakeLocked(
+ 10400, 103, null, "wakeLock4", null, BatteryStats.WAKE_TYPE_PARTIAL, false);
- assertThat(wakeLock1.timesAcquired).isEqualTo(1);
- assertThat(wakeLock1.timeHeldMs).isEqualTo(0); // Not currently held
- assertThat(wakeLock1.totalTimeHeldMs).isEqualTo(2000); // 3000-1000
+ mMockClock.realtime = 9000;
+ mBatteryStatsImpl.noteStopWakeLocked(
+ 10400, 103, null, "wakeLock4", null, BatteryStats.WAKE_TYPE_PARTIAL);
+
+ mMockClock.realtime = 10000;
+ mBatteryStatsImpl.noteStartWakeLocked(
+ 10400, 104, null, "wakeLock5", null, BatteryStats.WAKE_TYPE_PARTIAL, false);
+
+ mMockClock.realtime = 11000;
+ mBatteryStatsImpl.noteStopWakeLocked(
+ 10400, 104, null, "wakeLock5", null, BatteryStats.WAKE_TYPE_PARTIAL);
+
+ mMockClock.realtime = 12000;
+ mBatteryStatsImpl.noteStopWakeLocked(
+ 10300, 102, null, "wakeLock3", null, BatteryStats.WAKE_TYPE_PARTIAL);
+
+ mMockClock.realtime = 13000;
+
+ // Verify un-aggregated wakelocks.
+ WakeLockStats wakeLockStats = mBatteryStatsImpl.getWakeLockStats();
+ List<WakeLockStats.WakeLock> wakeLockList = wakeLockStats.getWakeLocks();
+ assertThat(wakeLockList).hasSize(4);
+
+ WakeLockStats.WakeLock wakeLock1 = getWakeLockFromList(wakeLockList, 10100, "wakeLock1");
+ assertThat(wakeLock1.isAggregated).isFalse();
+ assertThat(wakeLock1.totalWakeLockData.timesAcquired).isEqualTo(1);
+ assertThat(wakeLock1.totalWakeLockData.timeHeldMs).isEqualTo(0); // Not currently held
+ assertThat(wakeLock1.totalWakeLockData.totalTimeHeldMs).isEqualTo(2000); // 3000-1000
+
+ WakeLockStats.WakeLock wakeLock3 = getWakeLockFromList(wakeLockList, 10300, "wakeLock3");
+ assertThat(wakeLock3.isAggregated).isFalse();
+ assertThat(wakeLock3.totalWakeLockData.timesAcquired).isEqualTo(1);
+ assertThat(wakeLock3.totalWakeLockData.timeHeldMs).isEqualTo(0); // Not currently held
+ // (8000-7000)/2 + (9000-8000)/3 + (10000-9000)/2 + (11000-10000)/3 + (12000-11000)/2
+ assertThat(wakeLock3.totalWakeLockData.totalTimeHeldMs).isEqualTo(2166);
+
+ WakeLockStats.WakeLock wakeLock4 = getWakeLockFromList(wakeLockList, 10400, "wakeLock4");
+ assertThat(wakeLock4.isAggregated).isFalse();
+ assertThat(wakeLock4.totalWakeLockData.timesAcquired).isEqualTo(1);
+ assertThat(wakeLock4.totalWakeLockData.timeHeldMs).isEqualTo(0); // Not currently held
+ assertThat(wakeLock4.totalWakeLockData.totalTimeHeldMs).isEqualTo(333); // (9000-8000)/3
+
+ WakeLockStats.WakeLock wakeLock5 = getWakeLockFromList(wakeLockList, 10400, "wakeLock5");
+ assertThat(wakeLock5.isAggregated).isFalse();
+ assertThat(wakeLock5.totalWakeLockData.timesAcquired).isEqualTo(1);
+ assertThat(wakeLock5.totalWakeLockData.timeHeldMs).isEqualTo(0); // Not currently held
+ assertThat(wakeLock5.totalWakeLockData.totalTimeHeldMs).isEqualTo(333); // (11000-10000)/3
+
+ // Verify aggregated wakelocks.
+ List<WakeLockStats.WakeLock> aggregatedWakeLockList =
+ wakeLockStats.getAggregatedWakeLocks();
+ assertThat(aggregatedWakeLockList).hasSize(4);
+
+ WakeLockStats.WakeLock aggregatedWakeLock1 =
+ getAggregatedWakeLockFromList(aggregatedWakeLockList, 10100);
+ assertThat(aggregatedWakeLock1.isAggregated).isTrue();
+ assertThat(aggregatedWakeLock1.totalWakeLockData.timesAcquired).isEqualTo(1);
+ // Not currently held
+ assertThat(aggregatedWakeLock1.totalWakeLockData.timeHeldMs).isEqualTo(0);
+ // 3000-1000
+ assertThat(aggregatedWakeLock1.totalWakeLockData.totalTimeHeldMs).isEqualTo(2000);
+
+ WakeLockStats.WakeLock aggregatedWakeLock2 =
+ getAggregatedWakeLockFromList(aggregatedWakeLockList, 10200);
+ assertThat(aggregatedWakeLock2.isAggregated).isTrue();
+ assertThat(aggregatedWakeLock2.totalWakeLockData.timesAcquired).isEqualTo(2);
+ assertThat(aggregatedWakeLock2.totalWakeLockData.timeHeldMs).isEqualTo(7000); // 13000-6000
+ // (5000-4000) + (13000-6000)
+ assertThat(aggregatedWakeLock2.totalWakeLockData.totalTimeHeldMs)
+ .isEqualTo(8000);
+
+ WakeLockStats.WakeLock aggregatedWakeLock3 =
+ getAggregatedWakeLockFromList(aggregatedWakeLockList, 10300);
+ assertThat(aggregatedWakeLock3.isAggregated).isTrue();
+ assertThat(aggregatedWakeLock3.totalWakeLockData.timesAcquired).isEqualTo(1);
+ // Not currently held
+ assertThat(aggregatedWakeLock3.totalWakeLockData.timeHeldMs).isEqualTo(0);
+ // 12000-7000
+ assertThat(aggregatedWakeLock3.totalWakeLockData.totalTimeHeldMs).isEqualTo(5000);
+
+ WakeLockStats.WakeLock aggregatedWakeLock4 =
+ getAggregatedWakeLockFromList(aggregatedWakeLockList, 10400);
+ assertThat(aggregatedWakeLock4.isAggregated).isTrue();
+ assertThat(aggregatedWakeLock4.totalWakeLockData.timesAcquired).isEqualTo(2);
+ // Not currently held
+ assertThat(aggregatedWakeLock4.totalWakeLockData.timeHeldMs).isEqualTo(0);
+ assertThat(aggregatedWakeLock4.totalWakeLockData.totalTimeHeldMs)
+ .isEqualTo(2000); // (9000-8000) + (11000-10000)
+ }
- WakeLockStats.WakeLock wakeLock2 = wakeLockStats.stream()
- .filter(wl -> wl.uid == 10200 && wl.name.equals("wakeLock2")).findFirst().get();
+ private WakeLockStats.WakeLock getAggregatedWakeLockFromList(
+ List<WakeLockStats.WakeLock> wakeLockList, final int uid) {
+ return getWakeLockFromList(wakeLockList, uid, WakeLockStats.WakeLock.NAME_AGGREGATED);
+ }
- assertThat(wakeLock2.timesAcquired).isEqualTo(2);
- assertThat(wakeLock2.timeHeldMs).isEqualTo(3000); // 9000-6000
- assertThat(wakeLock2.totalTimeHeldMs).isEqualTo(4000); // (5000-4000) + (9000-6000)
+ private WakeLockStats.WakeLock getWakeLockFromList(
+ List<WakeLockStats.WakeLock> wakeLockList, final int uid, final String name) {
+ return wakeLockList.stream()
+ .filter(wl -> wl.uid == uid && wl.name.equals(name))
+ .findFirst()
+ .get();
}
@Test
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
index 6cd79bc09fb6..374426ad67a1 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
@@ -18,12 +18,17 @@ package com.android.server.power.stats;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.content.Context;
import android.hardware.SensorManager;
+import android.os.AggregateBatteryConsumer;
import android.os.BatteryConsumer;
import android.os.BatteryManager;
import android.os.BatteryStats;
@@ -34,6 +39,7 @@ import android.os.Parcel;
import android.os.Process;
import android.os.UidBatteryConsumer;
import android.platform.test.ravenwood.RavenwoodRule;
+import android.util.SparseLongArray;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -49,7 +55,6 @@ import org.junit.runner.RunWith;
import java.io.File;
import java.io.IOException;
-import java.nio.file.Files;
import java.util.List;
@SmallTest
@@ -64,11 +69,10 @@ public class BatteryUsageStatsProviderTest {
private static final long MINUTE_IN_MS = 60 * 1000;
private static final double PRECISION = 0.00001;
- private File mHistoryDir;
-
@Rule(order = 1)
public final BatteryUsageStatsRule mStatsRule =
- new BatteryUsageStatsRule(12345, mHistoryDir)
+ new BatteryUsageStatsRule(12345)
+ .createTempDirectory()
.setAveragePower(PowerProfile.POWER_FLASHLIGHT, 360.0)
.setAveragePower(PowerProfile.POWER_AUDIO, 720.0);
@@ -77,9 +81,6 @@ public class BatteryUsageStatsProviderTest {
@Before
public void setup() throws IOException {
- mHistoryDir = Files.createTempDirectory("BatteryUsageStatsProviderTest").toFile();
- clearDirectory(mHistoryDir);
-
if (RavenwoodRule.isUnderRavenwood()) {
mContext = mock(Context.class);
SensorManager sensorManager = mock(SensorManager.class);
@@ -89,17 +90,6 @@ public class BatteryUsageStatsProviderTest {
}
}
- private void clearDirectory(File dir) {
- if (dir.exists()) {
- for (File child : dir.listFiles()) {
- if (child.isDirectory()) {
- clearDirectory(child);
- }
- child.delete();
- }
- }
- }
-
@Test
public void test_getBatteryUsageStats() {
BatteryStatsImpl batteryStats = prepareBatteryStats();
@@ -417,7 +407,7 @@ public class BatteryUsageStatsProviderTest {
}
PowerStatsStore powerStatsStore = new PowerStatsStore(
- new File(mHistoryDir, "powerstatsstore"),
+ new File(mStatsRule.getHistoryDir(), "powerstatsstore"),
mStatsRule.getHandler(), null);
powerStatsStore.reset();
@@ -517,6 +507,57 @@ public class BatteryUsageStatsProviderTest {
}
@Test
+ public void saveBatteryUsageStatsOnReset_incompatibleEnergyConsumers() throws Throwable {
+ MockBatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+ batteryStats.initMeasuredEnergyStats(new String[]{"FOO", "BAR"});
+ int componentId0 = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
+ int componentId1 = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + 1;
+
+ synchronized (batteryStats) {
+ batteryStats.getUidStatsLocked(APP_UID);
+
+ SparseLongArray uidEnergies = new SparseLongArray();
+ uidEnergies.put(APP_UID, 30_000_000);
+ batteryStats.updateCustomEnergyConsumerStatsLocked(0, 100_000_000, uidEnergies);
+ batteryStats.updateCustomEnergyConsumerStatsLocked(1, 200_000_000, uidEnergies);
+ }
+
+ BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext, null,
+ mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), null, mMockClock);
+
+ PowerStatsStore powerStatsStore = mock(PowerStatsStore.class);
+ doAnswer(invocation -> {
+ BatteryUsageStats stats = invocation.getArgument(1);
+ AggregateBatteryConsumer device = stats.getAggregateBatteryConsumer(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
+ assertThat(device.getCustomPowerComponentName(componentId0)).isEqualTo("FOO");
+ assertThat(device.getCustomPowerComponentName(componentId1)).isEqualTo("BAR");
+ assertThat(device.getConsumedPowerForCustomComponent(componentId0))
+ .isWithin(PRECISION).of(27.77777);
+ assertThat(device.getConsumedPowerForCustomComponent(componentId1))
+ .isWithin(PRECISION).of(55.55555);
+
+ UidBatteryConsumer uid = stats.getUidBatteryConsumers().get(0);
+ assertThat(uid.getConsumedPowerForCustomComponent(componentId0))
+ .isWithin(PRECISION).of(8.33333);
+ assertThat(uid.getConsumedPowerForCustomComponent(componentId1))
+ .isWithin(PRECISION).of(8.33333);
+ return null;
+ }).when(powerStatsStore).storeBatteryUsageStats(anyLong(), any());
+
+ mStatsRule.getBatteryStats().saveBatteryUsageStatsOnReset(provider, powerStatsStore);
+
+ // Make an incompatible change of supported energy components. This will trigger
+ // a BatteryStats reset, which will generate a snapshot of battery stats.
+ mStatsRule.initMeasuredEnergyStatsLocked(
+ new String[]{"COMPONENT1"});
+
+ mStatsRule.waitForBackgroundThread();
+
+ verify(powerStatsStore).storeBatteryUsageStats(anyLong(), any());
+ }
+
+ @Test
public void testAggregateBatteryStats_incompatibleSnapshot() {
MockBatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
batteryStats.initMeasuredEnergyStats(new String[]{"FOO", "BAR"});
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
index 8bdb0292bf00..296ad0e939de 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
@@ -16,6 +16,8 @@
package com.android.server.power.stats;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.anyDouble;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
@@ -28,11 +30,11 @@ import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
+import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.UidBatteryConsumer;
import android.os.UserBatteryConsumer;
-import android.platform.test.ravenwood.RavenwoodRule;
import android.util.SparseArray;
import androidx.test.InstrumentationRegistry;
@@ -47,6 +49,8 @@ import org.junit.runners.model.Statement;
import org.mockito.stubbing.Answer;
import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
import java.util.Arrays;
@SuppressWarnings("SynchronizeOnNonFinalField")
@@ -59,7 +63,9 @@ public class BatteryUsageStatsRule implements TestRule {
private final PowerProfile mPowerProfile;
private final MockClock mMockClock = new MockClock();
- private final File mHistoryDir;
+ private String mTestName;
+ private boolean mCreateTempDirectory;
+ private File mHistoryDir;
private MockBatteryStatsImpl mBatteryStats;
private Handler mHandler;
@@ -74,34 +80,33 @@ public class BatteryUsageStatsRule implements TestRule {
private NetworkStats mNetworkStats;
private boolean[] mSupportedStandardBuckets;
private String[] mCustomPowerComponentNames;
+ private Throwable mThrowable;
public BatteryUsageStatsRule() {
- this(0, null);
+ this(0);
}
public BatteryUsageStatsRule(long currentTime) {
- this(currentTime, null);
- }
-
- public BatteryUsageStatsRule(long currentTime, File historyDir) {
mHandler = mock(Handler.class);
mPowerProfile = spy(new PowerProfile());
mMockClock.currentTime = currentTime;
- mHistoryDir = historyDir;
-
- if (!RavenwoodRule.isUnderRavenwood()) {
- lateInitBatteryStats();
- }
-
mCpusByPolicy.put(0, new int[]{0, 1, 2, 3});
mCpusByPolicy.put(4, new int[]{4, 5, 6, 7});
mFreqsByPolicy.put(0, new int[]{300000, 1000000, 2000000});
mFreqsByPolicy.put(4, new int[]{300000, 1000000, 2500000, 3000000});
}
- private void lateInitBatteryStats() {
+ private void initBatteryStats() {
if (mBatteryStats != null) return;
+ if (mCreateTempDirectory) {
+ try {
+ mHistoryDir = Files.createTempDirectory(mTestName).toFile();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ clearDirectory();
+ }
mBatteryStats = new MockBatteryStatsImpl(mMockClock, mHistoryDir, mHandler);
mBatteryStats.setPowerProfile(mPowerProfile);
mBatteryStats.setCpuScalingPolicies(new CpuScalingPolicies(mCpusByPolicy, mFreqsByPolicy));
@@ -134,6 +139,15 @@ public class BatteryUsageStatsRule implements TestRule {
return mHandler;
}
+ public File getHistoryDir() {
+ return mHistoryDir;
+ }
+
+ public BatteryUsageStatsRule createTempDirectory() {
+ mCreateTempDirectory = true;
+ return this;
+ }
+
public BatteryUsageStatsRule setTestPowerProfile(@XmlRes int xmlId) {
mPowerProfile.forceInitForTesting(InstrumentationRegistry.getContext(), xmlId);
return this;
@@ -265,18 +279,23 @@ public class BatteryUsageStatsRule implements TestRule {
@Override
public Statement apply(Statement base, Description description) {
+ mTestName = description.getClassName() + "#" + description.getMethodName();
return new Statement() {
@Override
public void evaluate() throws Throwable {
before();
base.evaluate();
+ after();
}
};
}
private void before() {
- lateInitBatteryStats();
+ initBatteryStats();
HandlerThread bgThread = new HandlerThread("bg thread");
+ bgThread.setUncaughtExceptionHandler((thread, throwable)-> {
+ mThrowable = throwable;
+ });
bgThread.start();
mHandler = new Handler(bgThread.getLooper());
mBatteryStats.setHandler(mHandler);
@@ -285,6 +304,26 @@ public class BatteryUsageStatsRule implements TestRule {
mBatteryStats.getOnBatteryScreenOffTimeBase().setRunning(!mScreenOn, 0, 0);
}
+ private void after() throws Throwable {
+ if (mHandler != null) {
+ waitForBackgroundThread();
+ }
+ }
+
+ public void waitForBackgroundThread() throws Throwable {
+ if (mThrowable != null) {
+ throw mThrowable;
+ }
+
+ ConditionVariable done = new ConditionVariable();
+ mHandler.post(done::open);
+ assertThat(done.block(10000)).isTrue();
+
+ if (mThrowable != null) {
+ throw mThrowable;
+ }
+ }
+
public PowerProfile getPowerProfile() {
return mPowerProfile;
}
@@ -296,6 +335,9 @@ public class BatteryUsageStatsRule implements TestRule {
}
public MockBatteryStatsImpl getBatteryStats() {
+ if (mBatteryStats == null) {
+ initBatteryStats();
+ }
return mBatteryStats;
}
@@ -369,4 +411,19 @@ public class BatteryUsageStatsRule implements TestRule {
}
return null;
}
+
+ public void clearDirectory() {
+ clearDirectory(mHistoryDir);
+ }
+
+ private void clearDirectory(File dir) {
+ if (dir.exists()) {
+ for (File child : dir.listFiles()) {
+ if (child.isDirectory()) {
+ clearDirectory(child);
+ }
+ child.delete();
+ }
+ }
+ }
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java
index af5b462e017d..2ea86a4527eb 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java
@@ -58,7 +58,7 @@ public class PowerStatsAggregatorTest {
@Before
public void setup() throws ParseException {
- mHistory = new BatteryStatsHistory(32, 1024,
+ mHistory = new BatteryStatsHistory(1024,
mock(BatteryStatsHistory.HistoryStepDetailsCalculator.class), mClock,
mMonotonicClock, mock(BatteryStatsHistory.TraceDelegate.class), null);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterInputTest.kt b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterInputTest.kt
index 52c7d8d2bd2e..5c8c6bb69ef8 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterInputTest.kt
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterInputTest.kt
@@ -17,31 +17,38 @@ package com.android.server.accessibility
import android.hardware.display.DisplayManagerGlobal
import android.os.SystemClock
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.CheckFlagsRule
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.view.Display
import android.view.Display.DEFAULT_DISPLAY
import android.view.DisplayAdjustments
import android.view.DisplayInfo
import android.view.IInputFilterHost
+import android.view.InputDevice.SOURCE_STYLUS
import android.view.InputDevice.SOURCE_TOUCHSCREEN
import android.view.InputEvent
import android.view.MotionEvent
+import android.view.MotionEvent.ACTION_CANCEL
import android.view.MotionEvent.ACTION_DOWN
-import android.view.MotionEvent.ACTION_MOVE
-import android.view.MotionEvent.ACTION_UP
import android.view.MotionEvent.ACTION_HOVER_ENTER
import android.view.MotionEvent.ACTION_HOVER_EXIT
import android.view.MotionEvent.ACTION_HOVER_MOVE
+import android.view.MotionEvent.ACTION_MOVE
+import android.view.MotionEvent.ACTION_UP
import android.view.WindowManagerPolicyConstants.FLAG_PASS_TO_USER
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.android.cts.input.inputeventmatchers.withDeviceId
import com.android.cts.input.inputeventmatchers.withMotionAction
+import com.android.cts.input.inputeventmatchers.withSource
import com.android.server.LocalServices
import com.android.server.accessibility.magnification.MagnificationProcessor
import com.android.server.wm.WindowManagerInternal
import java.util.concurrent.LinkedBlockingQueue
import org.hamcrest.Matchers.allOf
import org.junit.After
+import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -92,12 +99,17 @@ class AccessibilityInputFilterInputTest {
or AccessibilityInputFilter.FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER
or AccessibilityInputFilter.FLAG_FEATURE_INJECT_MOTION_EVENTS
or AccessibilityInputFilter.FLAG_FEATURE_FILTER_KEY_EVENTS)
+ const val STYLUS_SOURCE = SOURCE_STYLUS or SOURCE_TOUCHSCREEN
}
@Rule
@JvmField
val mocks: MockitoRule = MockitoJUnit.rule()
+ @Rule
+ @JvmField
+ val mCheckFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
@Mock
private lateinit var mockA11yController: WindowManagerInternal.AccessibilityControllerInternal
@@ -115,6 +127,9 @@ class AccessibilityInputFilterInputTest {
private lateinit var ams: AccessibilityManagerService
private lateinit var a11yInputFilter: AccessibilityInputFilter
private val touchDeviceId = 1
+ private val fromTouchScreen = allOf(withDeviceId(touchDeviceId), withSource(SOURCE_TOUCHSCREEN))
+ private val stylusDeviceId = 2
+ private val fromStylus = allOf(withDeviceId(stylusDeviceId), withSource(STYLUS_SOURCE))
@Before
fun setUp() {
@@ -156,23 +171,14 @@ class AccessibilityInputFilterInputTest {
enableFeatures(0)
val downTime = SystemClock.uptimeMillis()
- val downEvent = createMotionEvent(
- ACTION_DOWN, downTime, downTime, SOURCE_TOUCHSCREEN, touchDeviceId)
- send(downEvent)
- verifier.assertReceivedMotion(
- allOf(withMotionAction(ACTION_DOWN), withDeviceId(touchDeviceId)))
-
- val moveEvent = createMotionEvent(
- ACTION_MOVE, downTime, SystemClock.uptimeMillis(), SOURCE_TOUCHSCREEN, touchDeviceId)
- send(moveEvent)
- verifier.assertReceivedMotion(
- allOf(withMotionAction(ACTION_MOVE), withDeviceId(touchDeviceId)))
-
- val upEvent = createMotionEvent(
- ACTION_UP, downTime, SystemClock.uptimeMillis(), SOURCE_TOUCHSCREEN, touchDeviceId)
- send(upEvent)
- verifier.assertReceivedMotion(
- allOf(withMotionAction(ACTION_UP), withDeviceId(touchDeviceId)))
+ sendTouchEvent(ACTION_DOWN, downTime, downTime)
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_DOWN)))
+
+ sendTouchEvent(ACTION_MOVE, downTime, SystemClock.uptimeMillis())
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_MOVE)))
+
+ sendTouchEvent(ACTION_UP, downTime, SystemClock.uptimeMillis())
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_UP)))
verifier.assertNoEvents()
}
@@ -186,28 +192,91 @@ class AccessibilityInputFilterInputTest {
enableFeatures(ALL_A11Y_FEATURES)
val downTime = SystemClock.uptimeMillis()
- val downEvent = createMotionEvent(
- ACTION_DOWN, downTime, downTime, SOURCE_TOUCHSCREEN, touchDeviceId)
- send(MotionEvent.obtain(downEvent))
-
+ sendTouchEvent(ACTION_DOWN, downTime, downTime)
// DOWN event gets transformed to HOVER_ENTER
- verifier.assertReceivedMotion(
- allOf(withMotionAction(ACTION_HOVER_ENTER), withDeviceId(touchDeviceId)))
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_HOVER_ENTER)))
// MOVE becomes HOVER_MOVE
- val moveEvent = createMotionEvent(
- ACTION_MOVE, downTime, SystemClock.uptimeMillis(), SOURCE_TOUCHSCREEN, touchDeviceId)
- send(moveEvent)
- verifier.assertReceivedMotion(
- allOf(withMotionAction(ACTION_HOVER_MOVE), withDeviceId(touchDeviceId)))
+ sendTouchEvent(ACTION_MOVE, downTime, SystemClock.uptimeMillis())
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_HOVER_MOVE)))
// UP becomes HOVER_EXIT
- val upEvent = createMotionEvent(
- ACTION_UP, downTime, SystemClock.uptimeMillis(), SOURCE_TOUCHSCREEN, touchDeviceId)
- send(upEvent)
+ sendTouchEvent(ACTION_UP, downTime, SystemClock.uptimeMillis())
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_HOVER_EXIT)))
+
+ verifier.assertNoEvents()
+ }
+
+ /**
+ * Enable all a11y features and send a touchscreen stream of DOWN -> CANCEL -> DOWN events.
+ * These get converted into HOVER_ENTER -> HOVER_EXIT -> HOVER_ENTER events by the input filter.
+ */
+ @Test
+ fun testTouchDownCancelDownWithAllA11yFeatures() {
+ enableFeatures(ALL_A11Y_FEATURES)
+
+ val downTime = SystemClock.uptimeMillis()
+ sendTouchEvent(ACTION_DOWN, downTime, downTime)
+ // DOWN event gets transformed to HOVER_ENTER
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_HOVER_ENTER)))
+
+ // CANCEL becomes HOVER_EXIT
+ sendTouchEvent(ACTION_CANCEL, downTime, SystemClock.uptimeMillis())
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_HOVER_EXIT)))
+
+ // DOWN again! New hover is expected
+ val newDownTime = SystemClock.uptimeMillis()
+ sendTouchEvent(ACTION_DOWN, newDownTime, newDownTime)
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_HOVER_ENTER)))
+
+ verifier.assertNoEvents()
+ }
- verifier.assertReceivedMotion(
- allOf(withMotionAction(ACTION_HOVER_EXIT), withDeviceId(touchDeviceId)))
+ /**
+ * Enable all a11y features and send a stylus stream of DOWN -> CANCEL -> DOWN events.
+ * These get converted into HOVER_ENTER -> HOVER_EXIT -> HOVER_ENTER events by the input filter.
+ * This test is the same as above, but for stylus events.
+ */
+ @Test
+ fun testStylusDownCancelDownWithAllA11yFeatures() {
+ enableFeatures(ALL_A11Y_FEATURES)
+
+ val downTime = SystemClock.uptimeMillis()
+ sendStylusEvent(ACTION_DOWN, downTime, downTime)
+ // DOWN event gets transformed to HOVER_ENTER
+ verifier.assertReceivedMotion(allOf(fromStylus, withMotionAction(ACTION_HOVER_ENTER)))
+
+ // CANCEL becomes HOVER_EXIT
+ sendStylusEvent(ACTION_CANCEL, downTime, SystemClock.uptimeMillis())
+ verifier.assertReceivedMotion(allOf(fromStylus, withMotionAction(ACTION_HOVER_EXIT)))
+
+ // DOWN again! New hover is expected
+ val newDownTime = SystemClock.uptimeMillis()
+ sendStylusEvent(ACTION_DOWN, newDownTime, newDownTime)
+ verifier.assertReceivedMotion(allOf(fromStylus, withMotionAction(ACTION_HOVER_ENTER)))
+
+ verifier.assertNoEvents()
+ }
+
+ /**
+ * Enable all a11y features and send a stylus stream and then a touch stream.
+ */
+ @Test
+ fun testStylusThenTouch() {
+ enableFeatures(ALL_A11Y_FEATURES)
+
+ val downTime = SystemClock.uptimeMillis()
+ sendStylusEvent(ACTION_DOWN, downTime, downTime)
+ // DOWN event gets transformed to HOVER_ENTER
+ verifier.assertReceivedMotion(allOf(fromStylus, withMotionAction(ACTION_HOVER_ENTER)))
+
+ // CANCEL becomes HOVER_EXIT
+ sendStylusEvent(ACTION_CANCEL, downTime, SystemClock.uptimeMillis())
+ verifier.assertReceivedMotion(allOf(fromStylus, withMotionAction(ACTION_HOVER_EXIT)))
+
+ val newDownTime = SystemClock.uptimeMillis()
+ sendTouchEvent(ACTION_DOWN, newDownTime, newDownTime)
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_HOVER_ENTER)))
verifier.assertNoEvents()
}
@@ -223,26 +292,18 @@ class AccessibilityInputFilterInputTest {
enableFeatures(ALL_A11Y_FEATURES)
val downTime = SystemClock.uptimeMillis()
- val downEvent = createMotionEvent(
- ACTION_DOWN, downTime, downTime, SOURCE_TOUCHSCREEN, touchDeviceId)
- send(MotionEvent.obtain(downEvent))
+ sendTouchEvent(ACTION_DOWN, downTime, downTime)
// DOWN event gets transformed to HOVER_ENTER
- verifier.assertReceivedMotion(
- allOf(withMotionAction(ACTION_HOVER_ENTER), withDeviceId(touchDeviceId)))
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_HOVER_ENTER)))
verifier.assertNoEvents()
enableFeatures(0)
- verifier.assertReceivedMotion(
- allOf(withMotionAction(ACTION_HOVER_EXIT), withDeviceId(touchDeviceId)))
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_HOVER_EXIT)))
verifier.assertNoEvents()
- val moveEvent = createMotionEvent(
- ACTION_MOVE, downTime, SystemClock.uptimeMillis(), SOURCE_TOUCHSCREEN, touchDeviceId)
- send(moveEvent)
- val upEvent = createMotionEvent(
- ACTION_UP, downTime, SystemClock.uptimeMillis(), SOURCE_TOUCHSCREEN, touchDeviceId)
- send(upEvent)
+ sendTouchEvent(ACTION_MOVE, downTime, SystemClock.uptimeMillis())
+ sendTouchEvent(ACTION_UP, downTime, SystemClock.uptimeMillis())
// As the original gesture continues, no additional events should be getting sent by the
// filter because the HOVER_EXIT above already effectively finished the current gesture and
// the DOWN event was never sent to the host.
@@ -250,10 +311,148 @@ class AccessibilityInputFilterInputTest {
// Bug: the down event was swallowed, so the remainder of the gesture should be swallowed
// too. However, the MOVE and UP events are currently passed back to the dispatcher.
// TODO(b/310014874) - ensure a11y sends consistent input streams to the dispatcher
- verifier.assertReceivedMotion(
- allOf(withMotionAction(ACTION_MOVE), withDeviceId(touchDeviceId)))
- verifier.assertReceivedMotion(
- allOf(withMotionAction(ACTION_UP), withDeviceId(touchDeviceId)))
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_MOVE)))
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_UP)))
+
+ verifier.assertNoEvents()
+ }
+
+ /**
+ * Check multi-device behaviour when all a11y features are disabled. The events should pass
+ * through unmodified, but only from the active (first) device.
+ * The events from the inactive device should be dropped.
+ * In this test, we are injecting a touchscreen event stream and a stylus event stream,
+ * interleaved.
+ */
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_HANDLE_MULTI_DEVICE_INPUT)
+ fun testMultiDeviceEventsWithoutA11yFeatures() {
+ enableFeatures(0)
+
+ val touchDownTime = SystemClock.uptimeMillis()
+
+ // Touch device - ACTION_DOWN
+ sendTouchEvent(ACTION_DOWN, touchDownTime, touchDownTime)
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_DOWN)))
+
+ // Stylus device - ACTION_DOWN
+ val stylusDownTime = SystemClock.uptimeMillis()
+ sendStylusEvent(ACTION_DOWN, stylusDownTime, stylusDownTime)
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_CANCEL)))
+ verifier.assertReceivedMotion(allOf(fromStylus, withMotionAction(ACTION_DOWN)))
+
+ // Touch device - ACTION_MOVE
+ sendTouchEvent(ACTION_MOVE, touchDownTime, SystemClock.uptimeMillis())
+ // Touch event is dropped
+ verifier.assertNoEvents()
+
+ // Stylus device - ACTION_MOVE
+ sendStylusEvent(ACTION_MOVE, stylusDownTime, SystemClock.uptimeMillis())
+ verifier.assertReceivedMotion(allOf(fromStylus, withMotionAction(ACTION_MOVE)))
+
+ // Touch device - ACTION_UP
+ sendTouchEvent(ACTION_UP, touchDownTime, SystemClock.uptimeMillis())
+ // Touch event is dropped
+ verifier.assertNoEvents()
+
+ // Stylus device - ACTION_UP
+ sendStylusEvent(ACTION_UP, stylusDownTime, SystemClock.uptimeMillis())
+ verifier.assertReceivedMotion(allOf(fromStylus, withMotionAction(ACTION_UP)))
+
+ verifier.assertNoEvents()
+ }
+
+ /**
+ * Check multi-device behaviour when all a11y features are enabled. The events should be
+ * modified accordingly, like DOWN events getting converted to hovers.
+ * Only a single device should be active (the latest device to start a new gesture).
+ * In this test, we are injecting a touchscreen event stream and a stylus event stream,
+ * interleaved.
+ */
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_HANDLE_MULTI_DEVICE_INPUT)
+ fun testMultiDeviceEventsWithAllA11yFeatures() {
+ enableFeatures(ALL_A11Y_FEATURES)
+
+ // Touch device - ACTION_DOWN
+ val touchDownTime = SystemClock.uptimeMillis()
+ sendTouchEvent(ACTION_DOWN, touchDownTime, touchDownTime)
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_HOVER_ENTER)))
+
+ // Stylus device - ACTION_DOWN
+ val stylusDownTime = SystemClock.uptimeMillis()
+ sendStylusEvent(ACTION_DOWN, stylusDownTime, stylusDownTime)
+ // Touch is canceled and stylus is started
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_HOVER_EXIT)))
+ verifier.assertReceivedMotion(allOf(fromStylus, withMotionAction(ACTION_HOVER_ENTER)))
+
+ // Touch device - ACTION_MOVE
+ sendTouchEvent(ACTION_MOVE, touchDownTime, SystemClock.uptimeMillis())
+ // Stylus is active now; touch is ignored
+ verifier.assertNoEvents()
+
+ // Stylus device - ACTION_MOVE
+ sendStylusEvent(ACTION_MOVE, stylusDownTime, SystemClock.uptimeMillis())
+ verifier.assertReceivedMotion(allOf(fromStylus, withMotionAction(ACTION_HOVER_MOVE)))
+
+ // Touch device - ACTION_UP
+ sendTouchEvent(ACTION_UP, touchDownTime, SystemClock.uptimeMillis())
+ // Stylus is still active; touch is ignored
+ verifier.assertNoEvents()
+
+ sendStylusEvent(ACTION_UP, stylusDownTime, SystemClock.uptimeMillis())
+ verifier.assertReceivedMotion(allOf(fromStylus, withMotionAction(ACTION_HOVER_EXIT)))
+
+ // Now stylus is done, and a new touch gesture will work!
+ val newTouchDownTime = SystemClock.uptimeMillis()
+ sendTouchEvent(ACTION_DOWN, newTouchDownTime, newTouchDownTime)
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_HOVER_ENTER)))
+
+ verifier.assertNoEvents()
+ }
+
+ /**
+ * Check multi-device behaviour when all a11y features are enabled. The events should be
+ * modified accordingly, like DOWN events getting converted to hovers.
+ * Only a single device should be active at a given time. The touch events start and end
+ * while stylus is active. Check that the latest device is always given preference.
+ */
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_HANDLE_MULTI_DEVICE_INPUT)
+ fun testStylusWithTouchInTheMiddle() {
+ enableFeatures(ALL_A11Y_FEATURES)
+
+ // Stylus device - ACTION_DOWN
+ val stylusDownTime = SystemClock.uptimeMillis()
+ sendStylusEvent(ACTION_DOWN, stylusDownTime, stylusDownTime)
+ verifier.assertReceivedMotion(allOf(fromStylus, withMotionAction(ACTION_HOVER_ENTER)))
+
+ // Touch device - ACTION_DOWN
+ val touchDownTime = SystemClock.uptimeMillis()
+ sendTouchEvent(ACTION_DOWN, touchDownTime, touchDownTime)
+ // Touch DOWN causes stylus to get canceled
+ verifier.assertReceivedMotion(allOf(fromStylus, withMotionAction(ACTION_HOVER_EXIT)))
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_HOVER_ENTER)))
+
+ // Touch device - ACTION_MOVE
+ sendTouchEvent(ACTION_MOVE, touchDownTime, SystemClock.uptimeMillis())
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_HOVER_MOVE)))
+
+ sendStylusEvent(ACTION_MOVE, stylusDownTime, SystemClock.uptimeMillis())
+ // Stylus is ignored because touch is active now
+ verifier.assertNoEvents()
+
+ sendTouchEvent(ACTION_UP, touchDownTime, SystemClock.uptimeMillis())
+ verifier.assertReceivedMotion(allOf(fromTouchScreen, withMotionAction(ACTION_HOVER_EXIT)))
+
+ sendStylusEvent(ACTION_UP, stylusDownTime, SystemClock.uptimeMillis())
+ // The UP stylus event is also ignored
+ verifier.assertNoEvents()
+
+ // Now stylus works again, because touch gesture is finished
+ val newStylusDownTime = SystemClock.uptimeMillis()
+ sendStylusEvent(ACTION_DOWN, newStylusDownTime, newStylusDownTime)
+ verifier.assertReceivedMotion(allOf(fromStylus, withMotionAction(ACTION_HOVER_ENTER)))
verifier.assertNoEvents()
}
@@ -264,6 +463,20 @@ class AccessibilityInputFilterInputTest {
return display
}
+ private fun sendTouchEvent(action: Int, downTime: Long, eventTime: Long) {
+ if (action == ACTION_DOWN) {
+ assertEquals(downTime, eventTime)
+ }
+ send(createMotionEvent(action, downTime, eventTime, SOURCE_TOUCHSCREEN, touchDeviceId))
+ }
+
+ private fun sendStylusEvent(action: Int, downTime: Long, eventTime: Long) {
+ if (action == ACTION_DOWN) {
+ assertEquals(downTime, eventTime)
+ }
+ send(createMotionEvent(action, downTime, eventTime, STYLUS_SOURCE, stylusDeviceId))
+ }
+
private fun send(event: InputEvent) {
// We need to make a copy of the event before sending it to the filter, because the filter
// will recycle it, but the caller of this function might want to still be able to use
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
index b12d6da0f289..89d146da1b75 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
@@ -41,12 +41,12 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.annotation.Nullable;
import android.graphics.Region;
import android.os.IBinder;
import android.os.LocaleList;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.text.TextUtils;
import android.util.SparseArray;
import android.view.Display;
import android.view.IWindow;
@@ -452,7 +452,9 @@ public class AccessibilityWindowManagerTest {
false, mockHostToken, USER_SYSTEM_ID);
final int embeddedWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
false, mockEmbeddedToken, USER_SYSTEM_ID);
+
mA11yWindowManager.associateEmbeddedHierarchyLocked(mockHostToken, mockEmbeddedToken);
+
final int resolvedWindowId = mA11yWindowManager.resolveParentWindowIdLocked(
embeddedWindowId);
assertEquals(hostWindowId, resolvedWindowId);
@@ -467,10 +469,13 @@ public class AccessibilityWindowManagerTest {
false, mockHostToken, USER_SYSTEM_ID);
final int embeddedWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
false, mockEmbeddedToken, USER_SYSTEM_ID);
+
mA11yWindowManager.associateEmbeddedHierarchyLocked(mockHostToken, mockEmbeddedToken);
mA11yWindowManager.disassociateEmbeddedHierarchyLocked(mockEmbeddedToken);
+
final int resolvedWindowId = mA11yWindowManager.resolveParentWindowIdLocked(
embeddedWindowId);
+ assertNotEquals(hostWindowId, resolvedWindowId);
assertEquals(embeddedWindowId, resolvedWindowId);
}
@@ -917,7 +922,7 @@ public class AccessibilityWindowManagerTest {
final AccessibilityWindowInfo a11yWindow = mA11yWindowManager.findA11yWindowInfoByIdLocked(
windowId);
- assertTrue(TextUtils.equals(layoutParams.accessibilityTitle, a11yWindow.getTitle()));
+ assertEquals(toString(layoutParams.accessibilityTitle), toString(a11yWindow.getTitle()));
}
@Test
@@ -1057,7 +1062,7 @@ public class AccessibilityWindowManagerTest {
when(mockWindowToken.asBinder()).thenReturn(mockWindowBinder);
when(mMockA11ySecurityPolicy.isCallerInteractingAcrossUsers(userId))
.thenReturn(bGlobal);
- when(mMockWindowManagerInternal.getDisplayIdForWindow(mockWindowToken.asBinder()))
+ when(mMockWindowManagerInternal.getDisplayIdForWindow(mockWindowBinder))
.thenReturn(displayId);
int windowId = mA11yWindowManager.addAccessibilityInteractionConnection(
@@ -1077,7 +1082,7 @@ public class AccessibilityWindowManagerTest {
when(mockWindowToken.asBinder()).thenReturn(mockWindowBinder);
when(mMockA11ySecurityPolicy.isCallerInteractingAcrossUsers(userId))
.thenReturn(bGlobal);
- when(mMockWindowManagerInternal.getDisplayIdForWindow(mockWindowToken.asBinder()))
+ when(mMockWindowManagerInternal.getDisplayIdForWindow(mockWindowBinder))
.thenReturn(displayId);
int windowId = mA11yWindowManager.addAccessibilityInteractionConnection(
@@ -1148,6 +1153,11 @@ public class AccessibilityWindowManagerTest {
}
}
+ @Nullable
+ private static String toString(@Nullable CharSequence cs) {
+ return cs == null ? null : cs.toString();
+ }
+
static class DisplayIdMatcher extends TypeSafeMatcher<AccessibilityEvent> {
private final int mDisplayId;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/BrailleDisplayConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/BrailleDisplayConnectionTest.java
index 7c278cedbcc5..aec3f451fac6 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/BrailleDisplayConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/BrailleDisplayConnectionTest.java
@@ -17,24 +17,41 @@
package com.android.server.accessibility;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.accessibilityservice.BrailleDisplayController;
+import android.content.Context;
import android.os.Bundle;
+import android.os.IBinder;
import android.testing.DexmakerShareClassLoaderRule;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.internal.util.HexDump;
+
import com.google.common.truth.Expect;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.experimental.runners.Enclosed;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.io.File;
import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Collection;
import java.util.List;
/**
@@ -42,118 +59,265 @@ import java.util.List;
*
* <p>Prefer adding new tests in CTS where possible.
*/
+@RunWith(Enclosed.class)
public class BrailleDisplayConnectionTest {
- private static final Path NULL_PATH = Path.of("/dev/null");
-
- private BrailleDisplayConnection mBrailleDisplayConnection;
- @Mock
- private BrailleDisplayConnection.NativeInterface mNativeInterface;
- @Mock
- private AccessibilityServiceConnection mServiceConnection;
-
- @Rule
- public final Expect expect = Expect.create();
-
- // To mock package-private class
- @Rule
- public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
- new DexmakerShareClassLoaderRule();
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mBrailleDisplayConnection = new BrailleDisplayConnection(new Object(), mServiceConnection);
- }
- @Test
- public void defaultNativeScanner_getReportDescriptor_returnsDescriptor() {
- int descriptorSize = 4;
- byte[] descriptor = {0xB, 0xE, 0xE, 0xF};
- when(mNativeInterface.getHidrawDescSize(anyInt())).thenReturn(descriptorSize);
- when(mNativeInterface.getHidrawDesc(anyInt(), eq(descriptorSize))).thenReturn(descriptor);
+ public static class ScannerTest {
+ private static final Path NULL_PATH = Path.of("/dev/null");
- BrailleDisplayConnection.BrailleDisplayScanner scanner =
- mBrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
+ private BrailleDisplayConnection mBrailleDisplayConnection;
+ @Mock
+ private BrailleDisplayConnection.NativeInterface mNativeInterface;
+ @Mock
+ private AccessibilityServiceConnection mServiceConnection;
- assertThat(scanner.getDeviceReportDescriptor(NULL_PATH)).isEqualTo(descriptor);
- }
+ @Rule
+ public final Expect expect = Expect.create();
- @Test
- public void defaultNativeScanner_getReportDescriptor_invalidSize_returnsNull() {
- when(mNativeInterface.getHidrawDescSize(anyInt())).thenReturn(0);
+ private Context mContext;
- BrailleDisplayConnection.BrailleDisplayScanner scanner =
- mBrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
+ // To mock package-private class
+ @Rule
+ public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
+ new DexmakerShareClassLoaderRule();
- assertThat(scanner.getDeviceReportDescriptor(NULL_PATH)).isNull();
- }
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ when(mServiceConnection.isConnectedLocked()).thenReturn(true);
+ mBrailleDisplayConnection =
+ spy(new BrailleDisplayConnection(new Object(), mServiceConnection));
+ }
- @Test
- public void defaultNativeScanner_getUniqueId_returnsUniq() {
- String macAddress = "12:34:56:78";
- when(mNativeInterface.getHidrawUniq(anyInt())).thenReturn(macAddress);
+ @Test
+ public void defaultNativeScanner_getHidrawNodePaths_returnsHidrawPaths() throws Exception {
+ File testDir = mContext.getFilesDir();
+ Path hidrawNode0 = Path.of(testDir.getPath(), "hidraw0");
+ Path hidrawNode1 = Path.of(testDir.getPath(), "hidraw1");
+ Path otherDevice = Path.of(testDir.getPath(), "otherDevice");
+ Path[] nodePaths = {hidrawNode0, hidrawNode1, otherDevice};
+ try {
+ for (Path node : nodePaths) {
+ assertThat(node.toFile().createNewFile()).isTrue();
+ }
- BrailleDisplayConnection.BrailleDisplayScanner scanner =
- mBrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
+ BrailleDisplayConnection.BrailleDisplayScanner scanner =
+ mBrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
- assertThat(scanner.getUniqueId(NULL_PATH)).isEqualTo(macAddress);
- }
+ assertThat(scanner.getHidrawNodePaths(testDir.toPath()))
+ .containsExactly(hidrawNode0, hidrawNode1);
+ } finally {
+ for (Path node : nodePaths) {
+ node.toFile().delete();
+ }
+ }
+ }
- @Test
- public void defaultNativeScanner_getDeviceBusType_busUsb() {
- when(mNativeInterface.getHidrawBusType(anyInt()))
- .thenReturn(BrailleDisplayConnection.BUS_USB);
+ @Test
+ public void defaultNativeScanner_getReportDescriptor_returnsDescriptor() {
+ int descriptorSize = 4;
+ byte[] descriptor = {0xB, 0xE, 0xE, 0xF};
+ when(mNativeInterface.getHidrawDescSize(anyInt())).thenReturn(descriptorSize);
+ when(mNativeInterface.getHidrawDesc(anyInt(), eq(descriptorSize))).thenReturn(
+ descriptor);
- BrailleDisplayConnection.BrailleDisplayScanner scanner =
- mBrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
+ BrailleDisplayConnection.BrailleDisplayScanner scanner =
+ mBrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
- assertThat(scanner.getDeviceBusType(NULL_PATH))
- .isEqualTo(BrailleDisplayConnection.BUS_USB);
- }
+ assertThat(scanner.getDeviceReportDescriptor(NULL_PATH)).isEqualTo(descriptor);
+ }
+
+ @Test
+ public void defaultNativeScanner_getReportDescriptor_invalidSize_returnsNull() {
+ when(mNativeInterface.getHidrawDescSize(anyInt())).thenReturn(0);
+
+ BrailleDisplayConnection.BrailleDisplayScanner scanner =
+ mBrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
+
+ assertThat(scanner.getDeviceReportDescriptor(NULL_PATH)).isNull();
+ }
+
+ @Test
+ public void defaultNativeScanner_getUniqueId_returnsUniq() {
+ String macAddress = "12:34:56:78";
+ when(mNativeInterface.getHidrawUniq(anyInt())).thenReturn(macAddress);
+
+ BrailleDisplayConnection.BrailleDisplayScanner scanner =
+ mBrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
+
+ assertThat(scanner.getUniqueId(NULL_PATH)).isEqualTo(macAddress);
+ }
+
+ @Test
+ public void defaultNativeScanner_getDeviceBusType_busUsb() {
+ when(mNativeInterface.getHidrawBusType(anyInt()))
+ .thenReturn(BrailleDisplayConnection.BUS_USB);
+
+ BrailleDisplayConnection.BrailleDisplayScanner scanner =
+ mBrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
+
+ assertThat(scanner.getDeviceBusType(NULL_PATH))
+ .isEqualTo(BrailleDisplayConnection.BUS_USB);
+ }
- @Test
- public void defaultNativeScanner_getDeviceBusType_busBluetooth() {
- when(mNativeInterface.getHidrawBusType(anyInt()))
- .thenReturn(BrailleDisplayConnection.BUS_BLUETOOTH);
+ @Test
+ public void defaultNativeScanner_getDeviceBusType_busBluetooth() {
+ when(mNativeInterface.getHidrawBusType(anyInt()))
+ .thenReturn(BrailleDisplayConnection.BUS_BLUETOOTH);
- BrailleDisplayConnection.BrailleDisplayScanner scanner =
- mBrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
+ BrailleDisplayConnection.BrailleDisplayScanner scanner =
+ mBrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
- assertThat(scanner.getDeviceBusType(NULL_PATH))
- .isEqualTo(BrailleDisplayConnection.BUS_BLUETOOTH);
+ assertThat(scanner.getDeviceBusType(NULL_PATH))
+ .isEqualTo(BrailleDisplayConnection.BUS_BLUETOOTH);
+ }
+
+ @Test
+ public void write_bypassesServiceSideCheckWithLargeBuffer_disconnects() {
+ Mockito.doNothing().when(mBrailleDisplayConnection).disconnect();
+ mBrailleDisplayConnection.write(
+ new byte[IBinder.getSuggestedMaxIpcSizeBytes() * 2]);
+
+ verify(mBrailleDisplayConnection).disconnect();
+ }
+
+ @Test
+ public void write_notConnected_throwsIllegalStateException() {
+ when(mServiceConnection.isConnectedLocked()).thenReturn(false);
+
+ assertThrows(IllegalStateException.class,
+ () -> mBrailleDisplayConnection.write(new byte[1]));
+ }
+
+ @Test
+ public void write_unableToCreateWriteStream_disconnects() {
+ Mockito.doNothing().when(mBrailleDisplayConnection).disconnect();
+ // mBrailleDisplayConnection#connectLocked was never called so the
+ // connection's mHidrawNode is still null. This will throw an exception
+ // when attempting to create FileOutputStream on the node.
+ mBrailleDisplayConnection.write(new byte[1]);
+
+ verify(mBrailleDisplayConnection).disconnect();
+ }
+
+ // BrailleDisplayConnection#setTestData() is used to enable CTS testing with
+ // test Braille display data, but its own implementation should also be tested
+ // so that issues in this helper don't cause confusing failures in CTS.
+
+ @Test
+ public void setTestData_scannerReturnsTestData() {
+ Bundle bd1 = new Bundle(), bd2 = new Bundle();
+
+ Path path1 = Path.of("/dev/path1"), path2 = Path.of("/dev/path2");
+ bd1.putString(BrailleDisplayController.TEST_BRAILLE_DISPLAY_HIDRAW_PATH,
+ path1.toString());
+ bd2.putString(BrailleDisplayController.TEST_BRAILLE_DISPLAY_HIDRAW_PATH,
+ path2.toString());
+ byte[] desc1 = {0xB, 0xE}, desc2 = {0xE, 0xF};
+ bd1.putByteArray(BrailleDisplayController.TEST_BRAILLE_DISPLAY_DESCRIPTOR, desc1);
+ bd2.putByteArray(BrailleDisplayController.TEST_BRAILLE_DISPLAY_DESCRIPTOR, desc2);
+ String uniq1 = "uniq1", uniq2 = "uniq2";
+ bd1.putString(BrailleDisplayController.TEST_BRAILLE_DISPLAY_UNIQUE_ID, uniq1);
+ bd2.putString(BrailleDisplayController.TEST_BRAILLE_DISPLAY_UNIQUE_ID, uniq2);
+ int bus1 = BrailleDisplayConnection.BUS_USB, bus2 =
+ BrailleDisplayConnection.BUS_BLUETOOTH;
+ bd1.putBoolean(BrailleDisplayController.TEST_BRAILLE_DISPLAY_BUS_BLUETOOTH,
+ bus1 == BrailleDisplayConnection.BUS_BLUETOOTH);
+ bd2.putBoolean(BrailleDisplayController.TEST_BRAILLE_DISPLAY_BUS_BLUETOOTH,
+ bus2 == BrailleDisplayConnection.BUS_BLUETOOTH);
+
+ BrailleDisplayConnection.BrailleDisplayScanner scanner =
+ mBrailleDisplayConnection.setTestData(List.of(bd1, bd2));
+
+ expect.that(scanner.getHidrawNodePaths(Path.of("/dev"))).containsExactly(path1, path2);
+ expect.that(scanner.getDeviceReportDescriptor(path1)).isEqualTo(desc1);
+ expect.that(scanner.getDeviceReportDescriptor(path2)).isEqualTo(desc2);
+ expect.that(scanner.getUniqueId(path1)).isEqualTo(uniq1);
+ expect.that(scanner.getUniqueId(path2)).isEqualTo(uniq2);
+ expect.that(scanner.getDeviceBusType(path1)).isEqualTo(bus1);
+ expect.that(scanner.getDeviceBusType(path2)).isEqualTo(bus2);
+ }
+
+ @Test
+ public void setTestData_emptyTestData_returnsNullNodePaths() {
+ BrailleDisplayConnection.BrailleDisplayScanner scanner =
+ mBrailleDisplayConnection.setTestData(List.of());
+
+ expect.that(scanner.getHidrawNodePaths(Path.of("/dev"))).isNull();
+ }
}
- // BrailleDisplayConnection#setTestData() is used to enable CTS testing with
- // test Braille display data, but its own implementation should also be tested
- // so that issues in this helper don't cause confusing failures in CTS.
- @Test
- public void setTestData_scannerReturnsTestData() {
- Bundle bd1 = new Bundle(), bd2 = new Bundle();
-
- Path path1 = Path.of("/dev/path1"), path2 = Path.of("/dev/path2");
- bd1.putString(BrailleDisplayController.TEST_BRAILLE_DISPLAY_HIDRAW_PATH, path1.toString());
- bd2.putString(BrailleDisplayController.TEST_BRAILLE_DISPLAY_HIDRAW_PATH, path2.toString());
- byte[] desc1 = {0xB, 0xE}, desc2 = {0xE, 0xF};
- bd1.putByteArray(BrailleDisplayController.TEST_BRAILLE_DISPLAY_DESCRIPTOR, desc1);
- bd2.putByteArray(BrailleDisplayController.TEST_BRAILLE_DISPLAY_DESCRIPTOR, desc2);
- String uniq1 = "uniq1", uniq2 = "uniq2";
- bd1.putString(BrailleDisplayController.TEST_BRAILLE_DISPLAY_UNIQUE_ID, uniq1);
- bd2.putString(BrailleDisplayController.TEST_BRAILLE_DISPLAY_UNIQUE_ID, uniq2);
- int bus1 = BrailleDisplayConnection.BUS_USB, bus2 = BrailleDisplayConnection.BUS_BLUETOOTH;
- bd1.putBoolean(BrailleDisplayController.TEST_BRAILLE_DISPLAY_BUS_BLUETOOTH,
- bus1 == BrailleDisplayConnection.BUS_BLUETOOTH);
- bd2.putBoolean(BrailleDisplayController.TEST_BRAILLE_DISPLAY_BUS_BLUETOOTH,
- bus2 == BrailleDisplayConnection.BUS_BLUETOOTH);
-
- BrailleDisplayConnection.BrailleDisplayScanner scanner =
- mBrailleDisplayConnection.setTestData(List.of(bd1, bd2));
-
- expect.that(scanner.getHidrawNodePaths()).containsExactly(path1, path2);
- expect.that(scanner.getDeviceReportDescriptor(path1)).isEqualTo(desc1);
- expect.that(scanner.getDeviceReportDescriptor(path2)).isEqualTo(desc2);
- expect.that(scanner.getUniqueId(path1)).isEqualTo(uniq1);
- expect.that(scanner.getUniqueId(path2)).isEqualTo(uniq2);
- expect.that(scanner.getDeviceBusType(path1)).isEqualTo(bus1);
- expect.that(scanner.getDeviceBusType(path2)).isEqualTo(bus2);
+ @RunWith(Parameterized.class)
+ public static class BrailleDisplayDescriptorTest {
+ @Parameterized.Parameters(name = "{0}")
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][]{
+ {"match_BdPage", new byte[]{
+ // Just one item, defines the BD page
+ 0x05, 0x41}},
+ {"match_BdPageAfterAnotherPage", new byte[]{
+ // One item defines another page
+ 0x05, 0x01,
+ // Next item defines BD page
+ 0x05, 0x41}},
+ {"match_BdPageAfterSizeZeroItem", new byte[]{
+ // Size-zero item (last 2 bits are 00)
+ 0x00,
+ // Next item defines BD page
+ 0x05, 0x41}},
+ {"match_BdPageAfterSizeOneItem", new byte[]{
+ // Size-one item (last 2 bits are 01)
+ 0x01, 0x7F,
+ // Next item defines BD page
+ 0x05, 0x41}},
+ {"match_BdPageAfterSizeTwoItem", new byte[]{
+ // Size-two item (last 2 bits are 10)
+ 0x02, 0x7F, 0x7F,
+ 0x05, 0x41}},
+ {"match_BdPageAfterSizeFourItem", new byte[]{
+ // Size-four item (last 2 bits are 11)
+ 0x03, 0x7F, 0x7F, 0x7F, 0x7F,
+ 0x05, 0x41}},
+ {"match_BdPageInBetweenOtherPages", new byte[]{
+ // One item defines another page
+ 0x05, 0x01,
+ // Next item defines BD page
+ 0x05, 0x41,
+ // Next item defines another page
+ 0x05, 0x02}},
+ {"fail_OtherPage", new byte[]{
+ // Just one item, defines another page
+ 0x05, 0x01}},
+ {"fail_BdPageBeforeMissingData", new byte[]{
+ // This item defines BD page
+ 0x05, 0x41,
+ // Next item specifies size-one item (last 2 bits are 01) but
+ // that one data byte is missing; this descriptor is malformed.
+ 0x01}},
+ {"fail_BdPageWithWrongDataSize", new byte[]{
+ // This item defines a page with two-byte ID 0x41 0x7F, not 0x41.
+ 0x06, 0x41, 0x7F}},
+ {"fail_LongItem", new byte[]{
+ // Item has type bits 1111, indicating Long Item.
+ (byte) 0xF0}},
+ });
+ }
+
+
+ @Parameterized.Parameter(0)
+ public String mTestName;
+ @Parameterized.Parameter(1)
+ public byte[] mDescriptor;
+
+ @Test
+ public void isBrailleDisplay() {
+ final boolean expectedMatch = mTestName.startsWith("match_");
+ assertWithMessage(
+ "Expected isBrailleDisplay==" + expectedMatch
+ + " for descriptor " + HexDump.toHexString(mDescriptor))
+ .that(BrailleDisplayConnection.isBrailleDisplay(mDescriptor))
+ .isEqualTo(expectedMatch);
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
index b224773a9eb1..f3cd0d6b961e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
@@ -1360,7 +1360,7 @@ public class FullScreenMagnificationControllerTest {
DISPLAY_0, scale, Float.NaN, Float.NaN, true, SERVICE_ID_1);
checkActivatedAndMagnifying(/* activated= */ true, /* magnifying= */ false, DISPLAY_0);
- verify(mMockWindowManager).setForceShowMagnifiableBounds(DISPLAY_0, true);
+ verify(mMockWindowManager).setFullscreenMagnificationActivated(DISPLAY_0, true);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index cd904eb562de..a0c4b5e26c3f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -150,6 +150,9 @@ public class MagnificationControllerTest {
@Mock
private DisplayManagerInternal mDisplayManagerInternal;
+ @Mock
+ private Scroller mMockScroller;
+
// To mock package-private class
@Rule
public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
@@ -208,7 +211,7 @@ public class MagnificationControllerTest {
mScaleProvider,
() -> null,
ConcurrentUtils.DIRECT_EXECUTOR,
- () -> new Scroller(mContext),
+ () -> mMockScroller,
() -> mTimeAnimator));
mScreenMagnificationController.register(TEST_DISPLAY);
diff --git a/services/tests/servicestests/src/com/android/server/adaptiveauth/AdaptiveAuthServiceTest.java b/services/tests/servicestests/src/com/android/server/adaptiveauth/AdaptiveAuthServiceTest.java
new file mode 100644
index 000000000000..08a65292cf20
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/adaptiveauth/AdaptiveAuthServiceTest.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2024 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.adaptiveauth;
+
+import static android.adaptiveauth.Flags.FLAG_ENABLE_ADAPTIVE_AUTH;
+import static android.adaptiveauth.Flags.FLAG_REPORT_BIOMETRIC_AUTH_ATTEMPTS;
+import static android.security.Flags.FLAG_REPORT_PRIMARY_AUTH_ATTEMPTS;
+
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST;
+import static com.android.server.adaptiveauth.AdaptiveAuthService.MAX_ALLOWED_FAILED_AUTH_ATTEMPTS;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.KeyguardManager;
+import android.content.Context;
+import android.hardware.biometrics.AuthenticationStateListener;
+import android.hardware.biometrics.BiometricManager;
+import android.os.RemoteException;
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockSettingsInternal;
+import com.android.internal.widget.LockSettingsStateListener;
+import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
+import com.android.server.wm.WindowManagerInternal;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * atest FrameworksServicesTests:AdaptiveAuthServiceTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AdaptiveAuthServiceTest {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ private static final int PRIMARY_USER_ID = 0;
+ private static final int MANAGED_PROFILE_USER_ID = 12;
+ private static final int DEFAULT_COUNT_FAILED_AUTH_ATTEMPTS = 0;
+ private static final int REASON_UNKNOWN = 0; // BiometricRequestConstants.RequestReason
+
+ private Context mContext;
+ private AdaptiveAuthService mAdaptiveAuthService;
+
+ @Mock
+ LockPatternUtils mLockPatternUtils;
+ @Mock
+ private LockSettingsInternal mLockSettings;
+ @Mock
+ private BiometricManager mBiometricManager;
+ @Mock
+ private KeyguardManager mKeyguardManager;
+ @Mock
+ private WindowManagerInternal mWindowManager;
+ @Mock
+ private UserManagerInternal mUserManager;
+
+ @Captor
+ ArgumentCaptor<LockSettingsStateListener> mLockSettingsStateListenerCaptor;
+ @Captor
+ ArgumentCaptor<AuthenticationStateListener> mAuthenticationStateListenerCaptor;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mSetFlagsRule.enableFlags(FLAG_ENABLE_ADAPTIVE_AUTH);
+ mSetFlagsRule.enableFlags(FLAG_REPORT_PRIMARY_AUTH_ATTEMPTS);
+ mSetFlagsRule.enableFlags(FLAG_REPORT_BIOMETRIC_AUTH_ATTEMPTS);
+
+ mContext = spy(ApplicationProvider.getApplicationContext());
+ when(mContext.getSystemService(BiometricManager.class)).thenReturn(mBiometricManager);
+ when(mContext.getSystemService(KeyguardManager.class)).thenReturn(mKeyguardManager);
+
+ LocalServices.removeServiceForTest(LockSettingsInternal.class);
+ LocalServices.addService(LockSettingsInternal.class, mLockSettings);
+ LocalServices.removeServiceForTest(WindowManagerInternal.class);
+ LocalServices.addService(WindowManagerInternal.class, mWindowManager);
+ LocalServices.removeServiceForTest(UserManagerInternal.class);
+ LocalServices.addService(UserManagerInternal.class, mUserManager);
+
+ mAdaptiveAuthService = new AdaptiveAuthService(mContext, mLockPatternUtils);
+ mAdaptiveAuthService.init();
+
+ verify(mLockSettings).registerLockSettingsStateListener(
+ mLockSettingsStateListenerCaptor.capture());
+ verify(mBiometricManager).registerAuthenticationStateListener(
+ mAuthenticationStateListenerCaptor.capture());
+
+ // Set PRIMARY_USER_ID as the parent of MANAGED_PROFILE_USER_ID
+ when(mUserManager.getProfileParentId(eq(MANAGED_PROFILE_USER_ID)))
+ .thenReturn(PRIMARY_USER_ID);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ LocalServices.removeServiceForTest(LockSettingsInternal.class);
+ LocalServices.removeServiceForTest(WindowManagerInternal.class);
+ LocalServices.removeServiceForTest(UserManagerInternal.class);
+ }
+
+ @Test
+ public void testReportAuthAttempt_primaryAuthSucceeded()
+ throws RemoteException {
+ mLockSettingsStateListenerCaptor.getValue().onAuthenticationSucceeded(PRIMARY_USER_ID);
+ waitForAuthCompletion();
+
+ verifyNotLockDevice(DEFAULT_COUNT_FAILED_AUTH_ATTEMPTS /* expectedCntFailedAttempts */,
+ PRIMARY_USER_ID);
+ }
+
+ @Test
+ public void testReportAuthAttempt_primaryAuthFailed_once()
+ throws RemoteException {
+ mLockSettingsStateListenerCaptor.getValue().onAuthenticationFailed(PRIMARY_USER_ID);
+ waitForAuthCompletion();
+
+ verifyNotLockDevice(1 /* expectedCntFailedAttempts */, PRIMARY_USER_ID);
+ }
+
+ @Test
+ public void testReportAuthAttempt_primaryAuthFailed_multiple_deviceCurrentlyLocked()
+ throws RemoteException {
+ // Device is currently locked and Keyguard is showing
+ when(mKeyguardManager.isDeviceLocked(PRIMARY_USER_ID)).thenReturn(true);
+ when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
+
+ for (int i = 0; i < MAX_ALLOWED_FAILED_AUTH_ATTEMPTS; i++) {
+ mLockSettingsStateListenerCaptor.getValue().onAuthenticationFailed(PRIMARY_USER_ID);
+ }
+ waitForAuthCompletion();
+
+ verifyNotLockDevice(MAX_ALLOWED_FAILED_AUTH_ATTEMPTS /* expectedCntFailedAttempts */,
+ PRIMARY_USER_ID);
+ }
+
+ @Test
+ public void testReportAuthAttempt_primaryAuthFailed_multiple_deviceCurrentlyNotLocked()
+ throws RemoteException {
+ // Device is currently not locked and Keyguard is not showing
+ when(mKeyguardManager.isDeviceLocked(PRIMARY_USER_ID)).thenReturn(false);
+ when(mKeyguardManager.isKeyguardLocked()).thenReturn(false);
+
+ for (int i = 0; i < MAX_ALLOWED_FAILED_AUTH_ATTEMPTS; i++) {
+ mLockSettingsStateListenerCaptor.getValue().onAuthenticationFailed(PRIMARY_USER_ID);
+ }
+ waitForAuthCompletion();
+
+ verifyLockDevice(PRIMARY_USER_ID);
+ }
+
+ @Test
+ public void testReportAuthAttempt_biometricAuthSucceeded()
+ throws RemoteException {
+ mAuthenticationStateListenerCaptor.getValue()
+ .onAuthenticationSucceeded(REASON_UNKNOWN, PRIMARY_USER_ID);
+ waitForAuthCompletion();
+
+ verifyNotLockDevice(DEFAULT_COUNT_FAILED_AUTH_ATTEMPTS /* expectedCntFailedAttempts */,
+ PRIMARY_USER_ID);
+ }
+
+ @Test
+ public void testReportAuthAttempt_biometricAuthFailed_once()
+ throws RemoteException {
+ mAuthenticationStateListenerCaptor.getValue()
+ .onAuthenticationFailed(REASON_UNKNOWN, PRIMARY_USER_ID);
+ waitForAuthCompletion();
+
+ verifyNotLockDevice(1 /* expectedCntFailedAttempts */, PRIMARY_USER_ID);
+ }
+
+ @Test
+ public void testReportAuthAttempt_biometricAuthFailed_multiple_deviceCurrentlyLocked()
+ throws RemoteException {
+ // Device is currently locked and Keyguard is showing
+ when(mKeyguardManager.isDeviceLocked(PRIMARY_USER_ID)).thenReturn(true);
+ when(mKeyguardManager.isKeyguardLocked()).thenReturn(true);
+
+ for (int i = 0; i < MAX_ALLOWED_FAILED_AUTH_ATTEMPTS; i++) {
+ mAuthenticationStateListenerCaptor.getValue()
+ .onAuthenticationFailed(REASON_UNKNOWN, PRIMARY_USER_ID);
+ }
+ waitForAuthCompletion();
+
+ verifyNotLockDevice(MAX_ALLOWED_FAILED_AUTH_ATTEMPTS /* expectedCntFailedAttempts */,
+ PRIMARY_USER_ID);
+ }
+
+ @Test
+ public void testReportAuthAttempt_biometricAuthFailed_multiple_deviceCurrentlyNotLocked()
+ throws RemoteException {
+ // Device is currently not locked and Keyguard is not showing
+ when(mKeyguardManager.isDeviceLocked(PRIMARY_USER_ID)).thenReturn(false);
+ when(mKeyguardManager.isKeyguardLocked()).thenReturn(false);
+
+ for (int i = 0; i < MAX_ALLOWED_FAILED_AUTH_ATTEMPTS; i++) {
+ mAuthenticationStateListenerCaptor.getValue()
+ .onAuthenticationFailed(REASON_UNKNOWN, PRIMARY_USER_ID);
+ }
+ waitForAuthCompletion();
+
+ verifyLockDevice(PRIMARY_USER_ID);
+ }
+
+ @Test
+ public void testReportAuthAttempt_biometricAuthFailedThenPrimaryAuthSucceeded()
+ throws RemoteException {
+ // Three failed biometric auth attempts
+ for (int i = 0; i < 3; i++) {
+ mAuthenticationStateListenerCaptor.getValue()
+ .onAuthenticationFailed(REASON_UNKNOWN, PRIMARY_USER_ID);
+ }
+ // One successful primary auth attempt
+ mLockSettingsStateListenerCaptor.getValue().onAuthenticationSucceeded(PRIMARY_USER_ID);
+ waitForAuthCompletion();
+
+ verifyNotLockDevice(DEFAULT_COUNT_FAILED_AUTH_ATTEMPTS /* expectedCntFailedAttempts */,
+ PRIMARY_USER_ID);
+ }
+
+ @Test
+ public void testReportAuthAttempt_primaryAuthFailedThenBiometricAuthSucceeded()
+ throws RemoteException {
+ // Three failed primary auth attempts
+ for (int i = 0; i < 3; i++) {
+ mLockSettingsStateListenerCaptor.getValue().onAuthenticationFailed(PRIMARY_USER_ID);
+ }
+ // One successful biometric auth attempt
+ mAuthenticationStateListenerCaptor.getValue()
+ .onAuthenticationSucceeded(REASON_UNKNOWN, PRIMARY_USER_ID);
+ waitForAuthCompletion();
+
+ verifyNotLockDevice(DEFAULT_COUNT_FAILED_AUTH_ATTEMPTS /* expectedCntFailedAttempts */,
+ PRIMARY_USER_ID);
+ }
+
+ @Test
+ public void testReportAuthAttempt_primaryAuthAndBiometricAuthFailed_primaryUser()
+ throws RemoteException {
+ // Three failed primary auth attempts
+ for (int i = 0; i < 3; i++) {
+ mLockSettingsStateListenerCaptor.getValue().onAuthenticationFailed(PRIMARY_USER_ID);
+ }
+ // Two failed biometric auth attempts
+ for (int i = 0; i < 2; i++) {
+ mAuthenticationStateListenerCaptor.getValue()
+ .onAuthenticationFailed(REASON_UNKNOWN, PRIMARY_USER_ID);
+ }
+ waitForAuthCompletion();
+
+ verifyLockDevice(PRIMARY_USER_ID);
+ }
+
+ @Test
+ public void testReportAuthAttempt_primaryAuthAndBiometricAuthFailed_profileOfPrimaryUser()
+ throws RemoteException {
+ // Three failed primary auth attempts
+ for (int i = 0; i < 3; i++) {
+ mLockSettingsStateListenerCaptor.getValue()
+ .onAuthenticationFailed(MANAGED_PROFILE_USER_ID);
+ }
+ // Two failed biometric auth attempts
+ for (int i = 0; i < 2; i++) {
+ mAuthenticationStateListenerCaptor.getValue()
+ .onAuthenticationFailed(REASON_UNKNOWN, MANAGED_PROFILE_USER_ID);
+ }
+ waitForAuthCompletion();
+
+ verifyLockDevice(MANAGED_PROFILE_USER_ID);
+ }
+
+ private void verifyNotLockDevice(int expectedCntFailedAttempts, int userId) {
+ assertEquals(expectedCntFailedAttempts,
+ mAdaptiveAuthService.mFailedAttemptsForUser.get(userId));
+ verify(mWindowManager, never()).lockNow();
+ }
+
+ private void verifyLockDevice(int userId) {
+ assertEquals(MAX_ALLOWED_FAILED_AUTH_ATTEMPTS,
+ mAdaptiveAuthService.mFailedAttemptsForUser.get(userId));
+ verify(mLockPatternUtils).requireStrongAuth(
+ eq(SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST), eq(userId));
+ // If userId is MANAGED_PROFILE_USER_ID, the StrongAuthFlag of its parent (PRIMARY_USER_ID)
+ // should also be verified
+ if (userId == MANAGED_PROFILE_USER_ID) {
+ verify(mLockPatternUtils).requireStrongAuth(
+ eq(SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST), eq(PRIMARY_USER_ID));
+ }
+ verify(mWindowManager).lockNow();
+ }
+
+ /**
+ * Wait for all auth events to complete before verification
+ */
+ private static void waitForAuthCompletion() {
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/adaptiveauth/OWNERS b/services/tests/servicestests/src/com/android/server/adaptiveauth/OWNERS
new file mode 100644
index 000000000000..0218a7835586
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/adaptiveauth/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/adaptiveauth/OWNERS \ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 4307ec5aa7e1..cea10ea9ade4 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -87,6 +87,7 @@ import android.os.HandlerThread;
import android.os.IRemoteCallback;
import android.os.Looper;
import android.os.Message;
+import android.os.PowerManagerInternal;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -1223,6 +1224,7 @@ public class UserControllerTest {
private final UserManagerInternal mUserManagerInternalMock;
private final WindowManagerService mWindowManagerMock;
private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
+ private final PowerManagerInternal mPowerManagerInternal;
private final KeyguardManager mKeyguardManagerMock;
private final LockPatternUtils mLockPatternUtilsMock;
@@ -1244,6 +1246,7 @@ public class UserControllerTest {
mWindowManagerMock = mock(WindowManagerService.class);
mActivityTaskManagerInternal = mock(ActivityTaskManagerInternal.class);
mStorageManagerMock = mock(IStorageManager.class);
+ mPowerManagerInternal = mock(PowerManagerInternal.class);
mKeyguardManagerMock = mock(KeyguardManager.class);
when(mKeyguardManagerMock.isDeviceSecure(anyInt())).thenReturn(true);
mLockPatternUtilsMock = mock(LockPatternUtils.class);
@@ -1309,6 +1312,11 @@ public class UserControllerTest {
}
@Override
+ PowerManagerInternal getPowerManagerInternal() {
+ return mPowerManagerInternal;
+ }
+
+ @Override
KeyguardManager getKeyguardManager() {
return mKeyguardManagerMock;
}
diff --git a/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java b/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java
index fc5819de861f..e756082bc912 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java
@@ -69,6 +69,7 @@ public class AbsoluteVolumeBehaviorTest {
private AudioSystemAdapter mSpyAudioSystem;
private SystemServerAdapter mSystemServer;
private SettingsAdapter mSettingsAdapter;
+ private AudioVolumeGroupHelperBase mAudioVolumeGroupHelper;
private TestLooper mTestLooper;
private AudioService mAudioService;
@@ -93,9 +94,11 @@ public class AbsoluteVolumeBehaviorTest {
mSpyAudioSystem = spy(new NoOpAudioSystemAdapter());
mSystemServer = new NoOpSystemServerAdapter();
mSettingsAdapter = new NoOpSettingsAdapter();
+ mAudioVolumeGroupHelper = new AudioVolumeGroupHelperBase();
mAudioService = new AudioService(mContext, mSpyAudioSystem, mSystemServer,
- mSettingsAdapter, mMockAudioPolicy, mTestLooper.getLooper()) {
+ mSettingsAdapter, mAudioVolumeGroupHelper, mMockAudioPolicy,
+ mTestLooper.getLooper()) {
@Override
public int getDeviceForStream(int stream) {
return AudioSystem.DEVICE_OUT_SPEAKER;
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java
index d4d312894053..3623012b348f 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java
@@ -56,6 +56,7 @@ public class AudioDeviceVolumeManagerTest {
private AudioSystemAdapter mSpyAudioSystem;
private SystemServerAdapter mSystemServer;
private SettingsAdapter mSettingsAdapter;
+ private AudioVolumeGroupHelperBase mAudioVolumeGroupHelper;
private TestLooper mTestLooper;
private AudioPolicyFacade mAudioPolicyMock = mock(AudioPolicyFacade.class);
@@ -71,8 +72,10 @@ public class AudioDeviceVolumeManagerTest {
mSystemServer = new NoOpSystemServerAdapter();
mSettingsAdapter = new NoOpSettingsAdapter();
+ mAudioVolumeGroupHelper = new AudioVolumeGroupHelperBase();
mAudioService = new AudioService(mContext, mSpyAudioSystem, mSystemServer,
- mSettingsAdapter, mAudioPolicyMock, mTestLooper.getLooper()) {
+ mSettingsAdapter, mAudioVolumeGroupHelper, mAudioPolicyMock,
+ mTestLooper.getLooper()) {
@Override
public int getDeviceForStream(int stream) {
return AudioSystem.DEVICE_OUT_SPEAKER;
@@ -82,8 +85,9 @@ public class AudioDeviceVolumeManagerTest {
mTestLooper.dispatchAll();
}
+ // ------------ AudioDeviceVolumeManager related tests ------------
@Test
- public void testSetDeviceVolume() {
+ public void setDeviceVolume_checkIndex() {
AudioManager am = mContext.getSystemService(AudioManager.class);
final int minIndex = am.getStreamMinVolume(AudioManager.STREAM_MUSIC);
final int maxIndex = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
@@ -110,7 +114,7 @@ public class AudioDeviceVolumeManagerTest {
@Test
@RequiresFlagsDisabled(FLAG_DISABLE_PRESCALE_ABSOLUTE_VOLUME)
- public void testConfigurablePreScaleAbsoluteVolume() throws Exception {
+ public void configurablePreScaleAbsoluteVolume_checkIndex() throws Exception {
AudioManager am = mContext.getSystemService(AudioManager.class);
final int minIndex = am.getStreamMinVolume(AudioManager.STREAM_MUSIC);
final int maxIndex = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
@@ -159,7 +163,7 @@ public class AudioDeviceVolumeManagerTest {
@Test
@RequiresFlagsEnabled(FLAG_DISABLE_PRESCALE_ABSOLUTE_VOLUME)
- public void testDisablePreScaleAbsoluteVolume() throws Exception {
+ public void disablePreScaleAbsoluteVolume_checkIndex() throws Exception {
AudioManager am = mContext.getSystemService(AudioManager.class);
final int minIndex = am.getStreamMinVolume(AudioManager.STREAM_MUSIC);
final int maxIndex = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
index e565faa1c00b..634877eb2539 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
@@ -60,6 +60,7 @@ public class AudioServiceTest {
private Context mContext;
private AudioSystemAdapter mSpyAudioSystem;
private SettingsAdapter mSettingsAdapter;
+ private AudioVolumeGroupHelperBase mAudioVolumeGroupHelper;
@Spy private NoOpSystemServerAdapter mSpySystemServer;
@Mock private AppOpsManager mMockAppOpsManager;
@@ -80,11 +81,12 @@ public class AudioServiceTest {
mContext = InstrumentationRegistry.getTargetContext();
mSpyAudioSystem = spy(new NoOpAudioSystemAdapter());
mSettingsAdapter = new NoOpSettingsAdapter();
+ mAudioVolumeGroupHelper = new AudioVolumeGroupHelperBase();
when(mMockAppOpsManager.noteOp(anyInt(), anyInt(), anyString(), anyString(), anyString()))
.thenReturn(AppOpsManager.MODE_ALLOWED);
mAudioService = new AudioService(mContext, mSpyAudioSystem, mSpySystemServer,
- mSettingsAdapter, mMockAudioPolicy, null, mMockAppOpsManager,
- mMockPermissionEnforcer);
+ mSettingsAdapter, mAudioVolumeGroupHelper, mMockAudioPolicy, null,
+ mMockAppOpsManager, mMockPermissionEnforcer);
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java b/services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java
index f5862acb2811..8dfcc1843fed 100644
--- a/services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java
@@ -50,6 +50,7 @@ public class DeviceVolumeBehaviorTest {
private AudioSystemAdapter mAudioSystem;
private SystemServerAdapter mSystemServer;
private SettingsAdapter mSettingsAdapter;
+ private AudioVolumeGroupHelperBase mAudioVolumeGroupHelper;
private TestLooper mTestLooper;
private AudioPolicyFacade mAudioPolicyMock = mock(AudioPolicyFacade.class);
@@ -71,8 +72,10 @@ public class DeviceVolumeBehaviorTest {
mAudioSystem = new NoOpAudioSystemAdapter();
mSystemServer = new NoOpSystemServerAdapter();
mSettingsAdapter = new NoOpSettingsAdapter();
+ mAudioVolumeGroupHelper = new AudioVolumeGroupHelperBase();
mAudioService = new AudioService(mContext, mAudioSystem, mSystemServer,
- mSettingsAdapter, mAudioPolicyMock, mTestLooper.getLooper());
+ mSettingsAdapter, mAudioVolumeGroupHelper, mAudioPolicyMock,
+ mTestLooper.getLooper());
mTestLooper.dispatchAll();
}
diff --git a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
index 0eac718c2f14..96ac5d251ffd 100644
--- a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
+++ b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
@@ -137,6 +137,11 @@ public class NoOpAudioSystemAdapter extends AudioSystemAdapter {
}
@Override
+ public int setVolumeIndexForAttributes(AudioAttributes attributes, int index, int device) {
+ return AudioSystem.AUDIO_STATUS_OK;
+ }
+
+ @Override
@NonNull
public ArrayList<AudioDeviceAttributes> getDevicesForAttributes(
@NonNull AudioAttributes attributes, boolean forVolume) {
diff --git a/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java b/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java
new file mode 100644
index 000000000000..83bbd0e3f014
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java
@@ -0,0 +1,726 @@
+/*
+ * Copyright 2024 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.audio;
+
+import static android.media.AudioManager.ADJUST_LOWER;
+import static android.media.AudioManager.ADJUST_MUTE;
+import static android.media.AudioManager.ADJUST_RAISE;
+import static android.media.AudioManager.DEVICE_OUT_BLE_SPEAKER;
+import static android.media.AudioManager.DEVICE_OUT_BLUETOOTH_SCO;
+import static android.media.AudioManager.DEVICE_OUT_SPEAKER;
+import static android.media.AudioManager.DEVICE_OUT_USB_DEVICE;
+import static android.media.AudioManager.DEVICE_VOLUME_BEHAVIOR_UNSET;
+import static android.media.AudioManager.FLAG_ALLOW_RINGER_MODES;
+import static android.media.AudioManager.FLAG_BLUETOOTH_ABS_VOLUME;
+import static android.media.AudioManager.RINGER_MODE_NORMAL;
+import static android.media.AudioManager.RINGER_MODE_VIBRATE;
+import static android.media.AudioManager.STREAM_ACCESSIBILITY;
+import static android.media.AudioManager.STREAM_ALARM;
+import static android.media.AudioManager.STREAM_BLUETOOTH_SCO;
+import static android.media.AudioManager.STREAM_MUSIC;
+import static android.media.AudioManager.STREAM_NOTIFICATION;
+import static android.media.AudioManager.STREAM_RING;
+import static android.media.AudioManager.STREAM_SYSTEM;
+import static android.media.AudioManager.STREAM_VOICE_CALL;
+import static android.view.KeyEvent.ACTION_DOWN;
+import static android.view.KeyEvent.KEYCODE_VOLUME_UP;
+
+import static com.android.media.audio.Flags.FLAG_DISABLE_PRESCALE_ABSOLUTE_VOLUME;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.media.AudioDeviceAttributes;
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
+import android.media.AudioSystem;
+import android.media.IDeviceVolumeBehaviorDispatcher;
+import android.media.VolumeInfo;
+import android.media.audiopolicy.AudioVolumeGroup;
+import android.os.Looper;
+import android.os.PermissionEnforcer;
+import android.os.test.TestLooper;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.util.SparseIntArray;
+import android.view.KeyEvent;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@Presubmit
+public class VolumeHelperTest {
+ private static final AudioDeviceAttributes DEVICE_SPEAKER_OUT = new AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT, AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, "");
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ private MyAudioService mAudioService;
+
+ private AudioManager mAm;
+
+ private Context mContext;
+
+ private AudioSystemAdapter mSpyAudioSystem;
+ private SettingsAdapter mSettingsAdapter;
+ @Spy
+ private NoOpSystemServerAdapter mSpySystemServer;
+ @Mock
+ private AppOpsManager mMockAppOpsManager;
+ @Mock
+ private PermissionEnforcer mMockPermissionEnforcer;
+ @Mock
+ private AudioVolumeGroupHelperBase mAudioVolumeGroupHelper;
+
+ private final AudioPolicyFacade mFakeAudioPolicy = lookbackAudio -> false;
+
+ private AudioVolumeGroup mAudioMusicVolumeGroup;
+
+ private TestLooper mTestLooper;
+
+ public static final int[] BASIC_VOLUME_BEHAVIORS = {
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED
+ };
+
+ private static class MyAudioService extends AudioService {
+ private final SparseIntArray mStreamDevice = new SparseIntArray();
+
+ MyAudioService(Context context, AudioSystemAdapter audioSystem,
+ SystemServerAdapter systemServer, SettingsAdapter settings,
+ AudioVolumeGroupHelperBase audioVolumeGroupHelper, AudioPolicyFacade audioPolicy,
+ @Nullable Looper looper, AppOpsManager appOps,
+ @NonNull PermissionEnforcer enforcer) {
+ super(context, audioSystem, systemServer, settings, audioVolumeGroupHelper,
+ audioPolicy, looper, appOps, enforcer);
+ }
+
+ public void setDeviceForStream(int stream, int device) {
+ mStreamDevice.put(stream, device);
+ }
+
+ @Override
+ public int getDeviceForStream(int stream) {
+ if (mStreamDevice.indexOfKey(stream) < 0) {
+ return DEVICE_OUT_SPEAKER;
+ }
+ return mStreamDevice.get(stream);
+ }
+ }
+
+ private static class TestDeviceVolumeBehaviorDispatcherStub
+ extends IDeviceVolumeBehaviorDispatcher.Stub {
+
+ private AudioDeviceAttributes mDevice;
+ private int mVolumeBehavior;
+ private int mTimesCalled;
+
+ @Override
+ public void dispatchDeviceVolumeBehaviorChanged(@NonNull AudioDeviceAttributes device,
+ @AudioManager.DeviceVolumeBehavior int volumeBehavior) {
+ mDevice = device;
+ mVolumeBehavior = volumeBehavior;
+ mTimesCalled++;
+ }
+
+ public void reset() {
+ mTimesCalled = 0;
+ mVolumeBehavior = DEVICE_VOLUME_BEHAVIOR_UNSET;
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ mTestLooper = new TestLooper();
+
+ mSpyAudioSystem = spy(new NoOpAudioSystemAdapter());
+ mSettingsAdapter = new NoOpSettingsAdapter();
+
+ mAudioMusicVolumeGroup = getStreamTypeVolumeGroup(STREAM_MUSIC);
+ if (mAudioMusicVolumeGroup != null) {
+ when(mAudioVolumeGroupHelper.getAudioVolumeGroups()).thenReturn(
+ List.of(mAudioMusicVolumeGroup));
+ }
+
+ mAm = mContext.getSystemService(AudioManager.class);
+
+ mAudioService = new MyAudioService(mContext, mSpyAudioSystem, mSpySystemServer,
+ mSettingsAdapter, mAudioVolumeGroupHelper, mFakeAudioPolicy,
+ mTestLooper.getLooper(), mMockAppOpsManager, mMockPermissionEnforcer);
+
+ mTestLooper.dispatchAll();
+ prepareAudioServiceState();
+ mTestLooper.dispatchAll();
+
+ reset(mSpyAudioSystem);
+
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .adoptShellPermissionIdentity(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED,
+ Manifest.permission.MODIFY_AUDIO_ROUTING,
+ Manifest.permission.MODIFY_PHONE_STATE,
+ android.Manifest.permission.STATUS_BAR_SERVICE);
+ }
+
+ private void prepareAudioServiceState() throws Exception {
+ int[] usedStreamTypes =
+ {STREAM_MUSIC, STREAM_NOTIFICATION, STREAM_RING, STREAM_ALARM, STREAM_SYSTEM,
+ STREAM_VOICE_CALL, STREAM_ACCESSIBILITY};
+ for (int streamType : usedStreamTypes) {
+ final int streamVolume = (mAm.getStreamMinVolume(streamType) + mAm.getStreamMaxVolume(
+ streamType)) / 2;
+
+ mAudioService.setStreamVolume(streamType, streamVolume, /*flags=*/0,
+ mContext.getOpPackageName());
+ }
+
+ mAudioService.setRingerModeInternal(RINGER_MODE_NORMAL, mContext.getOpPackageName());
+ mAudioService.setRingerModeExternal(RINGER_MODE_NORMAL, mContext.getOpPackageName());
+ }
+
+ private AudioVolumeGroup getStreamTypeVolumeGroup(int streamType) {
+ // get the volume group from the AudioManager to pass permission checks
+ // when requesting from real service
+ final List<AudioVolumeGroup> audioVolumeGroups = AudioManager.getAudioVolumeGroups();
+ for (AudioVolumeGroup vg : audioVolumeGroups) {
+ for (int stream : vg.getLegacyStreamTypes()) {
+ if (stream == streamType) {
+ return vg;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ @After
+ public void tearDown() {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .dropShellPermissionIdentity();
+ }
+
+ // --------------- Volume Stream APIs ---------------
+ @Test
+ public void setStreamVolume_callsASSetStreamVolumeIndex() throws Exception {
+ int newIndex = circularNoMinMaxIncrementVolume(STREAM_MUSIC);
+
+ mAudioService.setDeviceForStream(STREAM_MUSIC, DEVICE_OUT_USB_DEVICE);
+ mAudioService.setStreamVolume(STREAM_MUSIC, newIndex, /*flags=*/0,
+ mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ verify(mSpyAudioSystem).setStreamVolumeIndexAS(
+ eq(STREAM_MUSIC), eq(newIndex), eq(DEVICE_OUT_USB_DEVICE));
+ }
+
+ @Test
+ public void setStreamRingVolume0_setsRingerModeVibrate() throws Exception {
+ mAudioService.setStreamVolume(STREAM_RING, 0, /*flags=*/0,
+ mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ assertEquals(RINGER_MODE_VIBRATE, mAudioService.getRingerModeExternal());
+ }
+
+ @Test
+ public void adjustStreamVolume_callsASSetStreamVolumeIndex() throws Exception {
+ mAudioService.setDeviceForStream(STREAM_MUSIC, DEVICE_OUT_USB_DEVICE);
+ mAudioService.adjustStreamVolume(STREAM_MUSIC, ADJUST_LOWER, /*flags=*/0,
+ mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
+ eq(STREAM_MUSIC), anyInt(), eq(DEVICE_OUT_USB_DEVICE));
+ }
+
+ @Test
+ public void handleVolumeKey_callsASSetStreamVolumeIndex() throws Exception {
+ final KeyEvent keyEvent = new KeyEvent(ACTION_DOWN, KEYCODE_VOLUME_UP);
+
+ mAudioService.setDeviceForStream(STREAM_MUSIC, DEVICE_OUT_USB_DEVICE);
+ mAudioService.handleVolumeKey(keyEvent, /*isOnTv=*/false, mContext.getOpPackageName(),
+ "adjustSuggestedStreamVolume_callsAudioSystemSetStreamVolumeIndex");
+ mTestLooper.dispatchAll();
+
+ verify(mSpyAudioSystem).setStreamVolumeIndexAS(
+ eq(STREAM_MUSIC), anyInt(), eq(DEVICE_OUT_USB_DEVICE));
+ }
+
+ // --------------- Volume Group APIs ---------------
+
+ @Test
+ public void setVolumeGroupVolumeIndex_callsASSetVolumeIndexForAttributes() throws Exception {
+ assumeNotNull(mAudioMusicVolumeGroup);
+
+ mAudioService.setDeviceForStream(STREAM_MUSIC, DEVICE_OUT_USB_DEVICE);
+ mAudioService.setVolumeGroupVolumeIndex(mAudioMusicVolumeGroup.getId(),
+ circularNoMinMaxIncrementVolume(STREAM_MUSIC), /*flags=*/0,
+ mContext.getOpPackageName(), /*attributionTag*/null);
+ mTestLooper.dispatchAll();
+
+ verify(mSpyAudioSystem).setVolumeIndexForAttributes(
+ any(), anyInt(), eq(DEVICE_OUT_USB_DEVICE));
+ }
+
+ @Test
+ public void adjustVolumeGroupVolume_callsASSetVolumeIndexForAttributes() throws Exception {
+ assumeNotNull(mAudioMusicVolumeGroup);
+
+ mAudioService.setDeviceForStream(STREAM_MUSIC, DEVICE_OUT_USB_DEVICE);
+ mAudioService.adjustVolumeGroupVolume(mAudioMusicVolumeGroup.getId(),
+ ADJUST_LOWER, /*flags=*/0, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ verify(mSpyAudioSystem).setVolumeIndexForAttributes(
+ any(), anyInt(), eq(DEVICE_OUT_USB_DEVICE));
+ }
+
+ @Test
+ public void check_getVolumeGroupVolumeIndex() throws Exception {
+ assumeNotNull(mAudioMusicVolumeGroup);
+
+ int newIndex = circularNoMinMaxIncrementVolume(STREAM_MUSIC);
+
+ mAudioService.setVolumeGroupVolumeIndex(mAudioMusicVolumeGroup.getId(),
+ newIndex, /*flags=*/0, mContext.getOpPackageName(), /*attributionTag*/null);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mAudioService.getVolumeGroupVolumeIndex(mAudioMusicVolumeGroup.getId()),
+ newIndex);
+ assertEquals(mAudioService.getStreamVolume(STREAM_MUSIC),
+ newIndex);
+ }
+
+ @Test
+ public void check_getVolumeGroupMaxVolumeIndex() throws Exception {
+ assumeNotNull(mAudioMusicVolumeGroup);
+
+ assertEquals(mAudioService.getVolumeGroupMaxVolumeIndex(mAudioMusicVolumeGroup.getId()),
+ mAudioService.getStreamMaxVolume(STREAM_MUSIC));
+ }
+
+ @Test
+ public void check_getVolumeGroupMinVolumeIndex() throws Exception {
+ assumeNotNull(mAudioMusicVolumeGroup);
+
+ assertEquals(mAudioService.getVolumeGroupMinVolumeIndex(mAudioMusicVolumeGroup.getId()),
+ mAudioService.getStreamMinVolume(STREAM_MUSIC));
+ }
+
+ @Test
+ public void check_getLastAudibleVolumeForVolumeGroup() throws Exception {
+ assumeNotNull(mAudioMusicVolumeGroup);
+
+ assertEquals(
+ mAudioService.getLastAudibleVolumeForVolumeGroup(mAudioMusicVolumeGroup.getId()),
+ mAudioService.getLastAudibleStreamVolume(STREAM_MUSIC));
+ }
+
+ @Test
+ public void check_isVolumeGroupMuted() throws Exception {
+ assumeNotNull(mAudioMusicVolumeGroup);
+
+ assertEquals(mAudioService.isVolumeGroupMuted(mAudioMusicVolumeGroup.getId()),
+ mAudioService.isStreamMute(STREAM_MUSIC));
+ }
+
+ // ------------------------- Mute Tests ------------------------
+
+ @Test
+ public void check_setMasterMute() {
+ mAudioService.setMasterMute(true, /*flags=*/0, mContext.getOpPackageName(),
+ mContext.getUserId(), /*attributionTag*/"");
+
+ assertTrue(mAudioService.isMasterMute());
+ }
+
+ @Test
+ public void check_isStreamAffectedByMute() {
+ assertFalse(mAudioService.isStreamAffectedByMute(STREAM_VOICE_CALL));
+ }
+
+ // --------------------- Volume Flag Check --------------------
+
+ @Test
+ public void flagAbsVolume_onBtDevice_changesVolume() throws Exception {
+ mAudioService.setDeviceForStream(STREAM_NOTIFICATION, DEVICE_OUT_BLE_SPEAKER);
+
+ int newIndex = circularNoMinMaxIncrementVolume(STREAM_NOTIFICATION);
+ mAudioService.setStreamVolume(STREAM_NOTIFICATION, newIndex, FLAG_BLUETOOTH_ABS_VOLUME,
+ mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+ verify(mSpyAudioSystem).setStreamVolumeIndexAS(
+ eq(STREAM_NOTIFICATION), anyInt(), eq(DEVICE_OUT_BLE_SPEAKER));
+
+ reset(mSpyAudioSystem);
+ mAudioService.adjustStreamVolume(STREAM_NOTIFICATION, ADJUST_LOWER,
+ FLAG_BLUETOOTH_ABS_VOLUME, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+ verify(mSpyAudioSystem).setStreamVolumeIndexAS(
+ eq(STREAM_NOTIFICATION), anyInt(), eq(DEVICE_OUT_BLE_SPEAKER));
+ }
+
+ @Test
+ public void flagAbsVolume_onNonBtDevice_noVolumeChange() throws Exception {
+ mAudioService.setDeviceForStream(STREAM_NOTIFICATION, DEVICE_OUT_SPEAKER);
+
+ int newIndex = circularNoMinMaxIncrementVolume(STREAM_NOTIFICATION);
+ mAudioService.setStreamVolume(STREAM_NOTIFICATION, newIndex, FLAG_BLUETOOTH_ABS_VOLUME,
+ mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+ verify(mSpyAudioSystem, times(0)).setStreamVolumeIndexAS(
+ eq(STREAM_NOTIFICATION), eq(newIndex), eq(DEVICE_OUT_BLE_SPEAKER));
+
+ mAudioService.adjustStreamVolume(STREAM_NOTIFICATION, ADJUST_LOWER,
+ FLAG_BLUETOOTH_ABS_VOLUME, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+ verify(mSpyAudioSystem, times(0)).setStreamVolumeIndexAS(
+ eq(STREAM_NOTIFICATION), anyInt(), eq(DEVICE_OUT_BLE_SPEAKER));
+ }
+
+ @Test
+ public void flagAllowRingerModes_onSystemStreams_changesMode() throws Exception {
+ mAudioService.setStreamVolume(STREAM_SYSTEM,
+ mAudioService.getStreamMinVolume(STREAM_SYSTEM), /*flags=*/0,
+ mContext.getOpPackageName());
+ mAudioService.setRingerModeInternal(RINGER_MODE_VIBRATE, mContext.getOpPackageName());
+
+ mAudioService.adjustStreamVolume(STREAM_SYSTEM, ADJUST_RAISE, FLAG_ALLOW_RINGER_MODES,
+ mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ assertNotEquals(mAudioService.getRingerModeInternal(), RINGER_MODE_VIBRATE);
+ }
+
+ @Test
+ public void flagAllowRingerModesAbsent_onNonSystemStreams_noModeChange() throws Exception {
+ mAudioService.setStreamVolume(STREAM_MUSIC,
+ mAudioService.getStreamMinVolume(STREAM_MUSIC), /*flags=*/0,
+ mContext.getOpPackageName());
+ mAudioService.setRingerModeInternal(RINGER_MODE_VIBRATE, mContext.getOpPackageName());
+
+ mAudioService.adjustStreamVolume(STREAM_MUSIC, ADJUST_RAISE, /*flags=*/0,
+ mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ assertEquals(mAudioService.getRingerModeInternal(), RINGER_MODE_VIBRATE);
+ }
+
+ // --------------------- Permission tests ---------------------
+
+ @Test
+ public void appOpsIgnore_noVolumeChange() throws Exception {
+ reset(mMockAppOpsManager);
+ when(mMockAppOpsManager.noteOp(anyInt(), anyInt(), anyString(), isNull(), isNull()))
+ .thenReturn(AppOpsManager.MODE_IGNORED);
+
+ mAudioService.adjustStreamVolume(STREAM_MUSIC, ADJUST_LOWER, /*flags=*/0,
+ mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ verify(mSpyAudioSystem, times(0)).setStreamVolumeIndexAS(
+ eq(STREAM_MUSIC), anyInt(), anyInt());
+ }
+
+ @Test
+ public void modifyPhoneStateAbsent_noMuteVoiceCallScoAllowed() throws Exception {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .dropShellPermissionIdentity();
+
+ mAudioService.setDeviceForStream(STREAM_VOICE_CALL, DEVICE_OUT_USB_DEVICE);
+ mAudioService.adjustStreamVolume(STREAM_VOICE_CALL, ADJUST_MUTE, /*flags=*/0,
+ mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ verify(mSpyAudioSystem, times(0)).setStreamVolumeIndexAS(
+ eq(STREAM_VOICE_CALL), anyInt(), eq(DEVICE_OUT_USB_DEVICE));
+
+ mAudioService.setDeviceForStream(STREAM_BLUETOOTH_SCO, DEVICE_OUT_BLUETOOTH_SCO);
+ mAudioService.adjustStreamVolume(STREAM_BLUETOOTH_SCO, ADJUST_MUTE, /*flags=*/0,
+ mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ verify(mSpyAudioSystem, times(0)).setStreamVolumeIndexAS(
+ eq(STREAM_BLUETOOTH_SCO), anyInt(), eq(DEVICE_OUT_USB_DEVICE));
+ }
+
+ // ----------------- AudioDeviceVolumeManager -----------------
+ @Test
+ public void setDeviceVolume_checkIndex() {
+ final int minIndex = mAm.getStreamMinVolume(STREAM_MUSIC);
+ final int maxIndex = mAm.getStreamMaxVolume(STREAM_MUSIC);
+ final int midIndex = (minIndex + maxIndex) / 2;
+ final VolumeInfo volMedia = new VolumeInfo.Builder(STREAM_MUSIC)
+ .setMinVolumeIndex(minIndex)
+ .setMaxVolumeIndex(maxIndex)
+ .build();
+ final VolumeInfo volMin = new VolumeInfo.Builder(volMedia).setVolumeIndex(minIndex).build();
+ final VolumeInfo volMid = new VolumeInfo.Builder(volMedia).setVolumeIndex(midIndex).build();
+ final AudioDeviceAttributes usbDevice = new AudioDeviceAttributes(
+ /*native type*/ AudioSystem.DEVICE_OUT_USB_DEVICE, /*address*/ "bla");
+
+ mAudioService.setDeviceVolume(volMin, usbDevice, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ assertEquals(mAudioService.getDeviceVolume(volMin, usbDevice,
+ mContext.getOpPackageName()), volMin);
+ verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
+ STREAM_MUSIC, minIndex, AudioSystem.DEVICE_OUT_USB_DEVICE);
+
+ mAudioService.setDeviceVolume(volMid, usbDevice, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+ assertEquals(mAudioService.getDeviceVolume(volMid, usbDevice,
+ mContext.getOpPackageName()), volMid);
+ verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
+ STREAM_MUSIC, midIndex, AudioSystem.DEVICE_OUT_USB_DEVICE);
+ }
+
+ @Test
+ @RequiresFlagsDisabled(FLAG_DISABLE_PRESCALE_ABSOLUTE_VOLUME)
+ public void configurablePreScaleAbsoluteVolume_checkIndex() throws Exception {
+ final int minIndex = mAm.getStreamMinVolume(STREAM_MUSIC);
+ final int maxIndex = mAm.getStreamMaxVolume(STREAM_MUSIC);
+ final VolumeInfo volMedia = new VolumeInfo.Builder(STREAM_MUSIC)
+ .setMinVolumeIndex(minIndex)
+ .setMaxVolumeIndex(maxIndex)
+ .build();
+ final AudioDeviceAttributes bleDevice = new AudioDeviceAttributes(
+ /*native type*/ AudioSystem.DEVICE_OUT_BLE_HEADSET, /*address*/ "fake_ble");
+ final int maxPreScaleIndex = 3;
+ final float[] preScale = new float[maxPreScaleIndex];
+ preScale[0] = mContext.getResources().getFraction(
+ com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index1,
+ 1, 1);
+ preScale[1] = mContext.getResources().getFraction(
+ com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index2,
+ 1, 1);
+ preScale[2] = mContext.getResources().getFraction(
+ com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index3,
+ 1, 1);
+
+ for (int i = 0; i < maxPreScaleIndex; i++) {
+ final int targetIndex = (int) (preScale[i] * maxIndex);
+ final VolumeInfo volCur = new VolumeInfo.Builder(volMedia)
+ .setVolumeIndex(i + 1).build();
+ // Adjust stream volume with FLAG_ABSOLUTE_VOLUME set (index:1~3)
+ mAudioService.setDeviceVolume(volCur, bleDevice, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ assertEquals(
+ mAudioService.getDeviceVolume(volCur, bleDevice, mContext.getOpPackageName()),
+ volCur);
+ // Stream volume changes
+ verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
+ STREAM_MUSIC, targetIndex,
+ AudioSystem.DEVICE_OUT_BLE_HEADSET);
+ }
+
+ // Adjust stream volume with FLAG_ABSOLUTE_VOLUME set (index:4)
+ final VolumeInfo volIndex4 = new VolumeInfo.Builder(volMedia)
+ .setVolumeIndex(4).build();
+ mAudioService.setDeviceVolume(volIndex4, bleDevice, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ assertEquals(
+ mAudioService.getDeviceVolume(volIndex4, bleDevice, mContext.getOpPackageName()),
+ volIndex4);
+ verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
+ STREAM_MUSIC, maxIndex,
+ AudioSystem.DEVICE_OUT_BLE_HEADSET);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_DISABLE_PRESCALE_ABSOLUTE_VOLUME)
+ public void disablePreScaleAbsoluteVolume_checkIndex() throws Exception {
+ final int minIndex = mAm.getStreamMinVolume(STREAM_MUSIC);
+ final int maxIndex = mAm.getStreamMaxVolume(STREAM_MUSIC);
+ final VolumeInfo volMedia = new VolumeInfo.Builder(STREAM_MUSIC)
+ .setMinVolumeIndex(minIndex)
+ .setMaxVolumeIndex(maxIndex)
+ .build();
+ final AudioDeviceAttributes bleDevice = new AudioDeviceAttributes(
+ /*native type*/ AudioSystem.DEVICE_OUT_BLE_HEADSET, /*address*/ "bla");
+ final int maxPreScaleIndex = 3;
+
+ for (int i = 0; i < maxPreScaleIndex; i++) {
+ final VolumeInfo volCur = new VolumeInfo.Builder(volMedia)
+ .setVolumeIndex(i + 1).build();
+ // Adjust stream volume with FLAG_ABSOLUTE_VOLUME set (index:1~3)
+ mAudioService.setDeviceVolume(volCur, bleDevice, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ // Stream volume changes
+ verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
+ STREAM_MUSIC, maxIndex,
+ AudioSystem.DEVICE_OUT_BLE_HEADSET);
+ }
+
+ // Adjust stream volume with FLAG_ABSOLUTE_VOLUME set (index:4)
+ final VolumeInfo volIndex4 = new VolumeInfo.Builder(volMedia)
+ .setVolumeIndex(4).build();
+ mAudioService.setDeviceVolume(volIndex4, bleDevice, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
+ STREAM_MUSIC, maxIndex,
+ AudioSystem.DEVICE_OUT_BLE_HEADSET);
+ }
+
+ // ---------------- DeviceVolumeBehaviorTest ----------------
+ @Test
+ public void setDeviceVolumeBehavior_changesDeviceVolumeBehavior() {
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ for (int behavior : BASIC_VOLUME_BEHAVIORS) {
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT, behavior,
+ mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ int actualBehavior = mAudioService.getDeviceVolumeBehavior(DEVICE_SPEAKER_OUT);
+
+ assertWithMessage("Expected volume behavior to be " + behavior
+ + " but was instead " + actualBehavior)
+ .that(actualBehavior).isEqualTo(behavior);
+ }
+ }
+
+ @Test
+ public void setToNewBehavior_triggersDeviceVolumeBehaviorDispatcher() {
+ TestDeviceVolumeBehaviorDispatcherStub dispatcher =
+ new TestDeviceVolumeBehaviorDispatcherStub();
+ mAudioService.registerDeviceVolumeBehaviorDispatcher(true, dispatcher);
+
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ for (int behavior : BASIC_VOLUME_BEHAVIORS) {
+ dispatcher.reset();
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT, behavior,
+ mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ assertThat(dispatcher.mTimesCalled).isEqualTo(1);
+ assertThat(dispatcher.mDevice).isEqualTo(DEVICE_SPEAKER_OUT);
+ assertWithMessage("Expected dispatched volume behavior to be " + behavior
+ + " but was instead " + dispatcher.mVolumeBehavior)
+ .that(dispatcher.mVolumeBehavior).isEqualTo(behavior);
+ }
+ }
+
+ @Test
+ public void setToSameBehavior_doesNotTriggerDeviceVolumeBehaviorDispatcher() {
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ TestDeviceVolumeBehaviorDispatcherStub dispatcher =
+ new TestDeviceVolumeBehaviorDispatcherStub();
+ mAudioService.registerDeviceVolumeBehaviorDispatcher(true, dispatcher);
+
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+ assertThat(dispatcher.mTimesCalled).isEqualTo(0);
+ }
+
+ @Test
+ public void unregisterDeviceVolumeBehaviorDispatcher_noLongerTriggered() {
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ TestDeviceVolumeBehaviorDispatcherStub dispatcher =
+ new TestDeviceVolumeBehaviorDispatcherStub();
+ mAudioService.registerDeviceVolumeBehaviorDispatcher(true, dispatcher);
+ mAudioService.registerDeviceVolumeBehaviorDispatcher(false, dispatcher);
+
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+ assertThat(dispatcher.mTimesCalled).isEqualTo(0);
+ }
+
+ @Test
+ public void setDeviceVolumeBehavior_checkIsVolumeFixed() throws Exception {
+ when(mSpyAudioSystem.getDevicesForAttributes(any(), anyBoolean())).thenReturn(
+ new ArrayList<>(List.of(DEVICE_SPEAKER_OUT)));
+
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED, mContext.getOpPackageName());
+
+ assertTrue(mAudioService.isVolumeFixed());
+ }
+
+ private int circularNoMinMaxIncrementVolume(int streamType) throws Exception {
+ final int streamMinVolume = mAm.getStreamMinVolume(streamType) + 1;
+ final int streamMaxVolume = mAm.getStreamMaxVolume(streamType) - 1;
+
+ int streamVolume = mAudioService.getStreamVolume(streamType);
+ if (streamVolume + 1 > streamMaxVolume) {
+ return streamMinVolume;
+ }
+ return streamVolume + 1;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
index 071db68704af..f0dc5f0606f3 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -60,8 +60,10 @@ import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintService;
import android.hardware.iris.IIrisService;
import android.os.Binder;
+import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
@@ -127,6 +129,8 @@ public class AuthServiceTest {
AppOpsManager mAppOpsManager;
@Mock
private VirtualDeviceManagerInternal mVdmInternal;
+ @Mock
+ private BiometricHandlerProvider mBiometricHandlerProvider;
@Captor
private ArgumentCaptor<List<FingerprintSensorPropertiesInternal>> mFingerprintPropsCaptor;
@Captor
@@ -136,6 +140,9 @@ public class AuthServiceTest {
@Captor
private ArgumentCaptor<List<FaceSensorPropertiesInternal>> mFacePropsCaptor;
+ private final TestLooper mFingerprintLooper = new TestLooper();
+ private final TestLooper mFaceLooper = new TestLooper();
+
@Before
public void setUp() {
// Placeholder test config
@@ -167,6 +174,11 @@ public class AuthServiceTest {
when(mInjector.getIrisService()).thenReturn(mIrisService);
when(mInjector.getAppOps(any())).thenReturn(mAppOpsManager);
when(mInjector.isHidlDisabled(any())).thenReturn(false);
+ when(mInjector.getBiometricHandlerProvider()).thenReturn(mBiometricHandlerProvider);
+ when(mBiometricHandlerProvider.getFingerprintHandler()).thenReturn(
+ new Handler(mFingerprintLooper.getLooper()));
+ when(mBiometricHandlerProvider.getFaceHandler()).thenReturn(
+ new Handler(mFaceLooper.getLooper()));
setInternalAndTestBiometricPermissions(mContext, false /* hasPermission */);
}
@@ -250,6 +262,9 @@ public class AuthServiceTest {
mAuthService = new AuthService(mContext, mInjector);
mAuthService.onStart();
+ mFingerprintLooper.dispatchAll();
+ mFaceLooper.dispatchAll();
+
verify(mFingerprintService).registerAuthenticatorsLegacy(
mFingerprintSensorConfigurationsCaptor.capture());
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricNotificationLoggerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricNotificationLoggerTest.java
new file mode 100644
index 000000000000..8319623eec0a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricNotificationLoggerTest.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics;
+
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.platform.test.annotations.Presubmit;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+
+import com.android.server.biometrics.log.BiometricFrameworkStatsLogger;
+import com.android.server.biometrics.sensors.BiometricNotificationUtils;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@Presubmit
+@SmallTest
+public class BiometricNotificationLoggerTest {
+ @Rule
+ public MockitoRule mockitorule = MockitoJUnit.rule();
+
+ @Mock
+ private BiometricFrameworkStatsLogger mLogger;
+ private BiometricNotificationLogger mNotificationLogger;
+
+ @Before
+ public void setUp() {
+ mNotificationLogger = new BiometricNotificationLogger(
+ mLogger);
+ }
+
+ @Test
+ public void testNotification_nullNotification_doesNothing() {
+ mNotificationLogger.onNotificationPosted(null, null);
+
+ verify(mLogger, never()).logFrameworkNotification(anyInt(), anyInt());
+ }
+
+ @Test
+ public void testNotification_emptyStringTag_doesNothing() {
+ final StatusBarNotification noti = createNotificationWithNullTag();
+ mNotificationLogger.onNotificationPosted(noti, null);
+
+ verify(mLogger, never()).logFrameworkNotification(anyInt(), anyInt());
+ }
+
+ @Test
+ public void testFaceNotification_posted() {
+ final StatusBarNotification noti = createFaceNotification();
+ mNotificationLogger.onNotificationPosted(noti, null);
+
+ verify(mLogger).logFrameworkNotification(
+ BiometricsProtoEnums.FRR_NOTIFICATION_ACTION_SHOWN,
+ BiometricsProtoEnums.MODALITY_FACE);
+ }
+
+ @Test
+ public void testFingerprintNotification_posted() {
+ final StatusBarNotification noti = createFingerprintNotification();
+ mNotificationLogger.onNotificationPosted(noti, null);
+
+ verify(mLogger).logFrameworkNotification(
+ BiometricsProtoEnums.FRR_NOTIFICATION_ACTION_SHOWN,
+ BiometricsProtoEnums.MODALITY_FINGERPRINT);
+ }
+
+ @Test
+ public void testFaceNotification_clicked() {
+ final StatusBarNotification noti = createFaceNotification();
+ mNotificationLogger.onNotificationRemoved(noti, null,
+ NotificationListenerService.REASON_CLICK);
+
+ verify(mLogger).logFrameworkNotification(
+ BiometricsProtoEnums.FRR_NOTIFICATION_ACTION_CLICKED,
+ BiometricsProtoEnums.MODALITY_FACE);
+ }
+
+ @Test
+ public void testFingerprintNotification_clicked() {
+ final StatusBarNotification noti = createFingerprintNotification();
+ mNotificationLogger.onNotificationRemoved(noti, null,
+ NotificationListenerService.REASON_CLICK);
+
+ verify(mLogger).logFrameworkNotification(
+ BiometricsProtoEnums.FRR_NOTIFICATION_ACTION_CLICKED,
+ BiometricsProtoEnums.MODALITY_FINGERPRINT);
+ }
+
+ @Test
+ public void testFaceNotification_dismissed() {
+ final StatusBarNotification noti = createFaceNotification();
+ mNotificationLogger.onNotificationRemoved(noti, null,
+ NotificationListenerService.REASON_CANCEL);
+
+ verify(mLogger).logFrameworkNotification(
+ BiometricsProtoEnums.FRR_NOTIFICATION_ACTION_DISMISSED,
+ BiometricsProtoEnums.MODALITY_FACE);
+ }
+
+ @Test
+ public void testFingerprintNotification_dismissed() {
+ final StatusBarNotification noti = createFingerprintNotification();
+ mNotificationLogger.onNotificationRemoved(noti, null,
+ NotificationListenerService.REASON_CANCEL);
+
+ verify(mLogger).logFrameworkNotification(
+ BiometricsProtoEnums.FRR_NOTIFICATION_ACTION_DISMISSED,
+ BiometricsProtoEnums.MODALITY_FINGERPRINT);
+ }
+
+ private StatusBarNotification createNotificationWithNullTag() {
+ final StatusBarNotification notification = mock(StatusBarNotification.class);
+ return notification;
+ }
+
+ private StatusBarNotification createFaceNotification() {
+ final StatusBarNotification notification = mock(StatusBarNotification.class);
+ when(notification.getTag())
+ .thenReturn(BiometricNotificationUtils.FACE_ENROLL_NOTIFICATION_TAG);
+ return notification;
+ }
+
+ private StatusBarNotification createFingerprintNotification() {
+ final StatusBarNotification notification = mock(StatusBarNotification.class);
+ when(notification.getTag())
+ .thenReturn(BiometricNotificationUtils.FINGERPRINT_ENROLL_NOTIFICATION_TAG);
+ return notification;
+ }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index 35ad55c0938e..49583ef5194b 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -187,6 +187,9 @@ public class BiometricServiceTest {
@Mock
private IGateKeeperService mGateKeeperService;
+ @Mock
+ private BiometricNotificationLogger mNotificationLogger;
+
BiometricContextProvider mBiometricContextProvider;
@Before
@@ -242,6 +245,7 @@ public class BiometricServiceTest {
when(mInjector.getBiometricContext(any())).thenReturn(mBiometricContextProvider);
when(mInjector.getKeystoreAuthorizationService()).thenReturn(mKeystoreAuthService);
when(mInjector.getGateKeeperService()).thenReturn(mGateKeeperService);
+ when(mInjector.getNotificationLogger()).thenReturn(mNotificationLogger);
when(mGateKeeperService.getSecureUserId(anyInt())).thenReturn(42L);
if (com.android.server.biometrics.Flags.deHidl()) {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
index b69b55448bcd..238a9289c05b 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
@@ -137,11 +137,11 @@ public class BiometricLoggerTest {
final long latency = 44;
final boolean enrollSuccessful = true;
- mLogger.logOnEnrolled(targetUserId, latency, enrollSuccessful);
+ mLogger.logOnEnrolled(targetUserId, latency, enrollSuccessful, -1);
verify(mSink).enroll(
eq(DEFAULT_MODALITY), eq(DEFAULT_ACTION), eq(DEFAULT_CLIENT),
- eq(targetUserId), eq(latency), eq(enrollSuccessful), anyFloat());
+ eq(targetUserId), eq(latency), eq(enrollSuccessful), anyFloat(), anyInt());
}
@Test
@@ -192,7 +192,8 @@ public class BiometricLoggerTest {
true/* isBiometricPrompt */);
mLogger.logOnEnrolled(2 /* targetUserId */,
10 /* latency */,
- true /* enrollSuccessful */);
+ true /* enrollSuccessful */,
+ 30 /* source */);
mLogger.logOnError(mContext, mOpContext,
4 /* error */,
0 /* vendorCode */,
@@ -205,7 +206,8 @@ public class BiometricLoggerTest {
anyInt(), anyInt(), anyInt(), anyBoolean(),
anyLong(), anyInt(), anyBoolean(), anyInt(), anyFloat());
verify(mSink, never()).enroll(
- anyInt(), anyInt(), anyInt(), anyInt(), anyLong(), anyBoolean(), anyFloat());
+ anyInt(), anyInt(), anyInt(), anyInt(), anyLong(), anyBoolean(), anyFloat(),
+ anyInt());
verify(mSink, never()).error(eq(mOpContext),
anyInt(), anyInt(), anyInt(), anyBoolean(),
anyLong(), anyInt(), anyInt(), anyInt());
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index f7480dea780b..981eba55a430 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -1139,7 +1139,7 @@ public class BiometricSchedulerTest {
super(context, lazyDaemon, token, listener, 0 /* userId */, new byte[69],
"test" /* owner */, mock(BiometricUtils.class), 5 /* timeoutSec */,
TEST_SENSOR_ID, true /* shouldVibrate */, mock(BiometricLogger.class),
- mock(BiometricContext.class));
+ mock(BiometricContext.class), 0 /* enrollReason */);
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
index 43ed07a0bf76..02363cd66349 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
@@ -21,15 +21,20 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyByte;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.same;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.face.ISession;
import android.hardware.face.Face;
+import android.hardware.face.FaceEnrollOptions;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
@@ -68,6 +73,7 @@ public class FaceEnrollClientTest {
private static final byte[] HAT = new byte[69];
private static final int USER_ID = 12;
+ private static final int ENROLL_SOURCE = FaceEnrollOptions.ENROLL_REASON_SUW;
@Rule
public final TestableContext mContext = new TestableContext(
@@ -208,6 +214,16 @@ public class FaceEnrollClientTest {
verify(mHal).enrollWithOptions(any());
}
+ @Test
+ public void testEnrollWithReasonLogsMetric() throws RemoteException {
+ final FaceEnrollClient client = createClient(4);
+ client.start(mCallback);
+ client.onEnrollResult(new Face("face", 1 /* faceId */, 20 /* deviceId */), 0);
+
+ verify(mBiometricLogger).logOnEnrolled(anyInt(), anyLong(), anyBoolean(),
+ eq(BiometricsProtoEnums.ENROLLMENT_SOURCE_SUW));
+ }
+
private FaceEnrollClient createClient() throws RemoteException {
return createClient(200 /* version */);
}
@@ -221,6 +237,7 @@ public class FaceEnrollClientTest {
mUtils, new int[0] /* disabledFeatures */, 6 /* timeoutSec */,
null /* previewSurface */, 8 /* sensorId */,
mBiometricLogger, mBiometricContext, 5 /* maxTemplatesPerUser */,
- true /* debugConsent */);
+ true /* debugConsent */,
+ (new FaceEnrollOptions.Builder()).setEnrollReason(ENROLL_SOURCE).build());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java
index 940fe69925b5..7a778d5dd211 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java
@@ -34,6 +34,7 @@ import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.biometrics.face.V1_0.OptionalUint64;
import android.hardware.biometrics.face.V1_0.Status;
import android.hardware.face.Face;
+import android.hardware.face.FaceEnrollOptions;
import android.hardware.face.HidlFaceSensorConfig;
import android.os.Handler;
import android.os.RemoteException;
@@ -201,7 +202,7 @@ public class HidlToAidlSensorAdapterTest {
USER_ID, HAT, TAG, 1 /* requestId */, mBiometricUtils,
new int[]{} /* disabledFeatures */, ENROLL_TIMEOUT_SEC, null /* previewSurface */,
SENSOR_ID, mLogger, mBiometricContext, 1 /* maxTemplatesPerUser */,
- false /* debugConsent */));
+ false /* debugConsent */, (new FaceEnrollOptions.Builder()).build()));
mLooper.dispatchAll();
verify(mAidlResponseHandlerCallback).onEnrollSuccess();
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
index 3ee54f53d44f..916f6960efc9 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java
@@ -21,6 +21,7 @@ import static com.android.systemui.shared.Flags.FLAG_SIDEFPS_CONTROLLER_REFACTOR
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.inOrder;
@@ -30,10 +31,12 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.fingerprint.ISession;
import android.hardware.biometrics.fingerprint.PointerContext;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.ISidefpsController;
@@ -78,6 +81,8 @@ import java.util.function.Consumer;
@SmallTest
public class FingerprintEnrollClientTest {
+ private static final int ENROLL_SOURCE = FingerprintEnrollOptions.ENROLL_REASON_SUW;
+
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Rule
public final CheckFlagsRule mCheckFlagsRule =
@@ -353,6 +358,16 @@ public class FingerprintEnrollClientTest {
c -> c.onEnrollResult(new Fingerprint("", 1, 1), 0));
}
+ @Test
+ public void testEnrollWithReasonLogsMetric() throws RemoteException {
+ final FingerprintEnrollClient client = createClient(4);
+ client.start(mCallback);
+ client.onEnrollResult(new Fingerprint("fingerprint", 1 /* faceId */, 20 /* deviceId */), 0);
+
+ verify(mBiometricLogger).logOnEnrolled(anyInt(), anyLong(), anyBoolean(),
+ eq(BiometricsProtoEnums.ENROLLMENT_SOURCE_SUW));
+ }
+
private void showHideOverlay_sidefpsControllerRemovalRefactor(
Consumer<FingerprintEnrollClient> block) throws RemoteException {
mSetFlagsRule.enableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR);
@@ -382,6 +397,8 @@ public class FingerprintEnrollClientTest {
HAT, "owner", mBiometricUtils, 8 /* sensorId */,
mBiometricLogger, mBiometricContext, mSensorProps, mUdfpsOverlayController,
mSideFpsController, mAuthenticationStateListeners, 6 /* maxTemplatesPerUser */,
- FingerprintManager.ENROLL_ENROLL);
+ FingerprintManager.ENROLL_ENROLL, (new FingerprintEnrollOptions.Builder())
+ .setEnrollReason(ENROLL_SOURCE).build()
+ );
}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapterTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapterTest.java
index cbbc54547bf6..6c3bfe80510e 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapterTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapterTest.java
@@ -34,6 +34,7 @@ import android.app.AlarmManager;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintEnrollOptions;
import android.hardware.fingerprint.HidlFingerprintSensorConfig;
import android.os.Handler;
import android.os.HandlerThread;
@@ -217,7 +218,8 @@ public class HidlToAidlSensorAdapterTest {
1 /* requestId */, null /* listener */, USER_ID, HAT, TAG, mBiometricUtils,
SENSOR_ID, mLogger, mBiometricContext,
mHidlToAidlSensorAdapter.getSensorProperties(), null, null,
- mAuthenticationStateListeners, 5 /* maxTemplatesPerUser */, ENROLL_ENROLL));
+ mAuthenticationStateListeners, 5 /* maxTemplatesPerUser */, ENROLL_ENROLL,
+ (new FingerprintEnrollOptions.Builder()).build()));
mLooper.dispatchAll();
verify(mAidlResponseHandlerCallback).onEnrollSuccess();
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
index 132b6219977a..ec3e97b641b8 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
@@ -38,6 +38,7 @@ import static org.mockito.Mockito.when;
import android.app.WindowConfiguration;
import android.companion.virtual.IVirtualDeviceIntentInterceptor;
import android.companion.virtual.VirtualDeviceManager;
+import android.content.AttributionSource;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -712,6 +713,7 @@ public class GenericWindowPolicyControllerTest {
return new GenericWindowPolicyController(
0,
0,
+ AttributionSource.myAttributionSource(),
/* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
/* activityLaunchAllowedByDefault= */ true,
/* activityPolicyExemptions= */ new ArraySet<>(),
@@ -732,6 +734,7 @@ public class GenericWindowPolicyControllerTest {
return new GenericWindowPolicyController(
0,
0,
+ AttributionSource.myAttributionSource(),
/* allowedUsers= */ new ArraySet<>(),
/* activityLaunchAllowedByDefault= */ true,
/* activityPolicyExemptions= */ new ArraySet<>(),
@@ -753,6 +756,7 @@ public class GenericWindowPolicyControllerTest {
return new GenericWindowPolicyController(
0,
0,
+ AttributionSource.myAttributionSource(),
/* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
/* activityLaunchAllowedByDefault= */ true,
/* activityPolicyExemptions= */ new ArraySet<>(),
@@ -774,6 +778,7 @@ public class GenericWindowPolicyControllerTest {
return new GenericWindowPolicyController(
0,
0,
+ AttributionSource.myAttributionSource(),
/* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
/* activityLaunchAllowedByDefault= */ true,
/* activityPolicyExemptions= */ Collections.singleton(blockedComponent),
@@ -795,6 +800,7 @@ public class GenericWindowPolicyControllerTest {
return new GenericWindowPolicyController(
0,
0,
+ AttributionSource.myAttributionSource(),
/* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
/* activityLaunchAllowedByDefault= */ false,
/* activityPolicyExemptions= */ Collections.singleton(allowedComponent),
@@ -816,6 +822,7 @@ public class GenericWindowPolicyControllerTest {
return new GenericWindowPolicyController(
0,
0,
+ AttributionSource.myAttributionSource(),
/* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
/* activityLaunchAllowedByDefault= */ true,
/* activityPolicyExemptions= */ new ArraySet<>(),
@@ -837,6 +844,7 @@ public class GenericWindowPolicyControllerTest {
return new GenericWindowPolicyController(
0,
0,
+ AttributionSource.myAttributionSource(),
/* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
/* activityLaunchAllowedByDefault= */ true,
/* activityPolicyExemptions= */ new ArraySet<>(),
@@ -858,6 +866,7 @@ public class GenericWindowPolicyControllerTest {
return new GenericWindowPolicyController(
0,
0,
+ AttributionSource.myAttributionSource(),
/* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
/* activityLaunchAllowedByDefault= */ true,
/* activityPolicyExemptions= */ new ArraySet<>(),
@@ -880,6 +889,7 @@ public class GenericWindowPolicyControllerTest {
return new GenericWindowPolicyController(
0,
0,
+ AttributionSource.myAttributionSource(),
/* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
/* activityLaunchAllowedByDefault= */ true,
/* activityPolicyExemptions= */ new ArraySet<>(),
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
index 07e6ab2d08fb..fd880dd23f25 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
@@ -25,6 +25,7 @@ import static org.mockito.ArgumentMatchers.startsWith;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
+import android.content.AttributionSource;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.input.IInputManager;
import android.hardware.input.InputManagerGlobal;
@@ -95,6 +96,7 @@ public class InputControllerTest {
mInputController = new InputController(mNativeWrapperMock,
new Handler(TestableLooper.get(this).getLooper()),
InstrumentationRegistry.getTargetContext().getSystemService(WindowManager.class),
+ AttributionSource.myAttributionSource(),
threadVerifier);
}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java
index faece4fbb325..67fc564fa778 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java
@@ -32,6 +32,7 @@ import android.companion.virtual.sensor.IVirtualSensorCallback;
import android.companion.virtual.sensor.VirtualSensor;
import android.companion.virtual.sensor.VirtualSensorConfig;
import android.companion.virtual.sensor.VirtualSensorEvent;
+import android.content.AttributionSource;
import android.hardware.Sensor;
import android.os.Binder;
import android.os.IBinder;
@@ -96,6 +97,7 @@ public class SensorControllerTest {
Throwable thrown = assertThrows(
RuntimeException.class,
() -> new SensorController(mVirtualDevice, VIRTUAL_DEVICE_ID,
+ AttributionSource.myAttributionSource(),
mVirtualSensorCallback, List.of(mVirtualSensorConfig)));
assertThat(thrown.getCause().getMessage())
@@ -168,6 +170,7 @@ public class SensorControllerTest {
doReturn(VIRTUAL_DEVICE_ID).when(mVirtualDevice).getDeviceId();
SensorController sensorController = new SensorController(mVirtualDevice, VIRTUAL_DEVICE_ID,
+ AttributionSource.myAttributionSource(),
mVirtualSensorCallback, List.of(mVirtualSensorConfig));
List<VirtualSensor> sensors = sensorController.getSensorList();
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index 157e8931edba..5e0806d58af3 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -389,7 +389,8 @@ public class VirtualDeviceManagerServiceTest {
final InputController.DeviceCreationThreadVerifier threadVerifier = () -> true;
mInputController = new InputController(mNativeWrapperMock,
new Handler(TestableLooper.get(this).getLooper()),
- mContext.getSystemService(WindowManager.class), threadVerifier);
+ mContext.getSystemService(WindowManager.class),
+ AttributionSource.myAttributionSource(), threadVerifier);
mCameraAccessController =
new CameraAccessController(mContext, mLocalService, mCameraAccessBlockedCallback);
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
index ef5270e8f003..52f28b9bdc50 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
@@ -27,6 +27,7 @@ import static org.mockito.Mockito.verify;
import android.companion.virtual.audio.IAudioConfigChangedCallback;
import android.companion.virtual.audio.IAudioRoutingCallback;
+import android.content.AttributionSource;
import android.content.Context;
import android.content.ContextWrapper;
import android.media.AudioPlaybackConfiguration;
@@ -72,11 +73,13 @@ public class VirtualAudioControllerTest {
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = Mockito.spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
- mVirtualAudioController = new VirtualAudioController(mContext);
+ mVirtualAudioController = new VirtualAudioController(mContext,
+ AttributionSource.myAttributionSource());
mGenericWindowPolicyController =
new GenericWindowPolicyController(
FLAG_SECURE,
SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
+ AttributionSource.myAttributionSource(),
/* allowedUsers= */ new ArraySet<>(),
/* activityLaunchAllowedByDefault= */ true,
/* activityPolicyExemptions= */ new ArraySet<>(),
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
index 81981e6b16ca..9ca1df09ffe9 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
@@ -37,6 +37,7 @@ import android.companion.virtual.camera.VirtualCameraCallback;
import android.companion.virtual.camera.VirtualCameraConfig;
import android.companion.virtualcamera.IVirtualCameraService;
import android.companion.virtualcamera.VirtualCameraConfiguration;
+import android.content.AttributionSource;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Looper;
@@ -104,7 +105,7 @@ public class VirtualCameraControllerTest {
public void registerCamera_registersCamera(int lensFacing) throws Exception {
mVirtualCameraController.registerCamera(createVirtualCameraConfig(
CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT_1, CAMERA_MAX_FPS_1, CAMERA_NAME_1,
- CAMERA_SENSOR_ORIENTATION_1, lensFacing));
+ CAMERA_SENSOR_ORIENTATION_1, lensFacing), AttributionSource.myAttributionSource());
ArgumentCaptor<VirtualCameraConfiguration> configurationCaptor =
ArgumentCaptor.forClass(VirtualCameraConfiguration.class);
@@ -121,7 +122,7 @@ public class VirtualCameraControllerTest {
VirtualCameraConfig config = createVirtualCameraConfig(
CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT_1, CAMERA_MAX_FPS_1, CAMERA_NAME_1,
CAMERA_SENSOR_ORIENTATION_1, CAMERA_LENS_FACING_1);
- mVirtualCameraController.registerCamera(config);
+ mVirtualCameraController.registerCamera(config, AttributionSource.myAttributionSource());
mVirtualCameraController.unregisterCamera(config);
@@ -131,11 +132,15 @@ public class VirtualCameraControllerTest {
@Test
public void close_unregistersAllCameras() throws Exception {
mVirtualCameraController.registerCamera(createVirtualCameraConfig(
- CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT_1, CAMERA_MAX_FPS_1, CAMERA_NAME_1,
- CAMERA_SENSOR_ORIENTATION_1, CAMERA_LENS_FACING_1));
+ CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT_1, CAMERA_MAX_FPS_1,
+ CAMERA_NAME_1,
+ CAMERA_SENSOR_ORIENTATION_1, CAMERA_LENS_FACING_1),
+ AttributionSource.myAttributionSource());
mVirtualCameraController.registerCamera(createVirtualCameraConfig(
- CAMERA_WIDTH_2, CAMERA_HEIGHT_2, CAMERA_FORMAT_2, CAMERA_MAX_FPS_2, CAMERA_NAME_2,
- CAMERA_SENSOR_ORIENTATION_2, CAMERA_LENS_FACING_2));
+ CAMERA_WIDTH_2, CAMERA_HEIGHT_2, CAMERA_FORMAT_2, CAMERA_MAX_FPS_2,
+ CAMERA_NAME_2,
+ CAMERA_SENSOR_ORIENTATION_2, CAMERA_LENS_FACING_2),
+ AttributionSource.myAttributionSource());
mVirtualCameraController.close();
@@ -160,11 +165,12 @@ public class VirtualCameraControllerTest {
int lensFacing) {
mVirtualCameraController.registerCamera(createVirtualCameraConfig(
CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT_1, CAMERA_MAX_FPS_1, CAMERA_NAME_1,
- CAMERA_SENSOR_ORIENTATION_1, lensFacing));
+ CAMERA_SENSOR_ORIENTATION_1, lensFacing), AttributionSource.myAttributionSource());
assertThrows(IllegalArgumentException.class,
() -> mVirtualCameraController.registerCamera(createVirtualCameraConfig(
- CAMERA_WIDTH_2, CAMERA_HEIGHT_2, CAMERA_FORMAT_2, CAMERA_MAX_FPS_2,
- CAMERA_NAME_2, CAMERA_SENSOR_ORIENTATION_2, lensFacing)));
+ CAMERA_WIDTH_2, CAMERA_HEIGHT_2, CAMERA_FORMAT_2, CAMERA_MAX_FPS_2,
+ CAMERA_NAME_2, CAMERA_SENSOR_ORIENTATION_2, lensFacing),
+ AttributionSource.myAttributionSource()));
}
@Parameters(method = "getAllLensFacingDirections")
@@ -176,8 +182,9 @@ public class VirtualCameraControllerTest {
assertThrows(IllegalArgumentException.class,
() -> mVirtualCameraController.registerCamera(createVirtualCameraConfig(
- CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT_1, CAMERA_MAX_FPS_1,
- CAMERA_NAME_1, CAMERA_SENSOR_ORIENTATION_1, lensFacing)));
+ CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT_1, CAMERA_MAX_FPS_1,
+ CAMERA_NAME_1, CAMERA_SENSOR_ORIENTATION_1, lensFacing),
+ AttributionSource.myAttributionSource()));
}
private VirtualCameraConfig createVirtualCameraConfig(
@@ -203,7 +210,7 @@ public class VirtualCameraControllerTest {
}
private static Integer[] getAllLensFacingDirections() {
- return new Integer[] {
+ return new Integer[]{
LENS_FACING_BACK,
LENS_FACING_FRONT
};
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index 6986cab72f56..e59b5ea027ed 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -270,6 +270,9 @@ public abstract class BaseLockSettingsServiceTests {
}
protected void setSecureFrpMode(boolean secure) {
+ if (android.security.Flags.frpEnforcement()) {
+ mStorage.setTestFactoryResetProtectionState(secure);
+ }
Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.SECURE_FRP_MODE, secure ? 1 : 0, UserHandle.USER_SYSTEM);
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
index ee076c6bcf4b..296d2cba83dd 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
@@ -21,12 +21,14 @@ import static org.mockito.Mockito.mock;
import android.app.IActivityManager;
import android.app.admin.DeviceStateCache;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.UserInfo;
import android.hardware.authsecret.IAuthSecret;
import android.os.Handler;
import android.os.Parcel;
import android.os.Process;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.os.storage.IStorageManager;
import android.security.KeyStore;
import android.security.keystore.KeyPermanentlyInvalidatedException;
@@ -41,6 +43,9 @@ import com.android.server.pm.UserManagerInternal;
import java.io.FileNotFoundException;
public class LockSettingsServiceTestable extends LockSettingsService {
+ private Intent mSavedFrpNotificationIntent = null;
+ private UserHandle mSavedFrpNotificationUserHandle = null;
+ private String mSavedFrpNotificationPermission = null;
public static class MockInjector extends LockSettingsService.Injector {
@@ -218,4 +223,29 @@ public class LockSettingsServiceTestable extends LockSettingsService {
mAuthSecret = null;
}
}
+
+ @Override
+ void sendBroadcast(Intent intent, UserHandle userHandle, String permission) {
+ mSavedFrpNotificationIntent = intent;
+ mSavedFrpNotificationUserHandle = userHandle;
+ mSavedFrpNotificationPermission = permission;
+ }
+
+ String getSavedFrpNotificationPermission() {
+ return mSavedFrpNotificationPermission;
+ }
+
+ UserHandle getSavedFrpNotificationUserHandle() {
+ return mSavedFrpNotificationUserHandle;
+ }
+
+ Intent getSavedFrpNotificationIntent() {
+ return mSavedFrpNotificationIntent;
+ }
+
+ void clearRecordedFrpNotificationData() {
+ mSavedFrpNotificationIntent = null;
+ mSavedFrpNotificationPermission = null;
+ mSavedFrpNotificationUserHandle = null;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
index 705359708bc7..4b22652a3f21 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
@@ -16,6 +16,7 @@
package com.android.server.locksettings;
+import static android.Manifest.permission.CONFIGURE_FACTORY_RESET_PROTECTION;
import static android.security.Flags.FLAG_REPORT_PRIMARY_AUTH_ATTEMPTS;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
@@ -39,7 +40,9 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.PropertyInvalidatedCache;
+import android.content.Intent;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import android.service.gatekeeper.GateKeeperResponse;
@@ -239,6 +242,12 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
}
@Test
+ public void testSetLockCredential_forPrimaryUser_sendsFrpNotification() throws Exception {
+ setCredential(PRIMARY_USER_ID, newPassword("password"));
+ checkRecordedFrpNotificationIntent();
+ }
+
+ @Test
public void testSetLockCredential_forPrimaryUser_sendsCredentials() throws Exception {
setCredential(PRIMARY_USER_ID, newPassword("password"));
verify(mRecoverableKeyStoreManager)
@@ -323,6 +332,15 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
}
@Test
+ public void testClearLockCredential_sendsFrpNotification() throws Exception {
+ setCredential(PRIMARY_USER_ID, newPassword("password"));
+ checkRecordedFrpNotificationIntent();
+ mService.clearRecordedFrpNotificationData();
+ clearCredential(PRIMARY_USER_ID, newPassword("password"));
+ checkRecordedFrpNotificationIntent();
+ }
+
+ @Test
public void testSetLockCredential_forUnifiedToSeparateChallengeProfile_sendsNewCredentials()
throws Exception {
final LockscreenCredential parentPassword = newPassword("parentPassword");
@@ -519,6 +537,23 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
mService.setString(null, "value", 0);
}
+ private void checkRecordedFrpNotificationIntent() {
+ if (android.security.Flags.frpEnforcement()) {
+ Intent savedNotificationIntent = mService.getSavedFrpNotificationIntent();
+ assertNotNull(savedNotificationIntent);
+ UserHandle userHandle = mService.getSavedFrpNotificationUserHandle();
+ assertEquals(userHandle,
+ UserHandle.of(mInjector.getUserManagerInternal().getMainUserId()));
+
+ String permission = mService.getSavedFrpNotificationPermission();
+ assertEquals(CONFIGURE_FACTORY_RESET_PROTECTION, permission);
+ } else {
+ assertNull(mService.getSavedFrpNotificationIntent());
+ assertNull(mService.getSavedFrpNotificationUserHandle());
+ assertNull(mService.getSavedFrpNotificationPermission());
+ }
+ }
+
private void checkPasswordHistoryLength(int userId, int expectedLen) {
String history = mService.getString(LockPatternUtils.PASSWORD_HISTORY_KEY, "", userId);
String[] hashes = TextUtils.split(history, LockPatternUtils.PASSWORD_HISTORY_DELIMITER);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
index fa3c7a4c4769..c01d0f644983 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
@@ -35,6 +35,7 @@ public class LockSettingsStorageTestable extends LockSettingsStorage {
public final File mStorageDir;
public PersistentDataBlockManagerInternal mPersistentDataBlockManager;
private byte[] mPersistentData;
+ private boolean mIsFactoryResetProtectionActive = false;
public LockSettingsStorageTestable(Context context, File storageDir) {
super(context);
@@ -63,6 +64,10 @@ public class LockSettingsStorageTestable extends LockSettingsStorage {
}).when(mPersistentDataBlockManager).getFrpCredentialHandle();
}
+ void setTestFactoryResetProtectionState(boolean active) {
+ mIsFactoryResetProtectionActive = active;
+ }
+
@Override
File getChildProfileLockFile(int userId) {
return remapToStorageDir(super.getChildProfileLockFile(userId));
@@ -101,4 +106,9 @@ public class LockSettingsStorageTestable extends LockSettingsStorage {
mappedPath.getParentFile().mkdirs();
return mappedPath;
}
+
+ @Override
+ public boolean isFactoryResetProtectionActive() {
+ return mIsFactoryResetProtectionActive;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 4451cae8db42..a529382bfb26 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -18,6 +18,7 @@ package com.android.server.net;
import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
import static android.Manifest.permission.NETWORK_STACK;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL;
import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE;
import static android.app.ActivityManager.PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK;
import static android.app.ActivityManager.PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK;
@@ -58,9 +59,11 @@ import static android.net.NetworkPolicyManager.ALLOWED_REASON_SYSTEM;
import static android.net.NetworkPolicyManager.ALLOWED_REASON_TOP;
import static android.net.NetworkPolicyManager.BACKGROUND_THRESHOLD_STATE;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
+import static android.net.NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE;
import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
import static android.net.NetworkPolicyManager.POLICY_NONE;
import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
+import static android.net.NetworkPolicyManager.TOP_THRESHOLD_STATE;
import static android.net.NetworkPolicyManager.allowedReasonsToString;
import static android.net.NetworkPolicyManager.blockedReasonsToString;
import static android.net.NetworkPolicyManager.uidPoliciesToString;
@@ -88,6 +91,7 @@ import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT;
import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT_SNOOZED;
import static com.android.server.net.NetworkPolicyManagerService.TYPE_RAPID;
import static com.android.server.net.NetworkPolicyManagerService.TYPE_WARNING;
+import static com.android.server.net.NetworkPolicyManagerService.UID_MSG_STATE_CHANGED;
import static com.android.server.net.NetworkPolicyManagerService.UidBlockedState.getEffectiveBlockedReasons;
import static com.android.server.net.NetworkPolicyManagerService.normalizeTemplate;
@@ -196,13 +200,12 @@ import com.android.server.LocalServices;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.usage.AppStandbyInternal;
-import com.google.common.util.concurrent.AbstractFuture;
-
import libcore.io.Streams;
import org.junit.After;
import org.junit.Assume;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.MethodRule;
@@ -241,10 +244,8 @@ import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -2143,13 +2144,14 @@ public class NetworkPolicyManagerServiceTest {
assertFalse(mService.isUidNetworkingBlocked(UID_E, false));
}
+ @Ignore("Temporarily disabled until the feature is enabled")
@Test
@RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
public void testBackgroundChainEnabled() throws Exception {
verify(mNetworkManager).setFirewallChainEnabled(FIREWALL_CHAIN_BACKGROUND, true);
}
-
+ @Ignore("Temporarily disabled until the feature is enabled")
@Test
@RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
public void testBackgroundChainOnProcStateChange() throws Exception {
@@ -2179,6 +2181,7 @@ public class NetworkPolicyManagerServiceTest {
assertTrue(mService.isUidNetworkingBlocked(UID_A, false));
}
+ @Ignore("Temporarily disabled until the feature is enabled")
@Test
@RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
public void testBackgroundChainOnAllowlistChange() throws Exception {
@@ -2217,6 +2220,7 @@ public class NetworkPolicyManagerServiceTest {
assertFalse(mService.isUidNetworkingBlocked(UID_B, false));
}
+ @Ignore("Temporarily disabled until the feature is enabled")
@Test
@RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
public void testBackgroundChainOnTempAllowlistChange() throws Exception {
@@ -2246,6 +2250,126 @@ public class NetworkPolicyManagerServiceTest {
assertTrue(mService.isUidNetworkingBlocked(UID_A, false));
}
+ @Ignore("Temporarily disabled until the feature is enabled")
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
+ public void testUidObserverFiltersProcStateChanges() throws Exception {
+ int testProcStateSeq = 0;
+ try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
+ // First callback for uid.
+ callOnUidStatechanged(UID_A, BACKGROUND_THRESHOLD_STATE + 1, testProcStateSeq++,
+ PROCESS_CAPABILITY_NONE);
+ assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
+ }
+ waitForUidEventHandlerIdle();
+ try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
+ // Doesn't cross the background threshold.
+ callOnUidStatechanged(UID_A, BACKGROUND_THRESHOLD_STATE, testProcStateSeq++,
+ PROCESS_CAPABILITY_NONE);
+ assertFalse(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
+ }
+ waitForUidEventHandlerIdle();
+ try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
+ // Crosses the background threshold.
+ callOnUidStatechanged(UID_A, BACKGROUND_THRESHOLD_STATE - 1, testProcStateSeq++,
+ PROCESS_CAPABILITY_NONE);
+ assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
+ }
+ waitForUidEventHandlerIdle();
+ try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
+ // Doesn't cross the foreground threshold.
+ callOnUidStatechanged(UID_A, FOREGROUND_THRESHOLD_STATE + 1, testProcStateSeq++,
+ PROCESS_CAPABILITY_NONE);
+ assertFalse(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
+ }
+ waitForUidEventHandlerIdle();
+ try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
+ // Crosses the foreground threshold.
+ callOnUidStatechanged(UID_A, FOREGROUND_THRESHOLD_STATE, testProcStateSeq++,
+ PROCESS_CAPABILITY_NONE);
+ assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
+ }
+ waitForUidEventHandlerIdle();
+ try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
+ // Doesn't cross the top threshold.
+ callOnUidStatechanged(UID_A, TOP_THRESHOLD_STATE + 1, testProcStateSeq++,
+ PROCESS_CAPABILITY_NONE);
+ assertFalse(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
+ }
+ waitForUidEventHandlerIdle();
+ try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
+ // Crosses the top threshold.
+ callOnUidStatechanged(UID_A, TOP_THRESHOLD_STATE, testProcStateSeq++,
+ PROCESS_CAPABILITY_NONE);
+ assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
+ }
+ waitForUidEventHandlerIdle();
+ try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
+ // Doesn't cross any other threshold.
+ callOnUidStatechanged(UID_A, TOP_THRESHOLD_STATE - 1, testProcStateSeq++,
+ PROCESS_CAPABILITY_NONE);
+ assertFalse(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
+ }
+ waitForUidEventHandlerIdle();
+ }
+
+ @Ignore("Temporarily disabled until the feature is enabled")
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
+ public void testUidObserverFiltersStaleChanges() throws Exception {
+ final int testProcStateSeq = 51;
+ try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
+ // First callback for uid.
+ callOnUidStatechanged(UID_B, BACKGROUND_THRESHOLD_STATE + 100, testProcStateSeq,
+ PROCESS_CAPABILITY_NONE);
+ assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
+ }
+ waitForUidEventHandlerIdle();
+ try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
+ // Stale callback because the procStateSeq is smaller.
+ callOnUidStatechanged(UID_B, BACKGROUND_THRESHOLD_STATE - 100, testProcStateSeq - 10,
+ PROCESS_CAPABILITY_NONE);
+ assertFalse(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
+ }
+ waitForUidEventHandlerIdle();
+ }
+
+ @Ignore("Temporarily disabled until the feature is enabled")
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
+ public void testUidObserverFiltersCapabilityChanges() throws Exception {
+ int testProcStateSeq = 0;
+ try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
+ // First callback for uid.
+ callOnUidStatechanged(UID_A, TOP_THRESHOLD_STATE, testProcStateSeq++,
+ PROCESS_CAPABILITY_NONE);
+ assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
+ }
+ waitForUidEventHandlerIdle();
+ try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
+ // The same process-state with one network capability added.
+ callOnUidStatechanged(UID_A, TOP_THRESHOLD_STATE, testProcStateSeq++,
+ PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK);
+ assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
+ }
+ waitForUidEventHandlerIdle();
+ try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
+ // The same process-state with another network capability added.
+ callOnUidStatechanged(UID_A, TOP_THRESHOLD_STATE, testProcStateSeq++,
+ PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK
+ | PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK);
+ assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
+ }
+ waitForUidEventHandlerIdle();
+ try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
+ // The same process-state with all capabilities, but no change in network capabilities.
+ callOnUidStatechanged(UID_A, TOP_THRESHOLD_STATE, testProcStateSeq++,
+ PROCESS_CAPABILITY_ALL);
+ assertFalse(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
+ }
+ waitForUidEventHandlerIdle();
+ }
+
@Test
public void testLowPowerStandbyAllowlist() throws Exception {
// Chain background is also enabled but these procstates are important enough to be exempt.
@@ -2559,17 +2683,6 @@ public class NetworkPolicyManagerServiceTest {
verify(mStatsManager).setDefaultGlobalAlert(anyLong());
}
- private static class TestAbstractFuture<T> extends AbstractFuture<T> {
- @Override
- public T get() throws InterruptedException, ExecutionException {
- try {
- return get(5, TimeUnit.SECONDS);
- } catch (TimeoutException e) {
- throw new RuntimeException(e);
- }
- }
- }
-
private static void assertTimeEquals(long expected, long actual) {
if (expected != actual) {
fail("expected " + formatTime(expected) + " but was actually " + formatTime(actual));
diff --git a/services/tests/servicestests/src/com/android/server/pdb/PersistentDataBlockServiceTest.java b/services/tests/servicestests/src/com/android/server/pdb/PersistentDataBlockServiceTest.java
index da8ec2e4ec15..f91f77a56385 100644
--- a/services/tests/servicestests/src/com/android/server/pdb/PersistentDataBlockServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pdb/PersistentDataBlockServiceTest.java
@@ -17,12 +17,17 @@
package com.android.server.pdb;
import static com.android.server.pdb.PersistentDataBlockService.DIGEST_SIZE_BYTES;
+import static com.android.server.pdb.PersistentDataBlockService.FRP_CREDENTIAL_RESERVED_SIZE;
+import static com.android.server.pdb.PersistentDataBlockService.FRP_SECRET_MAGIC;
import static com.android.server.pdb.PersistentDataBlockService.FRP_SECRET_SIZE;
+import static com.android.server.pdb.PersistentDataBlockService.HEADER_SIZE;
import static com.android.server.pdb.PersistentDataBlockService.MAX_DATA_BLOCK_SIZE;
import static com.android.server.pdb.PersistentDataBlockService.MAX_FRP_CREDENTIAL_HANDLE_SIZE;
import static com.android.server.pdb.PersistentDataBlockService.MAX_TEST_MODE_DATA_SIZE;
+import static com.android.server.pdb.PersistentDataBlockService.TEST_MODE_RESERVED_SIZE;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -36,6 +41,7 @@ import static org.mockito.Mockito.when;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import android.Manifest;
+import android.annotation.NonNull;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
@@ -63,6 +69,7 @@ import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
@RunWith(JUnitParamsRunner.class)
public class PersistentDataBlockServiceTest {
@@ -397,6 +404,68 @@ public class PersistentDataBlockServiceTest {
@Test
@Parameters({"false", "true"})
+ public void testPartitionFormat(boolean frpEnabled) throws Exception {
+ setUp(frpEnabled);
+
+ /*
+ * 1. Fill the PDB with a specific value, so we can check regions that weren't touched
+ * by formatting
+ */
+ FileChannel channel = FileChannel.open(mDataBlockFile.toPath(), StandardOpenOption.WRITE,
+ StandardOpenOption.TRUNCATE_EXISTING);
+ byte[] bufArray = new byte[(int) mPdbService.getBlockDeviceSize()];
+ Arrays.fill(bufArray, (byte) 0x7f);
+ ByteBuffer buf = ByteBuffer.wrap(bufArray);
+ channel.write(buf);
+ channel.close();
+
+ /*
+ * 2. Format it.
+ */
+ mPdbService.formatPartitionLocked(true);
+
+ /*
+ * 3. Check it.
+ */
+ channel = FileChannel.open(mDataBlockFile.toPath(), StandardOpenOption.READ);
+
+ // 3a. Skip the digest and header
+ channel.position(channel.position() + DIGEST_SIZE_BYTES + HEADER_SIZE);
+
+ // 3b. Check the FRP data segment
+ assertContains("FRP data", readData(channel, mPdbService.getMaximumFrpDataSize()).array(),
+ (byte) 0);
+
+ if (frpEnabled) {
+ // 3c. The FRP secret magic & value
+ assertThat(mPdbService.getFrpSecretMagicOffset()).isEqualTo(channel.position());
+ assertThat(readData(channel, FRP_SECRET_MAGIC.length).array()).isEqualTo(
+ FRP_SECRET_MAGIC);
+
+ assertThat(mPdbService.getFrpSecretDataOffset()).isEqualTo(channel.position());
+ assertContains("FRP secret", readData(channel, FRP_SECRET_SIZE).array(), (byte) 0);
+ }
+
+ // 3d. The test mode data (unmodified by formatPartitionLocked()).
+ assertThat(mPdbService.getTestHarnessModeDataOffset()).isEqualTo(channel.position());
+ assertContains("Test data", readData(channel, TEST_MODE_RESERVED_SIZE).array(),
+ (byte) 0x7f);
+
+ // 3e. The FRP credential segment
+ assertThat(mPdbService.getFrpCredentialDataOffset()).isEqualTo(channel.position());
+ assertContains("FRP credential", readData(channel, FRP_CREDENTIAL_RESERVED_SIZE).array(),
+ (byte) 0);
+
+ // 3f. OEM unlock byte.
+ assertThat(mPdbService.getOemUnlockDataOffset()).isEqualTo(channel.position());
+ assertThat(new byte[]{1}).isEqualTo(readData(channel, 1).array());
+
+ // 3g. EOF
+ assertThat(channel.position()).isEqualTo(channel.size());
+ }
+
+ @Test
+ @Parameters({"false", "true"})
public void wipePermissionCheck(boolean frpEnabled) throws Exception {
setUp(frpEnabled);
denyOemUnlockPermission();
@@ -987,4 +1056,20 @@ public class PersistentDataBlockServiceTest {
return buffer;
}
}
+
+ @NonNull
+ private static ByteBuffer readData(FileChannel channel, int length) throws IOException {
+ ByteBuffer buf = ByteBuffer.allocate(length);
+ assertThat(channel.read(buf)).isEqualTo(length);
+ buf.flip();
+ assertThat(buf.limit()).isEqualTo(length);
+ return buf;
+ }
+
+ private static void assertContains(String sectionName, byte[] buf, byte expected) {
+ for (int i = 0; i < buf.length; i++) {
+ assertWithMessage(sectionName + " is incorrect at offset " + i)
+ .that(buf[i]).isEqualTo(expected);
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java
index 1ae6e63c3ff1..4a43c2e6c180 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java
@@ -43,8 +43,10 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.InstallSourceInfo;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.ParceledListSlice;
import android.os.FileUtils;
import android.os.Looper;
import android.os.RemoteException;
@@ -115,9 +117,6 @@ public final class BackgroundInstallControlServiceTest {
private BackgroundInstallControlCallbackHelper mCallbackHelper;
@Captor
- private ArgumentCaptor<PackageManagerInternal.PackageListObserver> mPackageListObserverCaptor;
-
- @Captor
private ArgumentCaptor<UsageEventListener> mUsageEventListenerCaptor;
@Before
@@ -137,8 +136,8 @@ public final class BackgroundInstallControlServiceTest {
mUsageEventListener = mUsageEventListenerCaptor.getValue();
mBackgroundInstallControlService.onStart(true);
- verify(mPackageManagerInternal).getPackageList(mPackageListObserverCaptor.capture());
- mPackageListObserver = mPackageListObserverCaptor.getValue();
+
+ mPackageListObserver = mBackgroundInstallControlService.mPackageObserver;
}
@After
@@ -554,6 +553,7 @@ public final class BackgroundInstallControlServiceTest {
assertEquals(0, foregroundTimeFrames.size());
}
+ //package installed, but no UI interaction found
@Test
public void testHandleUsageEvent_packageAddedNoUsageEvent()
throws NoSuchFieldException, PackageManager.NameNotFoundException {
@@ -571,12 +571,10 @@ public final class BackgroundInstallControlServiceTest {
when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
.thenReturn(appInfo);
- long createTimestamp =
- PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis());
FieldSetter.setField(
appInfo,
ApplicationInfo.class.getDeclaredField("createTimestamp"),
- createTimestamp);
+ convertToTestAdjustTimestamp(PACKAGE_ADD_TIMESTAMP_1));
int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;
assertEquals(USER_ID_1, UserHandle.getUserId(uid));
@@ -590,6 +588,10 @@ public final class BackgroundInstallControlServiceTest {
assertTrue(packages.contains(USER_ID_1, PACKAGE_NAME_1));
}
+ private long convertToTestAdjustTimestamp(long timestamp) {
+ return timestamp - (System.currentTimeMillis() - SystemClock.uptimeMillis());
+ }
+
@Test
public void testHandleUsageEvent_packageAddedInsideTimeFrame()
throws NoSuchFieldException, PackageManager.NameNotFoundException {
@@ -607,12 +609,10 @@ public final class BackgroundInstallControlServiceTest {
when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
.thenReturn(appInfo);
- long createTimestamp =
- PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis());
FieldSetter.setField(
appInfo,
ApplicationInfo.class.getDeclaredField("createTimestamp"),
- createTimestamp);
+ convertToTestAdjustTimestamp(PACKAGE_ADD_TIMESTAMP_1));
int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;
assertEquals(USER_ID_1, UserHandle.getUserId(uid));
@@ -639,6 +639,122 @@ public final class BackgroundInstallControlServiceTest {
}
@Test
+ public void testHandleUsageEvent_fallsBackToAppInfoTimeWhenHistoricalSessionsNotFound()
+ throws NoSuchFieldException, PackageManager.NameNotFoundException {
+ assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages());
+ InstallSourceInfo installSourceInfo =
+ new InstallSourceInfo(
+ /* initiatingPackageName= */ INSTALLER_NAME_1,
+ /* initiatingPackageSigningInfo= */ null,
+ /* originatingPackageName= */ null,
+ /* installingPackageName= */ INSTALLER_NAME_1);
+ assertEquals(installSourceInfo.getInstallingPackageName(), INSTALLER_NAME_1);
+ when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo);
+ ApplicationInfo appInfo = mock(ApplicationInfo.class);
+
+ when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
+ .thenReturn(appInfo);
+
+ FieldSetter.setField(
+ appInfo,
+ ApplicationInfo.class.getDeclaredField("createTimestamp"),
+ // create timestamp is after generated foreground events (hence not considered
+ // foreground install)
+ convertToTestAdjustTimestamp(USAGE_EVENT_TIMESTAMP_2 + 100000));
+
+ int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;
+ assertEquals(USER_ID_1, UserHandle.getUserId(uid));
+
+ createPackageManagerHistoricalSessions(List.of(), USER_ID_1);
+
+ // The 2 relevants usage events are before the timeframe, the app is not considered
+ // foreground installed.
+ doReturn(PERMISSION_GRANTED)
+ .when(mPermissionManager)
+ .checkPermission(anyString(), anyString(), anyString(), anyInt());
+ generateUsageEvent(
+ UsageEvents.Event.ACTIVITY_RESUMED,
+ USER_ID_1,
+ INSTALLER_NAME_1,
+ USAGE_EVENT_TIMESTAMP_1);
+ generateUsageEvent(
+ Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2);
+
+ mPackageListObserver.onPackageAdded(PACKAGE_NAME_1, uid);
+ mTestLooper.dispatchAll();
+
+ var packages = mBackgroundInstallControlService.getBackgroundInstalledPackages();
+ assertNotNull(packages);
+ assertEquals(1, packages.size());
+ assertTrue(packages.contains(USER_ID_1, PACKAGE_NAME_1));
+ }
+
+ @Test
+ public void testHandleUsageEvent_usesHistoricalSessionCreateTimeWhenHistoricalSessionsFound()
+ throws NoSuchFieldException, PackageManager.NameNotFoundException {
+ assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages());
+ InstallSourceInfo installSourceInfo =
+ new InstallSourceInfo(
+ /* initiatingPackageName= */ INSTALLER_NAME_1,
+ /* initiatingPackageSigningInfo= */ null,
+ /* originatingPackageName= */ null,
+ /* installingPackageName= */ INSTALLER_NAME_1);
+ assertEquals(installSourceInfo.getInstallingPackageName(), INSTALLER_NAME_1);
+ when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(installSourceInfo);
+ ApplicationInfo appInfo = mock(ApplicationInfo.class);
+
+ when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
+ .thenReturn(appInfo);
+
+ FieldSetter.setField(
+ appInfo,
+ ApplicationInfo.class.getDeclaredField("createTimestamp"),
+ //create timestamp is out of window of (after) the interact events
+ convertToTestAdjustTimestamp(USAGE_EVENT_TIMESTAMP_2 + 1));
+
+ int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;
+ assertEquals(USER_ID_1, UserHandle.getUserId(uid));
+
+ PackageInstaller.SessionInfo installSession1 = mock(PackageInstaller.SessionInfo.class);
+ PackageInstaller.SessionInfo installSession2 = mock(PackageInstaller.SessionInfo.class);
+ doReturn(convertToTestAdjustTimestamp(0L)).when(installSession1).getCreatedMillis();
+ doReturn(convertToTestAdjustTimestamp(PACKAGE_ADD_TIMESTAMP_1)).when(installSession2)
+ .getCreatedMillis();
+ doReturn(PACKAGE_NAME_1).when(installSession1).getAppPackageName();
+ doReturn(PACKAGE_NAME_1).when(installSession2).getAppPackageName();
+ createPackageManagerHistoricalSessions(List.of(installSession1, installSession2),
+ USER_ID_1);
+
+ // The following 2 generated usage events occur after historical session create times hence,
+ // considered foreground install. The appInfo createTimestamp occurs after events, so the
+ // app would be considered background install if it falls back to it as reference create
+ // timestamp.
+ doReturn(PERMISSION_GRANTED)
+ .when(mPermissionManager)
+ .checkPermission(anyString(), anyString(), anyString(), anyInt());
+ generateUsageEvent(
+ UsageEvents.Event.ACTIVITY_RESUMED,
+ USER_ID_1,
+ INSTALLER_NAME_1,
+ USAGE_EVENT_TIMESTAMP_1);
+ generateUsageEvent(
+ Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2);
+
+ mPackageListObserver.onPackageAdded(PACKAGE_NAME_1, uid);
+ mTestLooper.dispatchAll();
+
+ assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages());
+ }
+
+ private void createPackageManagerHistoricalSessions(
+ List<PackageInstaller.SessionInfo> sessions, int userId) {
+ ParceledListSlice<PackageInstaller.SessionInfo> mockParcelList =
+ mock(ParceledListSlice.class);
+ when(mockParcelList.getList()).thenReturn(sessions);
+ when(mPackageManagerInternal.getHistoricalSessions(userId)).thenReturn(mockParcelList);
+ }
+
+ @Test
public void testHandleUsageEvent_packageAddedOutsideTimeFrame1()
throws NoSuchFieldException, PackageManager.NameNotFoundException {
assertNull(mBackgroundInstallControlService.getBackgroundInstalledPackages());
@@ -655,12 +771,10 @@ public final class BackgroundInstallControlServiceTest {
when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
.thenReturn(appInfo);
- long createTimestamp =
- PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis());
FieldSetter.setField(
appInfo,
ApplicationInfo.class.getDeclaredField("createTimestamp"),
- createTimestamp);
+ convertToTestAdjustTimestamp(PACKAGE_ADD_TIMESTAMP_1));
int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;
assertEquals(USER_ID_1, UserHandle.getUserId(uid));
@@ -708,12 +822,10 @@ public final class BackgroundInstallControlServiceTest {
when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
.thenReturn(appInfo);
- long createTimestamp =
- PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis());
FieldSetter.setField(
appInfo,
ApplicationInfo.class.getDeclaredField("createTimestamp"),
- createTimestamp);
+ convertToTestAdjustTimestamp(PACKAGE_ADD_TIMESTAMP_1));
int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;
assertEquals(USER_ID_1, UserHandle.getUserId(uid));
@@ -765,12 +877,10 @@ public final class BackgroundInstallControlServiceTest {
when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
.thenReturn(appInfo);
- long createTimestamp =
- PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis());
FieldSetter.setField(
appInfo,
ApplicationInfo.class.getDeclaredField("createTimestamp"),
- createTimestamp);
+ convertToTestAdjustTimestamp(PACKAGE_ADD_TIMESTAMP_1));
int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;
assertEquals(USER_ID_1, UserHandle.getUserId(uid));
@@ -818,12 +928,10 @@ public final class BackgroundInstallControlServiceTest {
when(mPackageManager.getApplicationInfoAsUser(eq(PACKAGE_NAME_1), any(), anyInt()))
.thenReturn(appInfo);
- long createTimestamp =
- PACKAGE_ADD_TIMESTAMP_1 - (System.currentTimeMillis() - SystemClock.uptimeMillis());
FieldSetter.setField(
appInfo,
ApplicationInfo.class.getDeclaredField("createTimestamp"),
- createTimestamp);
+ convertToTestAdjustTimestamp(PACKAGE_ADD_TIMESTAMP_1));
int uid = USER_ID_1 * UserHandle.PER_USER_RANGE;
assertEquals(USER_ID_1, UserHandle.getUserId(uid));
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 81df597f3f33..3e748ffb37e9 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -75,6 +75,7 @@ import android.content.pm.SigningDetails;
import android.content.pm.SigningInfo;
import android.content.pm.UserInfo;
import android.content.pm.UserPackage;
+import android.content.pm.UserProperties;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Icon;
@@ -776,6 +777,15 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
new UserInfo(USER_P1, "userP1",
UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_MANAGED_PROFILE), 0);
+ protected static final UserProperties USER_PROPERTIES_0 =
+ new UserProperties.Builder().setItemsRestrictedOnHomeScreen(false).build();
+
+ protected static final UserProperties USER_PROPERTIES_10 =
+ new UserProperties.Builder().setItemsRestrictedOnHomeScreen(false).build();
+
+ protected static final UserProperties USER_PROPERTIES_11 =
+ new UserProperties.Builder().setItemsRestrictedOnHomeScreen(true).build();
+
protected BiPredicate<String, Integer> mDefaultLauncherChecker =
(callingPackage, userId) ->
LAUNCHER_1.equals(callingPackage) || LAUNCHER_2.equals(callingPackage)
@@ -817,6 +827,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
= new HashMap<>();
protected final Map<Integer, UserInfo> mUserInfos = new HashMap<>();
+ protected final Map<Integer, UserProperties> mUserProperties = new HashMap<>();
protected final Map<Integer, Boolean> mRunningUsers = new HashMap<>();
protected final Map<Integer, Boolean> mUnlockedUsers = new HashMap<>();
@@ -911,6 +922,9 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
mUserInfos.put(USER_11, USER_INFO_11);
mUserInfos.put(USER_P0, USER_INFO_P0);
mUserInfos.put(USER_P1, USER_INFO_P1);
+ mUserProperties.put(USER_0, USER_PROPERTIES_0);
+ mUserProperties.put(USER_10, USER_PROPERTIES_10);
+ mUserProperties.put(USER_11, USER_PROPERTIES_11);
when(mMockUserManagerInternal.isUserUnlockingOrUnlocked(anyInt()))
.thenAnswer(inv -> {
@@ -959,6 +973,15 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
inv -> mUserInfos.get((Integer) inv.getArguments()[0])));
when(mMockActivityManagerInternal.getUidProcessState(anyInt())).thenReturn(
ActivityManager.PROCESS_STATE_CACHED_EMPTY);
+ when(mMockUserManagerInternal.getUserProperties(anyInt()))
+ .thenAnswer(inv -> {
+ final int userId = (Integer) inv.getArguments()[0];
+ final UserProperties userProperties = mUserProperties.get(userId);
+ if (userProperties == null) {
+ return new UserProperties.Builder().build();
+ }
+ return userProperties;
+ });
// User 0 and P0 are always running
mRunningUsers.put(USER_0, true);
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index d50affba1ea1..a0f2395f5203 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -18,7 +18,6 @@ package com.android.server.pm;
import static android.content.pm.ShortcutInfo.DISABLED_REASON_BACKUP_NOT_SUPPORTED;
import static android.content.pm.ShortcutInfo.DISABLED_REASON_NOT_DISABLED;
import static android.content.pm.ShortcutInfo.DISABLED_REASON_SIGNATURE_MISMATCH;
-import static android.content.pm.ShortcutInfo.DISABLED_REASON_VERSION_LOWER;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.anyOrNull;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.anyStringOrNull;
@@ -78,7 +77,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherApps;
-import android.content.pm.LauncherApps.PinItemRequest;
import android.content.pm.LauncherApps.ShortcutQuery;
import android.content.pm.PackageInfo;
import android.content.pm.ShortcutInfo;
@@ -4844,7 +4842,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
doReturn(expected != DISABLED_REASON_SIGNATURE_MISMATCH).when(
mMockPackageManagerInternal).isDataRestoreSafe(any(byte[].class), anyString());
- assertEquals(expected, spi.canRestoreTo(mService, pi, anyVersionOk));
+ assertEquals(expected, spi.canRestoreTo(mService, pi, true));
}
public void testCanRestoreTo() {
@@ -4872,7 +4870,6 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
checkCanRestoreTo(DISABLED_REASON_SIGNATURE_MISMATCH, spi1, false, 10, true, "x");
checkCanRestoreTo(DISABLED_REASON_SIGNATURE_MISMATCH, spi1, false, 10, true, "x", "y");
checkCanRestoreTo(DISABLED_REASON_SIGNATURE_MISMATCH, spi1, false, 10, true, "x");
- checkCanRestoreTo(DISABLED_REASON_VERSION_LOWER, spi1, false, 9, true, "sig1");
// Any version okay.
checkCanRestoreTo(DISABLED_REASON_NOT_DISABLED, spi1, true, 9, true, "sig1");
@@ -5983,14 +5980,6 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testBackupAndRestore_publisherLowerVersion() {
- prepareForBackupTest();
-
- addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 0); // Lower version
-
- checkBackupAndRestore_publisherNotRestored(ShortcutInfo.DISABLED_REASON_VERSION_LOWER);
- }
-
public void testBackupAndRestore_publisherWrongSignature() {
prepareForBackupTest();
@@ -6626,252 +6615,6 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
-
- /**
- * Restored to a lower version with no manifest shortcuts. All shortcuts are now invisible,
- * and all calls from the publisher should ignore them.
- */
- public void testBackupAndRestore_disabledShortcutsAreIgnored() {
- // Publish two manifest shortcuts.
- addManifestShortcutResource(
- new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
- R.xml.shortcut_5_altalt);
- updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(mServiceContext,
- genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
-
- runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
- assertTrue(mManager.setDynamicShortcuts(list(
- makeShortcutWithShortLabel("s1", "original-title"),
- makeShortcut("s2"), makeShortcut("s3"))));
- });
-
- // Pin from launcher 1.
- runWithCaller(LAUNCHER_1, USER_0, () -> {
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
- list("ms1", "ms2", "ms3", "ms4", "s1", "s2"), HANDLE_USER_0);
- });
-
- doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(
- any(byte[].class), anyString());
-
- backupAndRestore();
-
- // Lower the version and remove the manifest shortcuts.
- addManifestShortcutResource(
- new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
- R.xml.shortcut_0);
- addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 0); // Lower version
-
- // When re-installing the app, the manifest shortcut should be re-published.
- mService.mPackageMonitor.onReceive(mServiceContext,
- genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
- mService.mPackageMonitor.onReceive(mServiceContext,
- genPackageAddIntent(LAUNCHER_1, USER_0));
-
- // No shortcuts should be visible to the publisher.
- runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
- assertWith(getCallerVisibleShortcuts())
- .isEmpty();
- });
-
- final Runnable checkAllDisabledForLauncher = () -> {
- runWithCaller(LAUNCHER_1, USER_0, () -> {
- assertWith(getShortcutAsLauncher(USER_0))
- .areAllPinned()
- .haveIds("ms1", "ms2", "ms3", "ms4", "s1", "s2")
- .areAllDisabled()
- .areAllWithDisabledReason(ShortcutInfo.DISABLED_REASON_VERSION_LOWER)
-
- .forShortcutWithId("s1", si -> {
- assertEquals("original-title", si.getShortLabel());
- })
- .forShortcutWithId("ms1", si -> {
- assertEquals("string-com.android.test.1-user:0-res:"
- + R.string.shortcut_title1 + "/en"
- , si.getShortLabel());
- })
- .forShortcutWithId("ms2", si -> {
- assertEquals("string-com.android.test.1-user:0-res:"
- + R.string.shortcut_title2 + "/en"
- , si.getShortLabel());
- })
- .forShortcutWithId("ms3", si -> {
- assertEquals("string-com.android.test.1-user:0-res:"
- + R.string.shortcut_title1 + "/en"
- , si.getShortLabel());
- assertEquals("string-com.android.test.1-user:0-res:"
- + R.string.shortcut_title2 + "/en"
- , si.getLongLabel());
- })
- .forShortcutWithId("ms4", si -> {
- assertEquals("string-com.android.test.1-user:0-res:"
- + R.string.shortcut_title2 + "/en"
- , si.getShortLabel());
- assertEquals("string-com.android.test.1-user:0-res:"
- + R.string.shortcut_title2 + "/en"
- , si.getLongLabel());
- });
- });
- };
-
- checkAllDisabledForLauncher.run();
-
- runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
-
- makeCallerForeground(); // CALLING_PACKAGE_1 is now in the foreground.
-
- // All changing API calls should be ignored.
-
- getManager().enableShortcuts(list("ms1", "ms2", "ms3", "ms4", "s1", "s2"));
- checkAllDisabledForLauncher.run();
-
- getManager().enableShortcuts(list("ms1", "ms2", "ms3", "ms4", "s1", "s2"));
- checkAllDisabledForLauncher.run();
-
- getManager().enableShortcuts(list("ms1", "ms2", "ms3", "ms4", "s1", "s2"));
- checkAllDisabledForLauncher.run();
-
- getManager().removeAllDynamicShortcuts();
- getManager().removeDynamicShortcuts(list("ms1", "ms2", "ms3", "ms4", "s1", "s2"));
- checkAllDisabledForLauncher.run();
-
- getManager().updateShortcuts(list(makeShortcutWithShortLabel("s1", "new-title")));
- checkAllDisabledForLauncher.run();
-
-
- // Add a shortcut -- even though ms1 was immutable, it will succeed.
- assertTrue(getManager().addDynamicShortcuts(list(
- makeShortcutWithShortLabel("ms1", "original-title"))));
-
- runWithCaller(LAUNCHER_1, USER_0, () -> {
- assertWith(getShortcutAsLauncher(USER_0))
- .haveIds("ms1", "ms2", "ms3", "ms4", "s1", "s2")
-
- .selectByIds("ms1")
- .areAllEnabled()
- .areAllDynamic()
- .areAllPinned()
- .forAllShortcuts(si -> {
- assertEquals("original-title", si.getShortLabel());
- })
-
- // The rest still exist and disabled.
- .revertToOriginalList()
- .selectByIds("ms2", "ms3", "ms4", "s1", "s2")
- .areAllDisabled()
- .areAllPinned()
- ;
- });
-
- assertTrue(getManager().setDynamicShortcuts(list(
- makeShortcutWithShortLabel("ms2", "new-title-2"))));
-
- runWithCaller(LAUNCHER_1, USER_0, () -> {
- assertWith(getShortcutAsLauncher(USER_0))
- .haveIds("ms1", "ms2", "ms3", "ms4", "s1", "s2")
-
- .selectByIds("ms1")
- .areAllEnabled()
- .areAllNotDynamic() // ms1 was not in the list, so no longer dynamic.
- .areAllPinned()
- .areAllMutable()
- .forAllShortcuts(si -> {
- assertEquals("original-title", si.getShortLabel());
- })
-
- .revertToOriginalList()
- .selectByIds("ms2")
- .areAllEnabled()
- .areAllDynamic()
- .areAllPinned()
- .areAllMutable()
- .forAllShortcuts(si -> {
- assertEquals("new-title-2", si.getShortLabel());
- })
-
- // The rest still exist and disabled.
- .revertToOriginalList()
- .selectByIds("ms3", "ms4", "s1", "s2")
- .areAllDisabled()
- .areAllPinned()
- ;
- });
-
- // Prepare for requestPinShortcut().
- setDefaultLauncher(USER_0, LAUNCHER_1);
- mPinConfirmActivityFetcher = (packageName, userId) ->
- new ComponentName(packageName, PIN_CONFIRM_ACTIVITY_CLASS);
-
- mManager.requestPinShortcut(
- makeShortcutWithShortLabel("ms3", "new-title-3"),
- /*PendingIntent=*/ null);
-
- // Note this was pinned, so it'll be accepted right away.
- runWithCaller(LAUNCHER_1, USER_0, () -> {
- assertWith(getShortcutAsLauncher(USER_0))
- .selectByIds("ms3")
- .areAllEnabled()
- .areAllNotDynamic()
- .areAllPinned()
- .areAllMutable()
- .forAllShortcuts(si -> {
- assertEquals("new-title-3", si.getShortLabel());
- // The new one replaces the old manifest shortcut, so the long label
- // should be gone now.
- assertNull(si.getLongLabel());
- });
- });
-
- // Now, change the launcher to launcher2, and request pin again.
- setDefaultLauncher(USER_0, LAUNCHER_2);
-
- reset(mServiceContext);
-
- assertTrue(mManager.isRequestPinShortcutSupported());
- mManager.requestPinShortcut(
- makeShortcutWithShortLabel("ms4", "new-title-4"),
- /*PendingIntent=*/ null);
-
- // Initially there should be no pinned shortcuts for L2.
- runWithCaller(LAUNCHER_2, USER_0, () -> {
- assertWith(getShortcutAsLauncher(USER_0))
- .selectPinned()
- .isEmpty();
-
- final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
-
- verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
-
- assertEquals(LauncherApps.ACTION_CONFIRM_PIN_SHORTCUT,
- intent.getValue().getAction());
- assertEquals(LAUNCHER_2, intent.getValue().getComponent().getPackageName());
-
- // Check the request object.
- final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
-
- assertNotNull(request);
- assertEquals(PinItemRequest.REQUEST_TYPE_SHORTCUT, request.getRequestType());
-
- assertWith(request.getShortcutInfo())
- .haveIds("ms4")
- .areAllOrphan()
- .forAllShortcuts(si -> {
- assertEquals("new-title-4", si.getShortLabel());
- // The new one replaces the old manifest shortcut, so the long label
- // should be gone now.
- assertNull(si.getLongLabel());
- });
- assertTrue(request.accept());
-
- assertWith(getShortcutAsLauncher(USER_0))
- .selectPinned()
- .haveIds("ms4")
- .areAllEnabled();
- });
- });
- }
-
/**
* Test for restoring the pre-P backup format.
*/
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
index f1d3ba9db489..1331ae173b18 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
@@ -197,8 +197,6 @@ public class UserManagerServiceUserPropertiesTest {
assertEqualGetterOrThrows(orig::getAlwaysVisible, copy::getAlwaysVisible, exposeAll);
assertEqualGetterOrThrows(orig::getAllowStoppingUserWithDelayedLocking,
copy::getAllowStoppingUserWithDelayedLocking, exposeAll);
- assertEqualGetterOrThrows(orig::areItemsRestrictedOnHomeScreen,
- copy::areItemsRestrictedOnHomeScreen, exposeAll);
// Items requiring hasManagePermission - put them here using hasManagePermission.
assertEqualGetterOrThrows(orig::getShowInSettings, copy::getShowInSettings,
@@ -220,6 +218,8 @@ public class UserManagerServiceUserPropertiesTest {
copy::getCrossProfileContentSharingStrategy, true);
assertEqualGetterOrThrows(orig::getProfileApiVisibility, copy::getProfileApiVisibility,
true);
+ assertEqualGetterOrThrows(orig::areItemsRestrictedOnHomeScreen,
+ copy::areItemsRestrictedOnHomeScreen, true);
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 7f7cc35ced55..44c464ed6adf 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -350,8 +350,8 @@ public final class UserManagerTest {
privateProfileUserProperties::getAllowStoppingUserWithDelayedLocking);
assertThat(typeProps.getProfileApiVisibility()).isEqualTo(
privateProfileUserProperties.getProfileApiVisibility());
- assertThrows(SecurityException.class,
- privateProfileUserProperties::areItemsRestrictedOnHomeScreen);
+ assertThat(typeProps.areItemsRestrictedOnHomeScreen())
+ .isEqualTo(privateProfileUserProperties.areItemsRestrictedOnHomeScreen());
compareDrawables(mUserManager.getUserBadge(),
Resources.getSystem().getDrawable(userTypeDetails.getBadgePlain()));
diff --git a/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java b/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java
index 8bccce102887..771a76517b22 100644
--- a/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java
+++ b/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java
@@ -16,41 +16,60 @@
package com.android.server.search;
-import android.app.SearchManager;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+
import android.app.SearchableInfo;
import android.app.SearchableInfo.ActionKeyInfo;
import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ProviderInfo;
-import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
-import android.content.res.XmlResourceParser;
-import android.os.RemoteException;
-import com.android.server.search.Searchables;
-import android.test.AndroidTestCase;
+import android.content.pm.PackageManagerInternal;
import android.test.MoreAsserts;
-import android.test.mock.MockContext;
-import android.test.mock.MockPackageManager;
-import android.test.suitebuilder.annotation.SmallTest;
import android.view.KeyEvent;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
import java.util.ArrayList;
-import java.util.List;
-/**
- * To launch this test from the command line:
- *
- * adb shell am instrument -w \
- * -e class com.android.unit_tests.SearchablesTest \
- * com.android.unit_tests/android.test.InstrumentationTestRunner
- */
+@RunWith(AndroidJUnit4.class)
@SmallTest
-public class SearchablesTest extends AndroidTestCase {
-
+public class SearchablesTest {
+ @Mock protected PackageManagerInternal mPackageManagerInternal;
+
+ private Context mContext;
+
+ @Before
+ public final void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
+
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ }
+
+ @After
+ public final void tearDown() {
+ Mockito.framework().clearInlineMocks();
+ }
+
/*
* SearchableInfo tests
* Mock the context so I can provide very specific input data
@@ -69,15 +88,15 @@ public class SearchablesTest extends AndroidTestCase {
* Test that non-searchable activities return no searchable info (this would typically
* trigger the use of the default searchable e.g. contacts)
*/
+ @Test
public void testNonSearchable() {
// test basic array & hashmap
Searchables searchables = new Searchables(mContext, 0);
- searchables.updateSearchableList();
+ searchables.updateSearchableListIfNeeded();
// confirm that we return null for non-searchy activities
- ComponentName nonActivity = new ComponentName(
- "com.android.frameworks.coretests",
- "com.android.frameworks.coretests.activity.NO_SEARCH_ACTIVITY");
+ ComponentName nonActivity = new ComponentName("com.android.frameworks.servicestests",
+ "com.android.frameworks.servicestests.activity.NO_SEARCH_ACTIVITY");
SearchableInfo si = searchables.getSearchableInfo(nonActivity);
assertNull(si);
}
@@ -97,14 +116,12 @@ public class SearchablesTest extends AndroidTestCase {
* getIcon works
*/
+ @Test
public void testSearchablesListReal() {
- MyMockPackageManager mockPM = new MyMockPackageManager(mContext.getPackageManager());
- MyMockContext mockContext = new MyMockContext(mContext, mockPM);
+ doReturn(true).when(mPackageManagerInternal).canAccessComponent(anyInt(), any(), anyInt());
- // build item list with real-world source data
- mockPM.setSearchablesMode(MyMockPackageManager.SEARCHABLES_PASSTHROUGH);
- Searchables searchables = new Searchables(mockContext, 0);
- searchables.updateSearchableList();
+ Searchables searchables = new Searchables(mContext, 0);
+ searchables.updateSearchableListIfNeeded();
// tests with "real" searchables (deprecate, this should be a unit test)
ArrayList<SearchableInfo> searchablesList = searchables.getSearchablesList();
int count = searchablesList.size();
@@ -117,13 +134,12 @@ public class SearchablesTest extends AndroidTestCase {
/**
* This round of tests confirms good operations with "zero" searchables found
*/
+ @Test
public void testSearchablesListEmpty() {
- MyMockPackageManager mockPM = new MyMockPackageManager(mContext.getPackageManager());
- MyMockContext mockContext = new MyMockContext(mContext, mockPM);
+ doReturn(false).when(mPackageManagerInternal).canAccessComponent(anyInt(), any(), anyInt());
- mockPM.setSearchablesMode(MyMockPackageManager.SEARCHABLES_MOCK_ZERO);
- Searchables searchables = new Searchables(mockContext, 0);
- searchables.updateSearchableList();
+ Searchables searchables = new Searchables(mContext, 0);
+ searchables.updateSearchableListIfNeeded();
ArrayList<SearchableInfo> searchablesList = searchables.getSearchablesList();
assertNotNull(searchablesList);
MoreAsserts.assertEmpty(searchablesList);
@@ -219,231 +235,6 @@ public class SearchablesTest extends AndroidTestCase {
if (s != null) {
MoreAsserts.assertNotEqual(s, "");
}
- }
-
- /**
- * This is a mock for context. Used to perform a true unit test on SearchableInfo.
- *
- */
- private class MyMockContext extends MockContext {
-
- protected Context mRealContext;
- protected PackageManager mPackageManager;
-
- /**
- * Constructor.
- *
- * @param realContext Please pass in a real context for some pass-throughs to function.
- */
- MyMockContext(Context realContext, PackageManager packageManager) {
- mRealContext = realContext;
- mPackageManager = packageManager;
- }
-
- /**
- * Resources. Pass through for now.
- */
- @Override
- public Resources getResources() {
- return mRealContext.getResources();
- }
-
- /**
- * Package manager. Pass through for now.
- */
- @Override
- public PackageManager getPackageManager() {
- return mPackageManager;
- }
-
- /**
- * Package manager. Pass through for now.
- */
- @Override
- public Context createPackageContext(String packageName, int flags)
- throws PackageManager.NameNotFoundException {
- return mRealContext.createPackageContext(packageName, flags);
- }
-
- /**
- * Message broadcast. Pass through for now.
- */
- @Override
- public void sendBroadcast(Intent intent) {
- mRealContext.sendBroadcast(intent);
- }
- }
-
-/**
- * This is a mock for package manager. Used to perform a true unit test on SearchableInfo.
- *
- */
- private class MyMockPackageManager extends MockPackageManager {
-
- public final static int SEARCHABLES_PASSTHROUGH = 0;
- public final static int SEARCHABLES_MOCK_ZERO = 1;
- public final static int SEARCHABLES_MOCK_ONEGOOD = 2;
- public final static int SEARCHABLES_MOCK_ONEGOOD_ONEBAD = 3;
-
- protected PackageManager mRealPackageManager;
- protected int mSearchablesMode;
-
- public MyMockPackageManager(PackageManager realPM) {
- mRealPackageManager = realPM;
- mSearchablesMode = SEARCHABLES_PASSTHROUGH;
- }
-
- /**
- * Set the mode for various tests.
- */
- public void setSearchablesMode(int newMode) {
- switch (newMode) {
- case SEARCHABLES_PASSTHROUGH:
- case SEARCHABLES_MOCK_ZERO:
- mSearchablesMode = newMode;
- break;
-
- default:
- throw new UnsupportedOperationException();
- }
- }
-
- /**
- * Find activities that support a given intent.
- *
- * Retrieve all activities that can be performed for the given intent.
- *
- * @param intent The desired intent as per resolveActivity().
- * @param flags Additional option flags. The most important is
- * MATCH_DEFAULT_ONLY, to limit the resolution to only
- * those activities that support the CATEGORY_DEFAULT.
- *
- * @return A List<ResolveInfo> containing one entry for each matching
- * Activity. These are ordered from best to worst match -- that
- * is, the first item in the list is what is returned by
- * resolveActivity(). If there are no matching activities, an empty
- * list is returned.
- */
- @Override
- public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {
- assertNotNull(intent);
- assertTrue(intent.getAction().equals(Intent.ACTION_SEARCH)
- || intent.getAction().equals(Intent.ACTION_WEB_SEARCH)
- || intent.getAction().equals(SearchManager.INTENT_ACTION_GLOBAL_SEARCH));
- switch (mSearchablesMode) {
- case SEARCHABLES_PASSTHROUGH:
- return mRealPackageManager.queryIntentActivities(intent, flags);
- case SEARCHABLES_MOCK_ZERO:
- return null;
- default:
- throw new UnsupportedOperationException();
- }
- }
-
- @Override
- public ResolveInfo resolveActivity(Intent intent, int flags) {
- assertNotNull(intent);
- assertTrue(intent.getAction().equals(Intent.ACTION_WEB_SEARCH)
- || intent.getAction().equals(SearchManager.INTENT_ACTION_GLOBAL_SEARCH));
- switch (mSearchablesMode) {
- case SEARCHABLES_PASSTHROUGH:
- return mRealPackageManager.resolveActivity(intent, flags);
- case SEARCHABLES_MOCK_ZERO:
- return null;
- default:
- throw new UnsupportedOperationException();
- }
- }
-
- /**
- * Retrieve an XML file from a package. This is a low-level API used to
- * retrieve XML meta data.
- *
- * @param packageName The name of the package that this xml is coming from.
- * Can not be null.
- * @param resid The resource identifier of the desired xml. Can not be 0.
- * @param appInfo Overall information about <var>packageName</var>. This
- * may be null, in which case the application information will be retrieved
- * for you if needed; if you already have this information around, it can
- * be much more efficient to supply it here.
- *
- * @return Returns an TypedXmlPullParser allowing you to parse out the XML
- * data. Returns null if the xml resource could not be found for any
- * reason.
- */
- @Override
- public XmlResourceParser getXml(String packageName, int resid, ApplicationInfo appInfo) {
- assertNotNull(packageName);
- MoreAsserts.assertNotEqual(packageName, "");
- MoreAsserts.assertNotEqual(resid, 0);
- switch (mSearchablesMode) {
- case SEARCHABLES_PASSTHROUGH:
- return mRealPackageManager.getXml(packageName, resid, appInfo);
- case SEARCHABLES_MOCK_ZERO:
- default:
- throw new UnsupportedOperationException();
- }
- }
-
- /**
- * Find a single content provider by its base path name.
- *
- * @param name The name of the provider to find.
- * @param flags Additional option flags. Currently should always be 0.
- *
- * @return ContentProviderInfo Information about the provider, if found,
- * else null.
- */
- @Override
- public ProviderInfo resolveContentProvider(String name, int flags) {
- assertNotNull(name);
- MoreAsserts.assertNotEqual(name, "");
- assertEquals(flags, 0);
- switch (mSearchablesMode) {
- case SEARCHABLES_PASSTHROUGH:
- return mRealPackageManager.resolveContentProvider(name, flags);
- case SEARCHABLES_MOCK_ZERO:
- default:
- throw new UnsupportedOperationException();
- }
- }
-
- /**
- * Get the activity information for a particular activity.
- *
- * @param name The name of the activity to find.
- * @param flags Additional option flags.
- *
- * @return ActivityInfo Information about the activity, if found, else null.
- */
- @Override
- public ActivityInfo getActivityInfo(ComponentName name, int flags)
- throws NameNotFoundException {
- assertNotNull(name);
- MoreAsserts.assertNotEqual(name, "");
- switch (mSearchablesMode) {
- case SEARCHABLES_PASSTHROUGH:
- return mRealPackageManager.getActivityInfo(name, flags);
- case SEARCHABLES_MOCK_ZERO:
- throw new NameNotFoundException();
- default:
- throw new UnsupportedOperationException();
- }
- }
-
- @Override
- public int checkPermission(String permName, String pkgName) {
- assertNotNull(permName);
- assertNotNull(pkgName);
- switch (mSearchablesMode) {
- case SEARCHABLES_PASSTHROUGH:
- return mRealPackageManager.checkPermission(permName, pkgName);
- case SEARCHABLES_MOCK_ZERO:
- return PackageManager.PERMISSION_DENIED;
- default:
- throw new UnsupportedOperationException();
- }
- }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index eddff9abec21..3bc089fb3f5d 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -602,56 +602,6 @@ public class SystemConfigTest {
}
/**
- * Test that getRollbackDenylistedPackages works correctly for the tag:
- * {@code automatic-rollback-denylisted-app}.
- */
- @Test
- public void automaticRollbackDeny_vending() throws IOException {
- final String contents =
- "<config>\n"
- + " <automatic-rollback-denylisted-app package=\"com.android.vending\" />\n"
- + "</config>";
- final File folder = createTempSubfolder("folder");
- createTempFile(folder, "automatic-rollback-denylisted-app.xml", contents);
-
- readPermissions(folder, /* Grant all permission flags */ ~0);
-
- assertThat(mSysConfig.getAutomaticRollbackDenylistedPackages())
- .containsExactly("com.android.vending");
- }
-
- /**
- * Test that getRollbackDenylistedPackages works correctly for the tag:
- * {@code automatic-rollback-denylisted-app} without any packages.
- */
- @Test
- public void automaticRollbackDeny_empty() throws IOException {
- final String contents =
- "<config>\n"
- + " <automatic-rollback-denylisted-app />\n"
- + "</config>";
- final File folder = createTempSubfolder("folder");
- createTempFile(folder, "automatic-rollback-denylisted-app.xml", contents);
-
- readPermissions(folder, /* Grant all permission flags */ ~0);
-
- assertThat(mSysConfig.getAutomaticRollbackDenylistedPackages()).isEmpty();
- }
-
- /**
- * Test that getRollbackDenylistedPackages works correctly for the tag:
- * {@code automatic-rollback-denylisted-app} without the corresponding config.
- */
- @Test
- public void automaticRollbackDeny_noConfig() throws IOException {
- final File folder = createTempSubfolder("folder");
-
- readPermissions(folder, /* Grant all permission flags */ ~0);
-
- assertThat(mSysConfig.getAutomaticRollbackDenylistedPackages()).isEmpty();
- }
-
- /**
* Tests that readPermissions works correctly for the tag: {@code update-ownership}.
*/
@Test
@@ -712,7 +662,7 @@ public class SystemConfigTest {
android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
public void getEnhancedConfirmationTrustedInstallers_returnsTrustedInstallers()
throws IOException {
- String pkgName = "com.example.app";
+ String packageName = "com.example.app";
String certificateDigestStr = "E9:7A:BC:2C:D1:CA:8D:58:6A:57:0B:8C:F8:60:AA:D2:"
+ "8D:13:30:2A:FB:C9:00:2C:5D:53:B2:6C:09:A4:85:A0";
@@ -720,7 +670,7 @@ public class SystemConfigTest {
.toByteArray();
String contents = "<config>"
+ "<" + "enhanced-confirmation-trusted-installer" + " "
- + "package=\"" + pkgName + "\""
+ + "package=\"" + packageName + "\""
+ " sha256-cert-digest=\"" + certificateDigestStr + "\""
+ "/>"
+ "</config>";
@@ -734,10 +684,10 @@ public class SystemConfigTest {
assertThat(actualTrustedInstallers.size()).isEqualTo(1);
SignedPackage actual = actualTrustedInstallers.stream().findFirst().orElseThrow();
- SignedPackage expected = new SignedPackage(pkgName, certificateDigest);
+ SignedPackage expected = new SignedPackage(packageName, certificateDigest);
assertThat(actual.getCertificateDigest()).isEqualTo(expected.getCertificateDigest());
- assertThat(actual.getPkgName()).isEqualTo(expected.getPkgName());
+ assertThat(actual.getPackageName()).isEqualTo(expected.getPackageName());
assertThat(actual).isEqualTo(expected);
}
diff --git a/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java b/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java
index 6c085e085f4e..c8bef45af839 100644
--- a/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java
+++ b/services/tests/servicestests/src/com/android/server/utils/AnrTimerTest.java
@@ -16,9 +16,7 @@
package com.android.server.utils;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
import android.os.Handler;
import android.os.Looper;
@@ -110,7 +108,7 @@ public class AnrTimerTest {
*/
TestArg[] messages(int expected) {
synchronized (mLock) {
- assertEquals(expected, mMessages.size());
+ assertThat(mMessages.size()).isEqualTo(expected);
return mMessages.toArray(new TestArg[expected]);
}
}
@@ -154,8 +152,8 @@ public class AnrTimerTest {
}
void validate(TestArg expected, TestArg actual) {
- assertEquals(expected, actual);
- assertEquals(actual.what, MSG_TIMEOUT);
+ assertThat(actual).isEqualTo(expected);
+ assertThat(actual.what).isEqualTo(MSG_TIMEOUT);
}
@Parameters(name = "featureEnabled={0}")
@@ -180,11 +178,11 @@ public class AnrTimerTest {
Helper helper = new Helper(1);
try (TestAnrTimer timer = new TestAnrTimer(helper)) {
// One-time check that the injector is working as expected.
- assertEquals(mEnabled, timer.serviceEnabled());
+ assertThat(mEnabled).isEqualTo(timer.serviceEnabled());
TestArg t = new TestArg(1, 1);
timer.start(t, 10);
// Delivery is immediate but occurs on a different thread.
- assertTrue(helper.await(5000));
+ assertThat(helper.await(5000)).isTrue();
TestArg[] result = helper.messages(1);
validate(t, result[0]);
}
@@ -201,10 +199,10 @@ public class AnrTimerTest {
TestArg t = new TestArg(1, 1);
timer.start(t, 10000);
// Briefly pause.
- assertFalse(helper.await(10));
+ assertThat(helper.await(10)).isFalse();
timer.start(t, 10);
// Delivery is immediate but occurs on a different thread.
- assertTrue(helper.await(5000));
+ assertThat(helper.await(5000)).isTrue();
TestArg[] result = helper.messages(1);
validate(t, result[0]);
}
@@ -221,7 +219,7 @@ public class AnrTimerTest {
TestArg t = new TestArg(1, 1);
timer.start(t, 0);
// Delivery is immediate but occurs on a different thread.
- assertTrue(helper.await(5000));
+ assertThat(helper.await(5000)).isTrue();
TestArg[] result = helper.messages(1);
validate(t, result[0]);
}
@@ -243,7 +241,7 @@ public class AnrTimerTest {
timer.start(t2, 60);
timer.start(t3, 40);
// Delivery is immediate but occurs on a different thread.
- assertTrue(helper.await(5000));
+ assertThat(helper.await(5000)).isTrue();
TestArg[] result = helper.messages(3);
validate(t3, result[0]);
validate(t1, result[1]);
@@ -269,7 +267,7 @@ public class AnrTimerTest {
x2.start(t2, 60);
x3.start(t3, 40);
// Delivery is immediate but occurs on a different thread.
- assertTrue(helper.await(5000));
+ assertThat(helper.await(5000)).isTrue();
TestArg[] result = helper.messages(3);
validate(t3, result[0]);
validate(t1, result[1]);
@@ -292,10 +290,10 @@ public class AnrTimerTest {
timer.start(t2, 60);
timer.start(t3, 40);
// Briefly pause.
- assertFalse(helper.await(10));
+ assertThat(helper.await(10)).isFalse();
timer.cancel(t1);
// Delivery is immediate but occurs on a different thread.
- assertTrue(helper.await(5000));
+ assertThat(helper.await(5000)).isTrue();
TestArg[] result = helper.messages(2);
validate(t3, result[0]);
validate(t2, result[1]);
@@ -319,7 +317,7 @@ public class AnrTimerTest {
@Test
public void testDumpOutput() throws Exception {
String r1 = getDumpOutput();
- assertEquals(false, r1.contains("timer:"));
+ assertThat(r1).doesNotContain("timer:");
Helper helper = new Helper(2);
TestArg t1 = new TestArg(1, 1);
@@ -332,12 +330,15 @@ public class AnrTimerTest {
String r2 = getDumpOutput();
// There are timers in the list if and only if the feature is enabled.
- final boolean expected = mEnabled;
- assertEquals(expected, r2.contains("timer:"));
+ if (mEnabled) {
+ assertThat(r2).contains("timer:");
+ } else {
+ assertThat(r2).doesNotContain("timer:");
+ }
}
String r3 = getDumpOutput();
- assertEquals(false, r3.contains("timer:"));
+ assertThat(r3).doesNotContain("timer:");
}
/**
@@ -351,7 +352,7 @@ public class AnrTimerTest {
if (!mEnabled) return;
String r1 = getDumpOutput();
- assertEquals(false, r1.contains("timer:"));
+ assertThat(r1).doesNotContain("timer:");
Helper helper = new Helper(2);
TestArg t1 = new TestArg(1, 1);
@@ -366,8 +367,11 @@ public class AnrTimerTest {
String r2 = getDumpOutput();
// There are timers in the list if and only if the feature is enabled.
- final boolean expected = mEnabled;
- assertEquals(expected, r2.contains("timer:"));
+ if (mEnabled) {
+ assertThat(r2).contains("timer:");
+ } else {
+ assertThat(r2).doesNotContain("timer:");
+ }
}
// Try to make finalizers run. The timer object above should be a candidate. Finalizers
@@ -382,7 +386,7 @@ public class AnrTimerTest {
}
// The timer was not explicitly closed but it should have been implicitly closed by GC.
- assertEquals(false, r3.contains("timer:"));
+ assertThat(r3).doesNotContain("timer:");
}
// TODO: [b/302724778] Remove manual JNI load
diff --git a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
index ae0a758449b5..2039f93b9c40 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
@@ -17,6 +17,7 @@
package com.android.server.webkit;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
@@ -82,6 +83,13 @@ public class TestSystemImpl implements SystemInterface {
}
}
+ @Override
+ public void installExistingPackageForAllUsers(Context context, String packageName) {
+ for (int userId : mUsers) {
+ installPackageForUser(packageName, userId);
+ }
+ }
+
private void enablePackageForUser(String packageName, boolean enable, int userId) {
Map<Integer, PackageInfo> userPackages = mPackages.get(packageName);
if (userPackages == null) {
@@ -92,6 +100,17 @@ public class TestSystemImpl implements SystemInterface {
setPackageInfoForUser(userId, packageInfo);
}
+ private void installPackageForUser(String packageName, int userId) {
+ Map<Integer, PackageInfo> userPackages = mPackages.get(packageName);
+ if (userPackages == null) {
+ return;
+ }
+ PackageInfo packageInfo = userPackages.get(userId);
+ packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED;
+ packageInfo.applicationInfo.privateFlags &= (~ApplicationInfo.PRIVATE_FLAG_HIDDEN);
+ setPackageInfoForUser(userId, packageInfo);
+ }
+
@Override
public boolean systemIsDebuggable() { return mIsDebuggable; }
@@ -179,4 +198,7 @@ public class TestSystemImpl implements SystemInterface {
public boolean isMultiProcessDefaultEnabled() {
return mMultiProcessDefault;
}
+
+ @Override
+ public void pinWebviewIfRequired(ApplicationInfo appInfo) {}
}
diff --git a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
index 5a06327fdde3..53c172a191b6 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
@@ -1549,6 +1549,31 @@ public class WebViewUpdateServiceTest {
Matchers.anyObject(), Mockito.eq(testPackage), Mockito.eq(true));
}
+ @Test
+ @RequiresFlagsEnabled("android.webkit.update_service_v2")
+ public void testDefaultWebViewPackageInstalling() {
+ String testPackage = "testDefault";
+ WebViewProviderInfo[] packages =
+ new WebViewProviderInfo[] {
+ new WebViewProviderInfo(
+ testPackage,
+ "",
+ true /* default available */,
+ false /* fallback */,
+ null)
+ };
+ setupWithPackages(packages);
+ mTestSystemImpl.setPackageInfo(
+ createPackageInfo(
+ testPackage, true /* enabled */, true /* valid */, false /* installed */));
+
+ // Check that the boot time logic tries to install the default package.
+ runWebViewBootPreparationOnMainSync();
+ Mockito.verify(mTestSystemImpl)
+ .installExistingPackageForAllUsers(
+ Matchers.anyObject(), Mockito.eq(testPackage));
+ }
+
private void testDefaultPackageChosen(PackageInfo packageInfo) {
WebViewProviderInfo[] packages =
new WebViewProviderInfo[] {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
index 5eb76e352ea2..f0779144ad18 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
@@ -22,6 +22,9 @@ import static android.app.Notification.FLAG_CAN_COLORIZE;
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
import static android.app.Notification.FLAG_NO_CLEAR;
import static android.app.Notification.FLAG_ONGOING_EVENT;
+import static android.app.Notification.VISIBILITY_PRIVATE;
+import static android.app.Notification.VISIBILITY_PUBLIC;
+import static android.app.Notification.VISIBILITY_SECRET;
import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
import static com.android.server.notification.GroupHelper.BASE_FLAGS;
@@ -81,6 +84,8 @@ public class GroupHelperTest extends UiServiceTestCase {
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+ private final int DEFAULT_VISIBILITY = VISIBILITY_PRIVATE;
+
private @Mock GroupHelper.Callback mCallback;
private @Mock PackageManager mPackageManager;
@@ -127,7 +132,7 @@ public class GroupHelperTest extends UiServiceTestCase {
}
private NotificationAttributes getNotificationAttributes(int flags) {
- return new NotificationAttributes(flags, mSmallIcon, COLOR_DEFAULT);
+ return new NotificationAttributes(flags, mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY);
}
@Test
@@ -704,7 +709,8 @@ public class GroupHelperTest extends UiServiceTestCase {
final Icon icon = mock(Icon.class);
when(icon.sameAs(icon)).thenReturn(true);
final int iconColor = Color.BLUE;
- final NotificationAttributes attr = new NotificationAttributes(BASE_FLAGS, icon, iconColor);
+ final NotificationAttributes attr = new NotificationAttributes(BASE_FLAGS, icon, iconColor,
+ DEFAULT_VISIBILITY);
// Add notifications with same icon and color
for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
@@ -744,7 +750,7 @@ public class GroupHelperTest extends UiServiceTestCase {
doReturn(monochromeIcon).when(groupHelper).getMonochromeAppIcon(eq(pkg));
final NotificationAttributes initialAttr = new NotificationAttributes(BASE_FLAGS,
- initialIcon, initialIconColor);
+ initialIcon, initialIconColor, DEFAULT_VISIBILITY);
// Add notifications with same icon and color
for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
@@ -769,7 +775,42 @@ public class GroupHelperTest extends UiServiceTestCase {
// Summary should be updated to the default color and the icon to the monochrome icon
NotificationAttributes newAttr = new NotificationAttributes(BASE_FLAGS, monochromeIcon,
- COLOR_DEFAULT);
+ COLOR_DEFAULT, DEFAULT_VISIBILITY);
+ verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(), eq(newAttr));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE)
+ public void testAddSummary_diffVisibility() {
+ final String pkg = "package";
+ final Icon icon = mock(Icon.class);
+ when(icon.sameAs(icon)).thenReturn(true);
+ final int iconColor = Color.BLUE;
+ final NotificationAttributes attr = new NotificationAttributes(BASE_FLAGS, icon, iconColor,
+ VISIBILITY_PRIVATE);
+
+ // Add notifications with same icon and color and default visibility (private)
+ for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
+ StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM, null,
+ icon, iconColor);
+ mGroupHelper.onNotificationPosted(sbn, false);
+ }
+ // Check that the summary has private visibility
+ verify(mCallback, times(1)).addAutoGroupSummary(
+ anyInt(), eq(pkg), anyString(), eq(attr));
+ verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
+
+ // After auto-grouping, add new notification with public visibility
+ StatusBarNotification sbn = getSbn(pkg, AUTOGROUP_AT_COUNT,
+ String.valueOf(AUTOGROUP_AT_COUNT), UserHandle.SYSTEM, null, icon, iconColor);
+ sbn.getNotification().visibility = VISIBILITY_PUBLIC;
+ mGroupHelper.onNotificationPosted(sbn, true);
+
+ // Check that the summary visibility was updated
+ NotificationAttributes newAttr = new NotificationAttributes(BASE_FLAGS, icon, iconColor,
+ VISIBILITY_PUBLIC);
verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(), eq(newAttr));
}
@@ -781,7 +822,7 @@ public class GroupHelperTest extends UiServiceTestCase {
when(initialIcon.sameAs(initialIcon)).thenReturn(true);
final int initialIconColor = Color.BLUE;
final NotificationAttributes initialAttr = new NotificationAttributes(
- GroupHelper.FLAG_INVALID, initialIcon, initialIconColor);
+ GroupHelper.FLAG_INVALID, initialIcon, initialIconColor, DEFAULT_VISIBILITY);
// Add AUTOGROUP_AT_COUNT-1 notifications with same icon and color
ArrayList<StatusBarNotification> notifications = new ArrayList<>();
@@ -817,11 +858,12 @@ public class GroupHelperTest extends UiServiceTestCase {
// Create notifications with the same icon
List<NotificationAttributes> childrenAttr = new ArrayList<>();
for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
- childrenAttr.add(new NotificationAttributes(0, icon, COLOR_DEFAULT));
+ childrenAttr.add(new NotificationAttributes(0, icon, COLOR_DEFAULT,
+ DEFAULT_VISIBILITY));
}
//Check that the generated summary icon is the same as the child notifications'
- Icon summaryIcon = mGroupHelper.getAutobundledSummaryIconAndColor(pkg, childrenAttr).icon;
+ Icon summaryIcon = mGroupHelper.getAutobundledSummaryAttributes(pkg, childrenAttr).icon;
assertThat(summaryIcon).isEqualTo(icon);
}
@@ -837,11 +879,12 @@ public class GroupHelperTest extends UiServiceTestCase {
// Create notifications with different icons
List<NotificationAttributes> childrenAttr = new ArrayList<>();
for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
- childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), COLOR_DEFAULT));
+ childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), COLOR_DEFAULT,
+ DEFAULT_VISIBILITY));
}
// Check that the generated summary icon is the monochrome icon
- Icon summaryIcon = groupHelper.getAutobundledSummaryIconAndColor(pkg, childrenAttr).icon;
+ Icon summaryIcon = groupHelper.getAutobundledSummaryAttributes(pkg, childrenAttr).icon;
assertThat(summaryIcon).isEqualTo(monochromeIcon);
}
@@ -853,11 +896,12 @@ public class GroupHelperTest extends UiServiceTestCase {
// Create notifications with the same icon color
List<NotificationAttributes> childrenAttr = new ArrayList<>();
for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
- childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), iconColor));
+ childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), iconColor,
+ DEFAULT_VISIBILITY));
}
// Check that the generated summary icon color is the same as the child notifications'
- int summaryIconColor = mGroupHelper.getAutobundledSummaryIconAndColor(pkg,
+ int summaryIconColor = mGroupHelper.getAutobundledSummaryAttributes(pkg,
childrenAttr).iconColor;
assertThat(summaryIconColor).isEqualTo(iconColor);
}
@@ -869,17 +913,62 @@ public class GroupHelperTest extends UiServiceTestCase {
// Create notifications with different icon colors
List<NotificationAttributes> childrenAttr = new ArrayList<>();
for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
- childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), i));
+ childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), i,
+ DEFAULT_VISIBILITY));
}
// Check that the generated summary icon color is the default color
- int summaryIconColor = mGroupHelper.getAutobundledSummaryIconAndColor(pkg,
+ int summaryIconColor = mGroupHelper.getAutobundledSummaryAttributes(pkg,
childrenAttr).iconColor;
assertThat(summaryIconColor).isEqualTo(Notification.COLOR_DEFAULT);
}
@Test
@EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE)
+ public void testAutobundledSummaryVisibility_hasPublicChildren() {
+ final String pkg = "package";
+ final int iconColor = Color.BLUE;
+ // Create notifications with private and public visibility
+ List<NotificationAttributes> childrenAttr = new ArrayList<>();
+ childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), iconColor,
+ VISIBILITY_PUBLIC));
+ for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
+ childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), iconColor,
+ VISIBILITY_PRIVATE));
+ }
+
+ // Check that the generated summary visibility is public
+ int summaryVisibility = mGroupHelper.getAutobundledSummaryAttributes(pkg,
+ childrenAttr).visibility;
+ assertThat(summaryVisibility).isEqualTo(VISIBILITY_PUBLIC);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE)
+ public void testAutobundledSummaryVisibility_noPublicChildren() {
+ final String pkg = "package";
+ final int iconColor = Color.BLUE;
+ int visibility = VISIBILITY_PRIVATE;
+ // Create notifications with either private or secret visibility
+ List<NotificationAttributes> childrenAttr = new ArrayList<>();
+ for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
+ if (i % 2 == 0) {
+ visibility = VISIBILITY_PRIVATE;
+ } else {
+ visibility = VISIBILITY_SECRET;
+ }
+ childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), iconColor,
+ visibility));
+ }
+
+ // Check that the generated summary visibility is private
+ int summaryVisibility = mGroupHelper.getAutobundledSummaryAttributes(pkg,
+ childrenAttr).visibility;
+ assertThat(summaryVisibility).isEqualTo(VISIBILITY_PRIVATE);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE)
public void testMonochromeAppIcon_adaptiveIconExists() throws Exception {
final String pkg = "testPackage";
final int monochromeIconResId = 1234;
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 6aacfd706adc..715c9d4081b2 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -37,6 +37,7 @@ import static android.app.Notification.FLAG_NO_CLEAR;
import static android.app.Notification.FLAG_ONGOING_EVENT;
import static android.app.Notification.FLAG_ONLY_ALERT_ONCE;
import static android.app.Notification.FLAG_USER_INITIATED_JOB;
+import static android.app.Notification.VISIBILITY_PRIVATE;
import static android.app.NotificationChannel.USER_LOCKED_ALLOW_BUBBLE;
import static android.app.NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
@@ -594,6 +595,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
.thenReturn(INVALID_TASK_ID);
mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class));
when(mUm.getProfileIds(eq(mUserId), eq(false))).thenReturn(new int[] { mUserId });
+ when(mAmi.getCurrentUserId()).thenReturn(mUserId);
when(mPackageManagerClient.hasSystemFeature(FEATURE_TELECOM)).thenReturn(true);
@@ -2338,7 +2340,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mService.updateAutobundledSummaryLocked(0, "pkg",
new NotificationAttributes(GroupHelper.BASE_FLAGS | FLAG_ONGOING_EVENT,
- mock(Icon.class), 0), false);
+ mock(Icon.class), 0, VISIBILITY_PRIVATE), false);
waitForIdle();
assertTrue(summary.getSbn().isOngoing());
@@ -2357,7 +2359,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mService.updateAutobundledSummaryLocked(0, "pkg",
new NotificationAttributes(GroupHelper.BASE_FLAGS,
- mock(Icon.class), 0), false);
+ mock(Icon.class), 0, VISIBILITY_PRIVATE), false);
waitForIdle();
assertFalse(summary.getSbn().isOngoing());
@@ -3479,7 +3481,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
when(mPermissionHelper.isPermissionFixed(PKG, temp.getUserId())).thenReturn(true);
NotificationRecord r = mService.createAutoGroupSummary(temp.getUserId(),
- temp.getSbn().getPackageName(), temp.getKey(), 0, mock(Icon.class), 0);
+ temp.getSbn().getPackageName(), temp.getKey(), 0, mock(Icon.class), 0,
+ VISIBILITY_PRIVATE);
assertThat(r.isImportanceFixed()).isTrue();
}
@@ -5839,6 +5842,30 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void testStats_DirectReplyLifetimeExtendedPostsUpdate() throws Exception {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
+ final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ r.getSbn().getNotification().flags |= FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
+ mService.addNotification(r);
+
+ mService.mNotificationDelegate.onNotificationDirectReplied(r.getKey());
+ waitForIdle();
+
+ assertThat(mService.getNotificationRecord(r.getKey()).getStats().hasDirectReplied())
+ .isTrue();
+ // Checks that a post update is sent.
+ verify(mWorkerHandler, times(1))
+ .post(any(NotificationManagerService.PostNotificationRunnable.class));
+ ArgumentCaptor<NotificationRecord> captor =
+ ArgumentCaptor.forClass(NotificationRecord.class);
+ verify(mListeners, times(1)).prepareNotifyPostedLocked(captor.capture(), any(),
+ anyBoolean());
+ assertThat(captor.getValue().getNotification().flags
+ & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY).isEqualTo(
+ FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY);
+ }
+
+ @Test
public void testStats_updatedOnUserExpansion() throws Exception {
NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
mService.addNotification(r);
@@ -8504,6 +8531,36 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ public void testStats_SmartReplyAlreadyLifetimeExtendedPostsUpdate() throws Exception {
+ mSetFlagsRule.enableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR);
+ final int replyIndex = 2;
+ final String reply = "Hello";
+ final boolean modifiedBeforeSending = true;
+ final boolean generatedByAssistant = true;
+
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ r.getSbn().getNotification().flags |= FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
+ r.setSuggestionsGeneratedByAssistant(generatedByAssistant);
+ mService.addNotification(r);
+
+ mService.mNotificationDelegate.onNotificationSmartReplySent(
+ r.getKey(), replyIndex, reply, NOTIFICATION_LOCATION_UNKNOWN,
+ modifiedBeforeSending);
+ waitForIdle();
+
+ // Checks that a post update is sent.
+ verify(mWorkerHandler, times(1))
+ .post(any(NotificationManagerService.PostNotificationRunnable.class));
+ ArgumentCaptor<NotificationRecord> captor =
+ ArgumentCaptor.forClass(NotificationRecord.class);
+ verify(mListeners, times(1)).prepareNotifyPostedLocked(captor.capture(), any(),
+ anyBoolean());
+ assertThat(captor.getValue().getNotification().flags
+ & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY).isEqualTo(
+ FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY);
+ }
+
+ @Test
public void testOnNotificationActionClick() {
final int actionIndex = 2;
final Notification.Action action =
@@ -8534,6 +8591,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
final Notification.Action action =
new Notification.Action.Builder(null, "text", PendingIntent.getActivity(
mContext, 0, new Intent(), PendingIntent.FLAG_IMMUTABLE)).build();
+ final boolean generatedByAssistant = false;
+
// Creates a notification marked as being lifetime extended.
NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
r.getSbn().getNotification().flags |= FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
@@ -8547,6 +8606,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// The flag is removed, so the notification is no longer lifetime extended.
assertThat(r.getSbn().getNotification().flags
& FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY).isEqualTo(0);
+
+ // The record is sent out without the flag.
+ ArgumentCaptor<NotificationRecord> captor =
+ ArgumentCaptor.forClass(NotificationRecord.class);
+ verify(mAssistants, times(1)).notifyAssistantActionClicked(
+ captor.capture(), eq(action), eq(generatedByAssistant));
+ assertThat(captor.getValue().getNotification().flags
+ & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY).isEqualTo(0);
}
@Test
@@ -11941,7 +12008,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// style + self managed call - bypasses block
when(mTelecomManager.isInSelfManagedCall(
- r.getSbn().getPackageName(), r.getUser(), true)).thenReturn(true);
+ r.getSbn().getPackageName(), true)).thenReturn(true);
assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
r.getSbn().getId(), r.getSbn().getTag(), r, false, false)).isTrue();
@@ -12024,7 +12091,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// style + self managed call - bypasses block
mService.clearNotifications();
reset(mUsageStats);
- when(mTelecomManager.isInSelfManagedCall(r.getSbn().getPackageName(), r.getUser(), true))
+ when(mTelecomManager.isInSelfManagedCall(r.getSbn().getPackageName(), true))
.thenReturn(true);
mService.addEnqueuedNotification(r);
@@ -12215,9 +12282,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// grouphelper is a mock here, so make the calls it would make
// add summary
- mService.addNotification(mService.createAutoGroupSummary(nr1.getUserId(),
- nr1.getSbn().getPackageName(), nr1.getKey(),
- GroupHelper.BASE_FLAGS | FLAG_ONGOING_EVENT, mock(Icon.class), 0));
+ mService.addNotification(
+ mService.createAutoGroupSummary(nr1.getUserId(), nr1.getSbn().getPackageName(),
+ nr1.getKey(), GroupHelper.BASE_FLAGS | FLAG_ONGOING_EVENT, mock(Icon.class), 0,
+ VISIBILITY_PRIVATE));
// cancel both children
mBinderService.cancelNotificationWithTag(PKG, PKG, nr0.getSbn().getTag(),
@@ -12246,7 +12314,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mService.addNotification(nr1);
mService.addNotification(
mService.createAutoGroupSummary(nr1.getUserId(), nr1.getSbn().getPackageName(),
- nr1.getKey(), GroupHelper.BASE_FLAGS, mock(Icon.class), 0));
+ nr1.getKey(), GroupHelper.BASE_FLAGS, mock(Icon.class), 0, VISIBILITY_PRIVATE));
// add notifications + summary for USER_ALL
NotificationRecord nr0_all =
@@ -12259,7 +12327,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mService.addNotification(
mService.createAutoGroupSummary(nr0_all.getUserId(),
nr0_all.getSbn().getPackageName(),
- nr0_all.getKey(), GroupHelper.BASE_FLAGS, mock(Icon.class), 0));
+ nr0_all.getKey(), GroupHelper.BASE_FLAGS, mock(Icon.class), 0, VISIBILITY_PRIVATE));
// cancel both children for USER_ALL
mBinderService.cancelNotificationWithTag(PKG, PKG, nr0_all.getSbn().getTag(),
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
index 12f9e26e05d0..abfb95c9431a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -625,6 +625,21 @@ public class ZenModeConfigTest extends UiServiceTestCase {
}
@Test
+ public void testRuleXml_customInterruptionFilter() throws Exception {
+ ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
+ rule.zenMode = Settings.Global.ZEN_MODE_ALARMS;
+ rule.conditionId = Uri.parse("condition://android/blah");
+ assertThat(Condition.isValidId(rule.conditionId, ZenModeConfig.SYSTEM_AUTHORITY)).isTrue();
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ writeRuleXml(rule, baos);
+ ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+ ZenModeConfig.ZenRule fromXml = readRuleXml(bais);
+
+ assertEquals(rule.zenMode, fromXml.zenMode);
+ }
+
+ @Test
public void testZenPolicyXml_allUnset() throws Exception {
ZenPolicy policy = new ZenPolicy.Builder().build();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index f9ba33b526a9..8c50ef406ec6 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -260,6 +260,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
AppOpsManager mAppOps;
TestableFlagResolver mTestFlagResolver = new TestableFlagResolver();
ZenModeEventLoggerFake mZenModeEventLogger;
+ private String mPkg;
@Before
public void setUp() throws PackageManager.NameNotFoundException {
@@ -270,7 +271,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
mContentResolver = mContext.getContentResolver();
mResources = mock(Resources.class, withSettings()
.spiedInstance(mContext.getResources()));
- String pkg = mContext.getPackageName();
+ mPkg = mContext.getPackageName();
try {
when(mResources.getXml(R.xml.default_zen_mode_config)).thenReturn(
getDefaultConfigParser());
@@ -301,14 +302,14 @@ public class ZenModeHelperTest extends UiServiceTestCase {
when(mPackageManager.getPackageUidAsUser(eq(CUSTOM_PKG_NAME), anyInt()))
.thenReturn(CUSTOM_PKG_UID);
when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(
- new String[]{pkg});
+ new String[]{mPkg});
ApplicationInfo appInfoSpy = spy(new ApplicationInfo());
appInfoSpy.icon = ICON_RES_ID;
when(appInfoSpy.loadLabel(any())).thenReturn(CUSTOM_APP_LABEL);
when(mPackageManager.getApplicationInfo(eq(CUSTOM_PKG_NAME), anyInt()))
.thenReturn(appInfoSpy);
- when(mPackageManager.getApplicationInfo(eq(mContext.getPackageName()), anyInt()))
+ when(mPackageManager.getApplicationInfo(eq(mPkg), anyInt()))
.thenReturn(appInfoSpy);
mZenModeHelper.mPm = mPackageManager;
@@ -649,6 +650,74 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
+ @EnableFlags(Flags.FLAG_MODES_API)
+ public void testTotalSilence_consolidatedPolicyDisallowsAll() {
+ // Start with zen mode off just to make sure global/manual mode isn't doing anything.
+ mZenModeHelper.mZenMode = ZEN_MODE_OFF;
+
+ // Create a zen rule that calls for total silence via zen mode, but does not specify any
+ // particular policy. This confirms that the application of the policy is based only on the
+ // actual zen mode setting.
+ AutomaticZenRule azr = new AutomaticZenRule.Builder("OriginalName", CONDITION_ID)
+ .setInterruptionFilter(INTERRUPTION_FILTER_NONE)
+ .build();
+ String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+ azr, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "reason", Process.SYSTEM_UID);
+
+ // Enable rule
+ mZenModeHelper.setAutomaticZenRuleState(ruleId,
+ new Condition(azr.getConditionId(), "", STATE_TRUE),
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI,
+ Process.SYSTEM_UID);
+
+ // Confirm that the consolidated policy doesn't allow anything
+ NotificationManager.Policy policy = mZenModeHelper.getConsolidatedNotificationPolicy();
+ assertThat(policy.allowAlarms()).isFalse();
+ assertThat(policy.allowMedia()).isFalse();
+ assertThat(policy.allowCalls()).isFalse();
+ assertThat(policy.allowMessages()).isFalse();
+ assertThat(policy.allowConversations()).isFalse();
+ assertThat(policy.allowEvents()).isFalse();
+ assertThat(policy.allowReminders()).isFalse();
+ assertThat(policy.allowRepeatCallers()).isFalse();
+ assertThat(policy.allowPriorityChannels()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_MODES_API)
+ public void testAlarmsOnly_consolidatedPolicyOnlyAllowsAlarmsAndMedia() {
+ // Start with zen mode off just to make sure global/manual mode isn't doing anything.
+ mZenModeHelper.mZenMode = ZEN_MODE_OFF;
+
+ // Create a zen rule that calls for alarms only via zen mode, but does not specify any
+ // particular policy. This confirms that the application of the policy is based only on the
+ // actual zen mode setting.
+ AutomaticZenRule azr = new AutomaticZenRule.Builder("OriginalName", CONDITION_ID)
+ .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
+ .build();
+ String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+ azr, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "reason", Process.SYSTEM_UID);
+
+ // Enable rule
+ mZenModeHelper.setAutomaticZenRuleState(ruleId,
+ new Condition(azr.getConditionId(), "", STATE_TRUE),
+ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI,
+ Process.SYSTEM_UID);
+
+ // Confirm that the consolidated policy allows only alarms and media and nothing else
+ NotificationManager.Policy policy = mZenModeHelper.getConsolidatedNotificationPolicy();
+ assertThat(policy.allowAlarms()).isTrue();
+ assertThat(policy.allowMedia()).isTrue();
+ assertThat(policy.allowCalls()).isFalse();
+ assertThat(policy.allowMessages()).isFalse();
+ assertThat(policy.allowConversations()).isFalse();
+ assertThat(policy.allowEvents()).isFalse();
+ assertThat(policy.allowReminders()).isFalse();
+ assertThat(policy.allowRepeatCallers()).isFalse();
+ assertThat(policy.allowPriorityChannels()).isFalse();
+ }
+
+ @Test
public void testZenUpgradeNotification() {
/**
* Commit a485ec65b5ba947d69158ad90905abf3310655cf disabled DND status change
@@ -2444,16 +2513,16 @@ public class ZenModeHelperTest extends UiServiceTestCase {
scheduleInfo.endHour = 1;
Uri sharedUri = ZenModeConfig.toScheduleConditionId(scheduleInfo);
AutomaticZenRule zenRule = new AutomaticZenRule("name",
- new ComponentName("android", "ScheduleConditionProvider"),
+ new ComponentName(mPkg, "ScheduleConditionProvider"),
sharedUri,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelper.addAutomaticZenRule("android", zenRule,
+ String id = mZenModeHelper.addAutomaticZenRule(mPkg, zenRule,
UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
AutomaticZenRule zenRule2 = new AutomaticZenRule("name2",
- new ComponentName("android", "ScheduleConditionProvider"),
+ new ComponentName(mPkg, "ScheduleConditionProvider"),
sharedUri,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id2 = mZenModeHelper.addAutomaticZenRule("android", zenRule2,
+ String id2 = mZenModeHelper.addAutomaticZenRule(mPkg, zenRule2,
UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
Condition condition = new Condition(sharedUri, "", STATE_TRUE);
@@ -2463,11 +2532,11 @@ public class ZenModeHelperTest extends UiServiceTestCase {
for (ZenModeConfig.ZenRule rule : mZenModeHelper.mConfig.automaticRules.values()) {
if (rule.id.equals(id)) {
assertNotNull(rule.condition);
- assertTrue(rule.condition.state == STATE_TRUE);
+ assertEquals(STATE_TRUE, rule.condition.state);
}
if (rule.id.equals(id2)) {
assertNotNull(rule.condition);
- assertTrue(rule.condition.state == STATE_TRUE);
+ assertEquals(STATE_TRUE, rule.condition.state);
}
}
@@ -2478,11 +2547,11 @@ public class ZenModeHelperTest extends UiServiceTestCase {
for (ZenModeConfig.ZenRule rule : mZenModeHelper.mConfig.automaticRules.values()) {
if (rule.id.equals(id)) {
assertNotNull(rule.condition);
- assertTrue(rule.condition.state == STATE_FALSE);
+ assertEquals(STATE_FALSE, rule.condition.state);
}
if (rule.id.equals(id2)) {
assertNotNull(rule.condition);
- assertTrue(rule.condition.state == STATE_FALSE);
+ assertEquals(STATE_FALSE, rule.condition.state);
}
}
}
@@ -3569,14 +3638,14 @@ public class ZenModeHelperTest extends UiServiceTestCase {
AutomaticZenRule bedtime = new AutomaticZenRule.Builder("Bedtime Mode (TM)", CONDITION_ID)
.setType(TYPE_BEDTIME)
.build();
- String bedtimeRuleId = mZenModeHelper.addAutomaticZenRule("pkg", bedtime, UPDATE_ORIGIN_APP,
+ String bedtimeRuleId = mZenModeHelper.addAutomaticZenRule(mPkg, bedtime, UPDATE_ORIGIN_APP,
"reason", CUSTOM_PKG_UID);
// Create immersive rule
AutomaticZenRule immersive = new AutomaticZenRule.Builder("Immersed", CONDITION_ID)
.setType(TYPE_IMMERSIVE)
.build();
- String immersiveId = mZenModeHelper.addAutomaticZenRule("pkg", immersive, UPDATE_ORIGIN_APP,
+ String immersiveId = mZenModeHelper.addAutomaticZenRule(mPkg, immersive, UPDATE_ORIGIN_APP,
"reason", CUSTOM_PKG_UID);
// Event 2: Activate bedtime rule
@@ -5316,6 +5385,7 @@ public class ZenModeHelperTest extends UiServiceTestCase {
ZenRule rule = new ZenRule();
rule.pkg = pkg;
rule.creationTime = createdAt.toEpochMilli();
+ rule.enabled = true;
rule.deletionInstant = deletedAt;
// Plus stuff so that isValidAutomaticRule() passes
rule.name = "A rule from " + pkg + " created on " + createdAt;
@@ -5324,6 +5394,89 @@ public class ZenModeHelperTest extends UiServiceTestCase {
}
@Test
+ @EnableFlags(Flags.FLAG_MODES_API)
+ public void getAutomaticZenRuleState_ownedRule_returnsRuleState() {
+ String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+ new AutomaticZenRule.Builder("Rule", CONDITION_ID)
+ .setConfigurationActivity(
+ new ComponentName(mContext.getPackageName(), "Blah"))
+ .build(),
+ UPDATE_ORIGIN_APP, "reasons", CUSTOM_PKG_UID);
+
+ // Null condition -> STATE_FALSE
+ assertThat(mZenModeHelper.getAutomaticZenRuleState(id)).isEqualTo(Condition.STATE_FALSE);
+
+ mZenModeHelper.setAutomaticZenRuleState(id, CONDITION_TRUE, UPDATE_ORIGIN_APP,
+ CUSTOM_PKG_UID);
+ assertThat(mZenModeHelper.getAutomaticZenRuleState(id)).isEqualTo(Condition.STATE_TRUE);
+
+ mZenModeHelper.setAutomaticZenRuleState(id, CONDITION_FALSE, UPDATE_ORIGIN_APP,
+ CUSTOM_PKG_UID);
+ assertThat(mZenModeHelper.getAutomaticZenRuleState(id)).isEqualTo(Condition.STATE_FALSE);
+
+ mZenModeHelper.removeAutomaticZenRule(id, UPDATE_ORIGIN_APP, "", CUSTOM_PKG_UID);
+ assertThat(mZenModeHelper.getAutomaticZenRuleState(id)).isEqualTo(Condition.STATE_UNKNOWN);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_MODES_API)
+ public void getAutomaticZenRuleState_notOwnedRule_returnsStateUnknown() {
+ // Assume existence of a system-owned rule that is currently ACTIVE.
+ ZenRule systemRule = newZenRule("android", Instant.now(), null);
+ systemRule.zenMode = ZEN_MODE_ALARMS;
+ systemRule.condition = new Condition(systemRule.conditionId, "on", Condition.STATE_TRUE);
+ ZenModeConfig config = mZenModeHelper.mConfig.copy();
+ config.automaticRules.put("systemRule", systemRule);
+ mZenModeHelper.setConfig(config, null, UPDATE_ORIGIN_INIT, "", Process.SYSTEM_UID);
+ assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS);
+
+ assertThat(mZenModeHelper.getAutomaticZenRuleState("systemRule")).isEqualTo(
+ Condition.STATE_UNKNOWN);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_MODES_API)
+ public void setAutomaticZenRuleState_idForNotOwnedRule_ignored() {
+ // Assume existence of an other-package-owned rule that is currently ACTIVE.
+ assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
+ ZenRule otherRule = newZenRule("another.package", Instant.now(), null);
+ otherRule.zenMode = ZEN_MODE_ALARMS;
+ otherRule.condition = new Condition(otherRule.conditionId, "on", Condition.STATE_TRUE);
+ ZenModeConfig config = mZenModeHelper.mConfig.copy();
+ config.automaticRules.put("otherRule", otherRule);
+ mZenModeHelper.setConfig(config, null, UPDATE_ORIGIN_INIT, "", Process.SYSTEM_UID);
+ assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS);
+
+ // Should be ignored.
+ mZenModeHelper.setAutomaticZenRuleState("otherRule",
+ new Condition(otherRule.conditionId, "off", Condition.STATE_FALSE),
+ UPDATE_ORIGIN_APP, CUSTOM_PKG_UID);
+
+ assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_MODES_API)
+ public void setAutomaticZenRuleState_conditionForNotOwnedRule_ignored() {
+ // Assume existence of an other-package-owned rule that is currently ACTIVE.
+ assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
+ ZenRule otherRule = newZenRule("another.package", Instant.now(), null);
+ otherRule.zenMode = ZEN_MODE_ALARMS;
+ otherRule.condition = new Condition(otherRule.conditionId, "on", Condition.STATE_TRUE);
+ ZenModeConfig config = mZenModeHelper.mConfig.copy();
+ config.automaticRules.put("otherRule", otherRule);
+ mZenModeHelper.setConfig(config, null, UPDATE_ORIGIN_INIT, "", Process.SYSTEM_UID);
+ assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS);
+
+ // Should be ignored.
+ mZenModeHelper.setAutomaticZenRuleState(otherRule.conditionId,
+ new Condition(otherRule.conditionId, "off", Condition.STATE_FALSE),
+ UPDATE_ORIGIN_APP, CUSTOM_PKG_UID);
+
+ assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS);
+ }
+
+ @Test
@EnableFlags(android.app.Flags.FLAG_MODES_API)
public void testCallbacks_policy() throws Exception {
setupZenConfig();
@@ -5473,13 +5626,13 @@ public class ZenModeHelperTest extends UiServiceTestCase {
public void applyGlobalZenModeAsImplicitZenRule_modeOff_deactivatesImplicitRule() {
mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
mZenModeHelper.mConfig.automaticRules.clear();
- mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+ mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(mPkg, CUSTOM_PKG_UID,
ZEN_MODE_IMPORTANT_INTERRUPTIONS);
assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(1);
assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).condition.state)
.isEqualTo(STATE_TRUE);
- mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
+ mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(mPkg, CUSTOM_PKG_UID,
ZEN_MODE_OFF);
assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).condition.state)
diff --git a/services/tests/vibrator/Android.bp b/services/tests/vibrator/Android.bp
index 66dcaff687c8..da21cd3cf919 100644
--- a/services/tests/vibrator/Android.bp
+++ b/services/tests/vibrator/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_haptics_framework",
// 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"
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
index b431888a72fb..3e59878f9e1e 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
@@ -35,8 +35,8 @@ import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.ContextWrapper;
import android.content.pm.PackageManagerInternal;
+import android.os.ExternalVibrationScale;
import android.os.Handler;
-import android.os.IExternalVibratorService;
import android.os.PowerManagerInternal;
import android.os.UserHandle;
import android.os.VibrationAttributes;
@@ -49,6 +49,7 @@ import android.os.vibrator.PrimitiveSegment;
import android.os.vibrator.StepSegment;
import android.os.vibrator.VibrationConfig;
import android.os.vibrator.VibrationEffectSegment;
+import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
@@ -119,29 +120,65 @@ public class VibrationScalerTest {
public void testGetExternalVibrationScale() {
setDefaultIntensity(USAGE_TOUCH, Vibrator.VIBRATION_INTENSITY_LOW);
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH);
- assertEquals(IExternalVibratorService.SCALE_VERY_HIGH,
- mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH));
+ assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_VERY_HIGH,
+ mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH));
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_MEDIUM);
- assertEquals(IExternalVibratorService.SCALE_HIGH,
- mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH));
+ assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_HIGH,
+ mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH));
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_LOW);
- assertEquals(IExternalVibratorService.SCALE_NONE,
- mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH));
+ assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_NONE,
+ mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH));
setDefaultIntensity(USAGE_TOUCH, VIBRATION_INTENSITY_MEDIUM);
- assertEquals(IExternalVibratorService.SCALE_LOW,
- mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH));
+ assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_LOW,
+ mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH));
setDefaultIntensity(USAGE_TOUCH, VIBRATION_INTENSITY_HIGH);
- assertEquals(IExternalVibratorService.SCALE_VERY_LOW,
- mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH));
+ assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_VERY_LOW,
+ mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH));
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
// Vibration setting being bypassed will use default setting and not scale.
- assertEquals(IExternalVibratorService.SCALE_NONE,
- mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH));
+ assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_NONE,
+ mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH));
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+ public void testAdaptiveHapticsScale_withAdaptiveHapticsAvailable() {
+ setDefaultIntensity(USAGE_TOUCH, Vibrator.VIBRATION_INTENSITY_LOW);
+ setDefaultIntensity(USAGE_RINGTONE, Vibrator.VIBRATION_INTENSITY_LOW);
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH);
+ setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH);
+
+ mVibrationScaler.updateAdaptiveHapticsScale(USAGE_TOUCH, 0.5f);
+ mVibrationScaler.updateAdaptiveHapticsScale(USAGE_RINGTONE, 0.2f);
+
+ assertEquals(0.5f, mVibrationScaler.getAdaptiveHapticsScale(USAGE_TOUCH));
+ assertEquals(0.2f, mVibrationScaler.getAdaptiveHapticsScale(USAGE_RINGTONE));
+ assertEquals(1f, mVibrationScaler.getAdaptiveHapticsScale(USAGE_NOTIFICATION));
+
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
+ // Vibration setting being bypassed will apply adaptive haptics scales.
+ assertEquals(0.2f, mVibrationScaler.getAdaptiveHapticsScale(USAGE_RINGTONE));
+ }
+
+ @Test
+ @RequiresFlagsDisabled(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+ public void testAdaptiveHapticsScale_flagDisabled_adaptiveHapticScaleAlwaysNone() {
+ setDefaultIntensity(USAGE_TOUCH, Vibrator.VIBRATION_INTENSITY_LOW);
+ setDefaultIntensity(USAGE_RINGTONE, Vibrator.VIBRATION_INTENSITY_LOW);
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH);
+ setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH);
+
+ mVibrationScaler.updateAdaptiveHapticsScale(USAGE_TOUCH, 0.5f);
+ mVibrationScaler.updateAdaptiveHapticsScale(USAGE_RINGTONE, 0.2f);
+
+ assertEquals(1f, mVibrationScaler.getAdaptiveHapticsScale(USAGE_TOUCH));
+ assertEquals(1f, mVibrationScaler.getAdaptiveHapticsScale(USAGE_RINGTONE));
+ assertEquals(1f, mVibrationScaler.getAdaptiveHapticsScale(USAGE_NOTIFICATION));
}
@Test
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 f0803418376f..f54c7e57828b 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -100,6 +100,8 @@ public class VibrationSettingsTest {
@Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+ private static final int OLD_USER_ID = 123;
+ private static final int NEW_USER_ID = 456;
private static final int UID = 1;
private static final int VIRTUAL_DEVICE_ID = 1;
private static final String SYSUI_PACKAGE_NAME = "sysui";
@@ -211,10 +213,10 @@ public class VibrationSettingsTest {
mVibrationSettings.addListener(mListenerMock);
// Testing the broadcast flow manually.
- mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
- new Intent(Intent.ACTION_USER_SWITCHED));
+ mVibrationSettings.mUserSwitchObserver.onUserSwitching(NEW_USER_ID);
+ mVibrationSettings.mUserSwitchObserver.onUserSwitchComplete(NEW_USER_ID);
- verify(mListenerMock).onChange();
+ verify(mListenerMock, times(2)).onChange();
}
@Test
@@ -265,8 +267,7 @@ public class VibrationSettingsTest {
// Trigger multiple observers manually.
mVibrationSettings.mSettingObserver.onChange(false);
mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
- mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
- new Intent(Intent.ACTION_USER_SWITCHED));
+ mVibrationSettings.mUserSwitchObserver.onUserSwitchComplete(NEW_USER_ID);
mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
new Intent(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
@@ -834,13 +835,17 @@ public class VibrationSettingsTest {
assertEquals(VIBRATION_INTENSITY_HIGH,
mVibrationSettings.getCurrentIntensity(USAGE_RINGTONE));
- // Switching user is not working with FakeSettingsProvider.
- // Testing the broadcast flow manually.
- Settings.System.putIntForUser(mContextSpy.getContentResolver(),
- Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW,
+ // Test early update of settings based on new user id.
+ putUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW,
+ NEW_USER_ID);
+ mVibrationSettings.mUserSwitchObserver.onUserSwitching(NEW_USER_ID);
+ assertEquals(VIBRATION_INTENSITY_LOW,
+ mVibrationSettings.getCurrentIntensity(USAGE_RINGTONE));
+
+ // Test later update of settings for UserHandle.USER_CURRENT.
+ putUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW,
UserHandle.USER_CURRENT);
- mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
- new Intent(Intent.ACTION_USER_SWITCHED));
+ mVibrationSettings.mUserSwitchObserver.onUserSwitchComplete(NEW_USER_ID);
assertEquals(VIBRATION_INTENSITY_LOW,
mVibrationSettings.getCurrentIntensity(USAGE_RINGTONE));
}
@@ -956,12 +961,16 @@ public class VibrationSettingsTest {
}
private void setUserSetting(String settingName, int value) {
- Settings.System.putIntForUser(
- mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT);
+ putUserSetting(settingName, value, UserHandle.USER_CURRENT);
// FakeSettingsProvider doesn't support testing triggering ContentObserver yet.
mVibrationSettings.mSettingObserver.onChange(false);
}
+ private void putUserSetting(String settingName, int value, int userHandle) {
+ Settings.System.putIntForUser(
+ mContextSpy.getContentResolver(), settingName, value, userHandle);
+ }
+
private void setRingerMode(int ringerMode) {
when(mAudioManagerMock.getRingerModeInternal()).thenReturn(ringerMode);
// Mock AudioManager broadcast of internal ringer mode change.
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
index 2823223e4859..417fbd06be66 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 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.
@@ -35,7 +35,6 @@ import static org.mockito.Mockito.when;
import android.content.ComponentName;
import android.content.pm.PackageManagerInternal;
import android.frameworks.vibrator.ScaleParam;
-import android.frameworks.vibrator.VibrationParam;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -55,8 +54,6 @@ import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
-import java.util.ArrayList;
-import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
@@ -135,7 +132,7 @@ public class VibratorControlServiceTest {
vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f);
mVibratorControlService.onRequestVibrationParamsComplete(token,
- generateVibrationParams(vibrationScales));
+ VibrationParamGenerator.generateVibrationParams(vibrationScales));
verify(mMockVibrationScaler).updateAdaptiveHapticsScale(USAGE_ALARM, 0.7f);
verify(mMockVibrationScaler).updateAdaptiveHapticsScale(USAGE_NOTIFICATION, 0.4f);
@@ -162,7 +159,7 @@ public class VibratorControlServiceTest {
vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f);
mVibratorControlService.onRequestVibrationParamsComplete(new Binder(),
- generateVibrationParams(vibrationScales));
+ VibrationParamGenerator.generateVibrationParams(vibrationScales));
verifyZeroInteractions(mMockVibrationScaler);
}
@@ -175,7 +172,8 @@ public class VibratorControlServiceTest {
vibrationScales.put(ScaleParam.TYPE_ALARM, 0.7f);
vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f);
- mVibratorControlService.setVibrationParams(generateVibrationParams(vibrationScales),
+ mVibratorControlService.setVibrationParams(
+ VibrationParamGenerator.generateVibrationParams(vibrationScales),
mFakeVibratorController);
verify(mMockVibrationScaler).updateAdaptiveHapticsScale(USAGE_ALARM, 0.7f);
@@ -193,7 +191,8 @@ public class VibratorControlServiceTest {
vibrationScales.put(ScaleParam.TYPE_ALARM, 0.7f);
vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f);
- mVibratorControlService.setVibrationParams(generateVibrationParams(vibrationScales),
+ mVibratorControlService.setVibrationParams(
+ VibrationParamGenerator.generateVibrationParams(vibrationScales),
mFakeVibratorController);
verifyZeroInteractions(mMockVibrationScaler);
@@ -268,28 +267,6 @@ public class VibratorControlServiceTest {
}
}
- private VibrationParam[] generateVibrationParams(SparseArray<Float> vibrationScales) {
- List<VibrationParam> vibrationParamList = new ArrayList<>();
- for (int i = 0; i < vibrationScales.size(); i++) {
- int type = vibrationScales.keyAt(i);
- float scale = vibrationScales.valueAt(i);
-
- vibrationParamList.add(generateVibrationParam(type, scale));
- }
-
- return vibrationParamList.toArray(new VibrationParam[0]);
- }
-
- private VibrationParam generateVibrationParam(int type, float scale) {
- ScaleParam scaleParam = new ScaleParam();
- scaleParam.typesMask = type;
- scaleParam.scale = scale;
- VibrationParam vibrationParam = new VibrationParam();
- vibrationParam.setScale(scaleParam);
-
- return vibrationParam;
- }
-
private int buildVibrationTypesMask(int... types) {
int typesMask = 0;
for (int type : types) {
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 7db707a42ff0..ed89ccf07453 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -50,6 +50,7 @@ import android.content.ContextWrapper;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.res.Resources;
+import android.frameworks.vibrator.ScaleParam;
import android.hardware.input.IInputManager;
import android.hardware.input.InputManager;
import android.hardware.input.InputManagerGlobal;
@@ -59,16 +60,17 @@ import android.media.AudioAttributes;
import android.media.AudioManager;
import android.os.CombinedVibration;
import android.os.ExternalVibration;
+import android.os.ExternalVibrationScale;
import android.os.Handler;
import android.os.IBinder;
import android.os.IExternalVibrationController;
-import android.os.IExternalVibratorService;
import android.os.IVibratorStateListener;
import android.os.Looper;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.Process;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.VibrationAttributes;
@@ -82,6 +84,10 @@ import android.os.vibrator.PrimitiveSegment;
import android.os.vibrator.StepSegment;
import android.os.vibrator.VibrationConfig;
import android.os.vibrator.VibrationEffectSegment;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+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.SparseArray;
@@ -153,6 +159,8 @@ public class VibratorManagerServiceTest {
public MockitoRule rule = MockitoJUnit.rule();
@Rule
public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -185,8 +193,10 @@ public class VibratorManagerServiceTest {
private Context mContextSpy;
private TestLooper mTestLooper;
private FakeVibrator mVibrator;
+ private FakeVibratorController mFakeVibratorController;
private PowerManagerInternal.LowPowerModeListener mRegisteredPowerModeListener;
private VibratorManagerService.ExternalVibratorService mExternalVibratorService;
+ private VibratorControlService mVibratorControlService;
private VibrationConfig mVibrationConfig;
private InputManagerGlobal.TestSession mInputManagerGlobalSession;
private InputManager mInputManager;
@@ -197,6 +207,7 @@ public class VibratorManagerServiceTest {
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
mInputManagerGlobalSession = InputManagerGlobal.createTestSession(mIInputManagerMock);
mVibrationConfig = new VibrationConfig(mContextSpy.getResources());
+ mFakeVibratorController = new FakeVibratorController();
ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
@@ -310,6 +321,8 @@ public class VibratorManagerServiceTest {
if (service instanceof VibratorManagerService.ExternalVibratorService) {
mExternalVibratorService =
(VibratorManagerService.ExternalVibratorService) service;
+ } else if (service instanceof VibratorControlService) {
+ mVibratorControlService = (VibratorControlService) service;
}
}
@@ -321,9 +334,13 @@ public class VibratorManagerServiceTest {
VibratorControllerHolder createVibratorControllerHolder() {
VibratorControllerHolder holder = new VibratorControllerHolder();
- holder.setVibratorController(new FakeVibratorController());
+ holder.setVibratorController(mFakeVibratorController);
return holder;
}
+
+ boolean isServiceDeclared(String name) {
+ return true;
+ }
});
return mService;
}
@@ -1108,12 +1125,13 @@ public class VibratorManagerServiceTest {
ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
AUDIO_ALARM_ATTRS,
controller, firstToken);
- int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
+ ExternalVibrationScale scale =
+ mExternalVibratorService.onExternalVibrationStart(externalVibration);
vibrateAndWaitUntilFinished(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
RINGTONE_ATTRS);
- assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
+ assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
// The external vibration should have been cancelled
verify(controller).mute();
assertEquals(Arrays.asList(false, true, false),
@@ -1421,6 +1439,7 @@ public class VibratorManagerServiceTest {
public void vibrate_withIntensitySettings_appliesSettingsToScaleVibrations() throws Exception {
int defaultNotificationIntensity =
mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_NOTIFICATION);
+ // This will scale up notification vibrations.
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
defaultNotificationIntensity < Vibrator.VIBRATION_INTENSITY_HIGH
? defaultNotificationIntensity + 1
@@ -1428,6 +1447,7 @@ public class VibratorManagerServiceTest {
int defaultTouchIntensity =
mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_TOUCH);
+ // This will scale down touch vibrations.
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
defaultTouchIntensity > Vibrator.VIBRATION_INTENSITY_LOW
? defaultTouchIntensity - 1
@@ -1482,6 +1502,42 @@ public class VibratorManagerServiceTest {
}
@Test
+ public void vibrate_withBypassScaleFlag_ignoresIntensitySettingsAndResolvesAmplitude()
+ throws Exception {
+ // Permission needed for bypassing user settings
+ grantPermission(android.Manifest.permission.MODIFY_PHONE_STATE);
+
+ int defaultTouchIntensity =
+ mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_TOUCH);
+ // This will scale down touch vibrations.
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
+ defaultTouchIntensity > Vibrator.VIBRATION_INTENSITY_LOW
+ ? defaultTouchIntensity - 1
+ : defaultTouchIntensity);
+
+ int defaultAmplitude = mContextSpy.getResources().getInteger(
+ com.android.internal.R.integer.config_defaultVibrationAmplitude);
+
+ mockVibrators(1);
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
+ fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+ VibratorManagerService service = createSystemReadyService();
+
+ vibrateAndWaitUntilFinished(service,
+ VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE),
+ new VibrationAttributes.Builder()
+ .setUsage(VibrationAttributes.USAGE_TOUCH)
+ .setFlags(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE)
+ .build());
+
+ assertEquals(1, fakeVibrator.getAllEffectSegments().size());
+
+ assertEquals(defaultAmplitude / 255f, fakeVibrator.getAmplitudes().get(0), 1e-5);
+
+ cancelVibrate(service); // Clean up long-ish effect.
+ }
+
+ @Test
public void vibrate_withPowerModeChange_cancelVibrationIfNotAllowed() throws Exception {
mockVibrators(1, 2);
VibratorManagerService service = createSystemReadyService();
@@ -1670,13 +1726,14 @@ public class VibratorManagerServiceTest {
ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
AUDIO_ALARM_ATTRS,
mock(IExternalVibrationController.class), binderToken);
- int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
- assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
+ ExternalVibrationScale scale = mExternalVibratorService.onExternalVibrationStart(
+ externalVibration);
+ assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
when(mVirtualDeviceManagerInternalMock.isAppRunningOnAnyVirtualDevice(UID))
.thenReturn(true);
scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
- assertEquals(IExternalVibratorService.SCALE_MUTE, scale);
+ assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
}
@Test
@@ -1689,10 +1746,11 @@ public class VibratorManagerServiceTest {
ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
AUDIO_ALARM_ATTRS,
mock(IExternalVibrationController.class), binderToken);
- int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
+ ExternalVibrationScale scale = mExternalVibratorService.onExternalVibrationStart(
+ externalVibration);
mExternalVibratorService.onExternalVibrationStop(externalVibration);
- assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
+ assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
assertEquals(Arrays.asList(false, true, false),
mVibratorProviders.get(1).getExternalControlStates());
@@ -1715,17 +1773,19 @@ public class VibratorManagerServiceTest {
ExternalVibration firstVibration = new ExternalVibration(UID, PACKAGE_NAME,
AUDIO_ALARM_ATTRS,
firstController, firstToken);
- int firstScale = mExternalVibratorService.onExternalVibrationStart(firstVibration);
+ ExternalVibrationScale firstScale =
+ mExternalVibratorService.onExternalVibrationStart(firstVibration);
AudioAttributes ringtoneAudioAttrs = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
.build();
ExternalVibration secondVibration = new ExternalVibration(UID, PACKAGE_NAME,
ringtoneAudioAttrs, secondController, secondToken);
- int secondScale = mExternalVibratorService.onExternalVibrationStart(secondVibration);
+ ExternalVibrationScale secondScale =
+ mExternalVibratorService.onExternalVibrationStart(secondVibration);
- assertNotEquals(IExternalVibratorService.SCALE_MUTE, firstScale);
- assertNotEquals(IExternalVibratorService.SCALE_MUTE, secondScale);
+ assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, firstScale.scaleLevel);
+ assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, secondScale.scaleLevel);
verify(firstController).mute();
verify(secondController, never()).mute();
// Set external control called only once.
@@ -1761,8 +1821,9 @@ public class VibratorManagerServiceTest {
ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
AUDIO_ALARM_ATTRS,
mock(IExternalVibrationController.class));
- int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
- assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
+ ExternalVibrationScale scale =
+ mExternalVibratorService.onExternalVibrationStart(externalVibration);
+ assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
// Vibration is cancelled.
assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
@@ -1787,9 +1848,10 @@ public class VibratorManagerServiceTest {
ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
AUDIO_ALARM_ATTRS,
mock(IExternalVibrationController.class));
- int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
+ ExternalVibrationScale scale =
+ mExternalVibratorService.onExternalVibrationStart(externalVibration);
// External vibration is ignored.
- assertEquals(IExternalVibratorService.SCALE_MUTE, scale);
+ assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
// Vibration is not cancelled.
assertFalse(waitUntil(s -> !s.isVibrating(1), service, CLEANUP_TIMEOUT_MILLIS));
@@ -1814,8 +1876,9 @@ public class VibratorManagerServiceTest {
ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
AUDIO_ALARM_ATTRS, mock(IExternalVibrationController.class));
- int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
- assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
+ ExternalVibrationScale scale =
+ mExternalVibratorService.onExternalVibrationStart(externalVibration);
+ assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
// Vibration is cancelled.
assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
@@ -1841,9 +1904,10 @@ public class VibratorManagerServiceTest {
ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
AUDIO_NOTIFICATION_ATTRS,
mock(IExternalVibrationController.class));
- int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
+ ExternalVibrationScale scale =
+ mExternalVibratorService.onExternalVibrationStart(externalVibration);
// New vibration is ignored.
- assertEquals(IExternalVibratorService.SCALE_MUTE, scale);
+ assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
// Vibration is not cancelled.
assertFalse(waitUntil(s -> !s.isVibrating(1), service, CLEANUP_TIMEOUT_MILLIS));
@@ -1863,22 +1927,26 @@ public class VibratorManagerServiceTest {
setRingerMode(AudioManager.RINGER_MODE_SILENT);
createSystemReadyService();
- int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
- assertEquals(IExternalVibratorService.SCALE_MUTE, scale);
+ ExternalVibrationScale scale =
+ mExternalVibratorService.onExternalVibrationStart(externalVibration);
+ assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
setRingerMode(AudioManager.RINGER_MODE_NORMAL);
createSystemReadyService();
scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
- assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
+ assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
createSystemReadyService();
scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
- assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
+ assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
}
@Test
public void onExternalVibration_withBypassMuteAudioFlag_ignoresUserSettings() {
+ // Permission needed for bypassing user settings
+ grantPermission(android.Manifest.permission.MODIFY_PHONE_STATE);
+
mockVibrators(1);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY,
@@ -1892,16 +1960,16 @@ public class VibratorManagerServiceTest {
.build();
createSystemReadyService();
- int scale = mExternalVibratorService.onExternalVibrationStart(
- new ExternalVibration(UID, PACKAGE_NAME, audioAttrs,
- mock(IExternalVibrationController.class)));
- assertEquals(IExternalVibratorService.SCALE_MUTE, scale);
+ ExternalVibration vib = new ExternalVibration(UID, PACKAGE_NAME, audioAttrs,
+ mock(IExternalVibrationController.class));
+ ExternalVibrationScale scale = mExternalVibratorService.onExternalVibrationStart(vib);
+ assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
- createSystemReadyService();
+ mExternalVibratorService.onExternalVibrationStop(vib);
scale = mExternalVibratorService.onExternalVibrationStart(
new ExternalVibration(UID, PACKAGE_NAME, flaggedAudioAttrs,
mock(IExternalVibrationController.class)));
- assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
+ assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
}
@Test
@@ -1912,14 +1980,94 @@ public class VibratorManagerServiceTest {
Vibrator.VIBRATION_INTENSITY_OFF);
AudioAttributes flaggedAudioAttrs = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_UNKNOWN)
- .setFlags(AudioAttributes.FLAG_BYPASS_MUTE)
.build();
createSystemReadyService();
- int scale = mExternalVibratorService.onExternalVibrationStart(
- new ExternalVibration(/* uid= */ 123, PACKAGE_NAME, flaggedAudioAttrs,
- mock(IExternalVibrationController.class)));
- assertEquals(IExternalVibratorService.SCALE_MUTE, scale);
+ ExternalVibrationScale scale =
+ mExternalVibratorService.onExternalVibrationStart(
+ new ExternalVibration(/* uid= */ 123, PACKAGE_NAME, flaggedAudioAttrs,
+ mock(IExternalVibrationController.class)));
+ assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+ public void onExternalVibration_withAdaptiveHaptics_returnsCorrectAdaptiveScales()
+ throws RemoteException {
+ mockVibrators(1);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL,
+ IVibrator.CAP_AMPLITUDE_CONTROL);
+ createSystemReadyService();
+
+ SparseArray<Float> vibrationScales = new SparseArray<>();
+ vibrationScales.put(ScaleParam.TYPE_ALARM, 0.7f);
+ vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f);
+
+ mVibratorControlService.setVibrationParams(
+ VibrationParamGenerator.generateVibrationParams(vibrationScales),
+ mFakeVibratorController);
+ ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
+ AUDIO_ALARM_ATTRS,
+ mock(IExternalVibrationController.class));
+ ExternalVibrationScale scale =
+ mExternalVibratorService.onExternalVibrationStart(externalVibration);
+ mExternalVibratorService.onExternalVibrationStop(externalVibration);
+
+ assertEquals(scale.adaptiveHapticsScale, 0.7f, 0);
+
+ externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
+ AUDIO_NOTIFICATION_ATTRS,
+ mock(IExternalVibrationController.class));
+ scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
+ mExternalVibratorService.onExternalVibrationStop(externalVibration);
+
+ assertEquals(scale.adaptiveHapticsScale, 0.4f, 0);
+
+ AudioAttributes ringtoneAudioAttrs = new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
+ .build();
+ externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
+ ringtoneAudioAttrs,
+ mock(IExternalVibrationController.class));
+ scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
+
+ assertEquals(scale.adaptiveHapticsScale, 1f, 0);
+ }
+
+ @Test
+ @RequiresFlagsDisabled(android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+ public void onExternalVibration_withAdaptiveHapticsFlagDisabled_alwaysReturnScaleNone()
+ throws RemoteException {
+ mockVibrators(1);
+ mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL,
+ IVibrator.CAP_AMPLITUDE_CONTROL);
+ createSystemReadyService();
+
+ SparseArray<Float> vibrationScales = new SparseArray<>();
+ vibrationScales.put(ScaleParam.TYPE_ALARM, 0.7f);
+ vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f);
+
+ mVibratorControlService.setVibrationParams(
+ VibrationParamGenerator.generateVibrationParams(vibrationScales),
+ mFakeVibratorController);
+ ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
+ AUDIO_ALARM_ATTRS,
+ mock(IExternalVibrationController.class));
+ ExternalVibrationScale scale =
+ mExternalVibratorService.onExternalVibrationStart(externalVibration);
+ mExternalVibratorService.onExternalVibrationStop(externalVibration);
+
+ assertEquals(scale.adaptiveHapticsScale, 1f, 0);
+
+ mVibratorControlService.setVibrationParams(
+ VibrationParamGenerator.generateVibrationParams(vibrationScales),
+ mFakeVibratorController);
+ externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
+ AUDIO_NOTIFICATION_ATTRS,
+ mock(IExternalVibrationController.class));
+ scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
+
+ assertEquals(scale.adaptiveHapticsScale, 1f, 0);
}
@Test
diff --git a/services/tests/vibrator/utils/com/android/server/vibrator/VibrationParamGenerator.java b/services/tests/vibrator/utils/com/android/server/vibrator/VibrationParamGenerator.java
new file mode 100644
index 000000000000..a606388da190
--- /dev/null
+++ b/services/tests/vibrator/utils/com/android/server/vibrator/VibrationParamGenerator.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 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.vibrator;
+
+import android.frameworks.vibrator.ScaleParam;
+import android.frameworks.vibrator.VibrationParam;
+import android.util.SparseArray;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A helper class that can be used to generate arrays of {@link VibrationParam}.
+ */
+public final class VibrationParamGenerator {
+ /**
+ * Generates an array of {@link VibrationParam}.
+ */
+ public static VibrationParam[] generateVibrationParams(SparseArray<Float> vibrationScales) {
+ List<VibrationParam> vibrationParamList = new ArrayList<>();
+ for (int i = 0; i < vibrationScales.size(); i++) {
+ int type = vibrationScales.keyAt(i);
+ float scale = vibrationScales.valueAt(i);
+
+ vibrationParamList.add(generateVibrationParam(type, scale));
+ }
+
+ return vibrationParamList.toArray(new VibrationParam[0]);
+ }
+
+ private static VibrationParam generateVibrationParam(int type, float scale) {
+ ScaleParam scaleParam = new ScaleParam();
+ scaleParam.typesMask = type;
+ scaleParam.scale = scale;
+ VibrationParam vibrationParam = new VibrationParam();
+ vibrationParam.setScale(scaleParam);
+
+ return vibrationParam;
+ }
+}
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index e83f03d155aa..b2922945aff9 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -22,16 +22,21 @@ filegroup {
genrule {
name: "wmtests.protologsrc",
srcs: [
+ ":protolog-impl",
":protolog-groups",
":wmtests-sources",
],
tools: ["protologtool"],
cmd: "$(location protologtool) transform-protolog-calls " +
"--protolog-class com.android.internal.protolog.common.ProtoLog " +
- "--protolog-impl-class com.android.internal.protolog.ProtoLogImpl " +
- "--protolog-cache-class 'com.android.server.wm.ProtoLogCache' " +
"--loggroups-class com.android.internal.protolog.ProtoLogGroup " +
"--loggroups-jar $(location :protolog-groups) " +
+ // Used for the ProtoLogIntegrationTest, where don't test decoding or writing to file
+ // so the parameters below are irrelevant.
+ "--viewer-config-file-path /some/unused/file/path.pb " +
+ "--legacy-viewer-config-file-path /some/unused/file/path.json.gz " +
+ "--legacy-output-file-path /some/unused/file/path.winscope " +
+ // END of irrelevant params.
"--output-srcjar $(out) " +
"$(locations :wmtests-sources)",
out: ["wmtests.protolog.srcjar"],
@@ -42,7 +47,7 @@ android_test {
// We only want this apk build for tests.
srcs: [
- ":wmtests.protologsrc",
+ ":wmtests-sources",
"src/**/*.aidl",
],
diff --git a/services/tests/wmtests/AndroidTest.xml b/services/tests/wmtests/AndroidTest.xml
index 46e87dceb8d4..2512ee592ef3 100644
--- a/services/tests/wmtests/AndroidTest.xml
+++ b/services/tests/wmtests/AndroidTest.xml
@@ -34,4 +34,11 @@
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="run-command" value="settings put secure immersive_mode_confirmations confirmed" />
</target_preparer>
+
+ <!-- Collect the dumped files for debugging -->
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="directory-keys" value="/storage/emulated/0/ScreenshotTests" />
+ <option name="clean-up" value="true" />
+ <option name="collect-on-run-ended-only" value="true" />
+ </metrics_collector>
</configuration>
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
index 6853c4cdea0e..e904eae00802 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
@@ -223,7 +223,11 @@ public class ShortcutLoggingTests extends ShortcutKeyTestBase {
KeyboardLogEvent.LAUNCH_DEFAULT_MAPS, KeyEvent.KEYCODE_M, META_ON},
{"Meta + S -> Launch Default Messaging App",
new int[]{META_KEY, KeyEvent.KEYCODE_S},
- KeyboardLogEvent.LAUNCH_DEFAULT_MESSAGING, KeyEvent.KEYCODE_S, META_ON}};
+ KeyboardLogEvent.LAUNCH_DEFAULT_MESSAGING, KeyEvent.KEYCODE_S, META_ON},
+ {"Meta + Ctrl + DPAD_DOWN -> Enter desktop mode",
+ new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_DOWN},
+ KeyboardLogEvent.DESKTOP_MODE, KeyEvent.KEYCODE_DPAD_DOWN,
+ META_ON | CTRL_ON}};
}
@Keep
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 31d6fa3e91f8..67c528cf40ae 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -769,7 +769,8 @@ public class ActivityRecordTests extends WindowTestsBase {
activity.setState(STOPPED, "Testing");
ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
- activity.addResultLocked(topActivity, "resultWho", 0, 0, new Intent());
+ activity.addResultLocked(topActivity, "resultWho", 0, 0, new Intent(),
+ /* callerToken */ null);
topActivity.finishing = true;
doReturn(TASK_FRAGMENT_VISIBILITY_VISIBLE).when(task).getVisibility(null);
@@ -1298,8 +1299,8 @@ public class ActivityRecordTests extends WindowTestsBase {
targetActivity.finishIfPossible(0, new Intent(), null, "test", false /* oomAdj */);
waitUntilHandlersIdle();
- verify(resultToActivity).sendResult(anyInt(), eq(null), anyInt(), anyInt(), any(), eq(null),
- anyBoolean());
+ verify(resultToActivity).sendResult(anyInt(), eq(null), anyInt(), anyInt(), any(), any(),
+ eq(null), anyBoolean());
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
index db241de246f6..0544b89d70fd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
@@ -23,7 +23,10 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.google.common.truth.Truth.assertThat;
+
import android.content.Intent;
+import android.os.Binder;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
@@ -48,6 +51,8 @@ public class ActivityStartControllerTests extends WindowTestsBase {
private Factory mFactory;
private ActivityStarter mStarter;
+ private static final String TEST_TYPE = "testType";
+
@Before
public void setUp() throws Exception {
mFactory = mock(Factory.class);
@@ -58,6 +63,28 @@ public class ActivityStartControllerTests extends WindowTestsBase {
}
/**
+ * Ensures that when an [Activity] is started in a [TaskFragment] the associated
+ * [ActivityStarter.Request] has the intent's resolved type correctly set.
+ */
+ @Test
+ public void testStartActivityInTaskFragment_setsActivityStarterRequestResolvedType() {
+ final Intent intent = new Intent();
+ intent.setType(TEST_TYPE);
+
+ mController.startActivityInTaskFragment(
+ mock(TaskFragment.class),
+ intent,
+ null /* activityOptions */,
+ null /* resultTo */ ,
+ Binder.getCallingPid(),
+ Binder.getCallingUid(),
+ null /* errorCallbackToken */
+ );
+
+ assertThat(mStarter.mRequest.resolvedType).isEqualTo(TEST_TYPE);
+ }
+
+ /**
* Ensures instances are recycled after execution.
*/
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 847c9d09b73a..6132ee3c89c4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -1561,7 +1561,7 @@ public class ActivityStarterTests extends WindowTestsBase {
.build();
final int result = starter.recycleTask(task, null, null, null,
- BalVerdict.ALLOW_BY_DEFAULT);
+ BalVerdict.ALLOW_PRIVILEGED);
assertThat(result == START_SUCCESS).isTrue();
assertThat(starter.mAddingToTask).isTrue();
}
@@ -1857,7 +1857,7 @@ public class ActivityStarterTests extends WindowTestsBase {
startActivityInner(starter, targetRecord, sourceRecord, null /* options */,
null /* inTask */, null /* inTaskFragment */);
verify(sourceRecord).sendResult(anyInt(), any(), anyInt(), eq(RESULT_CANCELED), any(),
- any());
+ any(), any());
}
@Test
@@ -2075,7 +2075,7 @@ public class ActivityStarterTests extends WindowTestsBase {
starter.startActivityInner(target, source, null /* voiceSession */,
null /* voiceInteractor */, 0 /* startFlags */,
options, inTask, inTaskFragment,
- BalVerdict.ALLOW_BY_DEFAULT,
+ BalVerdict.ALLOW_PRIVILEGED,
null /* intentGrants */, -1 /* realCallingUid */);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
index 203475156491..6b614fadba39 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
@@ -242,7 +242,8 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase {
verify(activity).getFilteredReferrer(eq(activity.launchedFromPackage));
activity.deliverNewIntentLocked(ActivityBuilder.DEFAULT_FAKE_UID,
- new Intent(), null /* intentGrants */, "other.package2");
+ new Intent(), null /* intentGrants */, "other.package2",
+ /* isShareIdentityEnabled */ false, /* userId */ -1, /* recipientAppId */ -1);
verify(activity).getFilteredReferrer(eq("other.package2"));
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
index c99fda9c06d3..ef131ac337b1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
@@ -16,6 +16,10 @@
package com.android.server.wm;
+import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_PENDING_INTENT;
+import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_PERMISSION;
+import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_VISIBLE_WINDOW;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -25,6 +29,7 @@ import static org.mockito.ArgumentMatchers.eq;
import android.app.ActivityOptions;
import android.app.AppOpsManager;
import android.app.BackgroundStartPrivileges;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManagerInternal;
@@ -51,7 +56,10 @@ import org.mockito.quality.Strictness;
import java.lang.reflect.Field;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import java.util.Optional;
/**
* Tests for the {@link ActivityStarter} class.
@@ -73,9 +81,12 @@ public class BackgroundActivityStartControllerTests {
private static final String REGULAR_PACKAGE_1 = "package.app1";
private static final String REGULAR_PACKAGE_2 = "package.app2";
- public @Rule MockitoRule mMockitoRule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
+ private static final Intent TEST_INTENT = new Intent()
+ .setComponent(new ComponentName("package.app3", "someClass"));
+
+ public @Rule MockitoRule mMockitoRule = MockitoJUnit.rule().strictness(Strictness.LENIENT);
- BackgroundActivityStartController mController;
+ TestableBackgroundActivityStartController mController;
@Mock
ActivityMetricsLogger mActivityMetricsLogger;
@Mock
@@ -112,6 +123,56 @@ public class BackgroundActivityStartControllerTests {
List<String> mShownToasts = new ArrayList<>();
List<BalAllowedLog> mBalAllowedLogs = new ArrayList<>();
+ class TestableBackgroundActivityStartController extends BackgroundActivityStartController {
+ Optional<BalVerdict> mCallerVerdict = Optional.empty();
+ Optional<BalVerdict> mRealCallerVerdict = Optional.empty();
+ Map<WindowProcessController, BalVerdict> mProcessVerdicts = new HashMap<>();
+
+ TestableBackgroundActivityStartController(ActivityTaskManagerService service,
+ ActivityTaskSupervisor supervisor) {
+ super(service, supervisor);
+ }
+
+ @Override
+ protected void showToast(String toastText) {
+ mShownToasts.add(toastText);
+ }
+
+ @Override
+ protected void writeBalAllowedLog(String activityName, int code,
+ BackgroundActivityStartController.BalState state) {
+ mBalAllowedLogs.add(new BalAllowedLog(activityName, code));
+ }
+
+ @Override
+ BalVerdict checkBackgroundActivityStartAllowedByCaller(BalState state) {
+ return mCallerVerdict.orElseGet(
+ () -> super.checkBackgroundActivityStartAllowedByCaller(state));
+ }
+
+ public void setCallerVerdict(BalVerdict verdict) {
+ this.mCallerVerdict = Optional.of(verdict);
+ }
+
+ @Override
+ BalVerdict checkBackgroundActivityStartAllowedBySender(BalState state) {
+ return mRealCallerVerdict.orElseGet(
+ () -> super.checkBackgroundActivityStartAllowedBySender(state));
+ }
+
+ public void setRealCallerVerdict(BalVerdict verdict) {
+ this.mRealCallerVerdict = Optional.of(verdict);
+ }
+
+ @Override
+ BalVerdict checkProcessAllowsBal(WindowProcessController app, BalState state) {
+ if (mProcessVerdicts.containsKey(app)) {
+ return mProcessVerdicts.get(app);
+ }
+ return super.checkProcessAllowsBal(app, state);
+ }
+ }
+
@Before
public void setUp() throws Exception {
// wire objects
@@ -127,18 +188,10 @@ public class BackgroundActivityStartControllerTests {
//Mockito.when(mSupervisor.getBackgroundActivityLaunchController()).thenReturn(mController);
setViaReflection(mSupervisor, "mRecentTasks", mRecentTasks);
- mController = new BackgroundActivityStartController(mService, mSupervisor) {
- @Override
- protected void showToast(String toastText) {
- mShownToasts.add(toastText);
- }
+ mController = new TestableBackgroundActivityStartController(mService, mSupervisor);
- @Override
- protected void writeBalAllowedLog(String activityName, int code,
- BackgroundActivityStartController.BalState state) {
- mBalAllowedLogs.add(new BalAllowedLog(activityName, code));
- }
- };
+ // nicer toString
+ Mockito.when(mPendingIntentRecord.toString()).thenReturn("PendingIntentRecord");
// safe defaults
Mockito.when(mAppOpsManager.checkOpNoThrow(
@@ -146,7 +199,6 @@ public class BackgroundActivityStartControllerTests {
anyInt(), anyString())).thenReturn(AppOpsManager.MODE_DEFAULT);
Mockito.when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt())).thenReturn(
BalVerdict.BLOCK);
-
}
private void setViaReflection(Object o, String property, Object value) {
@@ -175,7 +227,7 @@ public class BackgroundActivityStartControllerTests {
int realCallingPid = NO_PID;
PendingIntentRecord originatingPendingIntent = null;
BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
- Intent intent = new Intent();
+ Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = ActivityOptions.makeBasic();
// call
@@ -186,17 +238,46 @@ public class BackgroundActivityStartControllerTests {
// assertions
assertThat(verdict.getCode()).isEqualTo(BackgroundActivityStartController.BAL_BLOCK);
+ assertThat(mBalAllowedLogs).isEmpty(); // not allowed
+ }
- assertThat(mBalAllowedLogs).isEmpty();
+ // Tests for BackgroundActivityStartController.checkBackgroundActivityStart
+
+ @Test
+ public void testRegularActivityStart_notAllowed_isBlocked() {
+ // setup state
+ mController.setCallerVerdict(BalVerdict.BLOCK);
+ mController.setRealCallerVerdict(BalVerdict.BLOCK);
+
+ // prepare call
+ int callingUid = REGULAR_UID_1;
+ int callingPid = REGULAR_PID_1;
+ final String callingPackage = REGULAR_PACKAGE_1;
+ int realCallingUid = NO_UID;
+ int realCallingPid = NO_PID;
+ PendingIntentRecord originatingPendingIntent = null;
+ BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ Intent intent = TEST_INTENT;
+ ActivityOptions checkedOptions = ActivityOptions.makeBasic();
+
+ // call
+ BalVerdict verdict = mController.checkBackgroundActivityStart(callingUid, callingPid,
+ callingPackage, realCallingUid, realCallingPid, mCallerApp,
+ originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ checkedOptions);
+
+ // assertions
+ assertThat(verdict).isEqualTo(BalVerdict.BLOCK);
+ assertThat(mBalAllowedLogs).isEmpty(); // not allowed
}
@Test
- public void testRegularActivityStart_allowedBLPC_isAllowed() {
+ public void testRegularActivityStart_allowedByCaller_isAllowed() {
// setup state
- BalVerdict blpcVerdict = new BalVerdict(
- BackgroundActivityStartController.BAL_ALLOW_PERMISSION, true, "Allowed by BLPC");
- Mockito.when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt())).thenReturn(
- blpcVerdict);
+ BalVerdict callerVerdict = new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, false,
+ "CallerIsVisible");
+ mController.setCallerVerdict(callerVerdict);
+ mController.setRealCallerVerdict(BalVerdict.BLOCK);
// prepare call
int callingUid = REGULAR_UID_1;
@@ -206,7 +287,7 @@ public class BackgroundActivityStartControllerTests {
int realCallingPid = NO_PID;
PendingIntentRecord originatingPendingIntent = null;
BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
- Intent intent = new Intent();
+ Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = ActivityOptions.makeBasic();
// call
@@ -216,28 +297,61 @@ public class BackgroundActivityStartControllerTests {
checkedOptions);
// assertions
- assertThat(verdict).isEqualTo(blpcVerdict);
+ assertThat(verdict).isEqualTo(callerVerdict);
+ assertThat(mBalAllowedLogs).isEmpty(); // non-critical exception
+ }
+
+ @Test
+ public void testRegularActivityStart_allowedByRealCaller_isAllowed() {
+ // setup state
+ BalVerdict realCallerVerdict = new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, false,
+ "RealCallerIsVisible");
+ mController.setCallerVerdict(BalVerdict.BLOCK);
+ mController.setRealCallerVerdict(realCallerVerdict);
+
+ // prepare call
+ int callingUid = REGULAR_UID_1;
+ int callingPid = REGULAR_PID_1;
+ final String callingPackage = REGULAR_PACKAGE_1;
+ int realCallingUid = NO_UID;
+ int realCallingPid = NO_PID;
+ PendingIntentRecord originatingPendingIntent = null;
+ BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ Intent intent = TEST_INTENT;
+ ActivityOptions checkedOptions = ActivityOptions.makeBasic();
+
+ // call
+ BalVerdict verdict = mController.checkBackgroundActivityStart(callingUid, callingPid,
+ callingPackage, realCallingUid, realCallingPid, mCallerApp,
+ originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ checkedOptions);
+
+ // assertions
+ assertThat(verdict).isEqualTo(realCallerVerdict);
assertThat(mBalAllowedLogs).containsExactly(
- new BalAllowedLog("", BackgroundActivityStartController.BAL_ALLOW_PERMISSION));
+ new BalAllowedLog("package.app3/someClass", realCallerVerdict.getCode()));
+ // TODO questionable log (should we only log PIs?)
}
@Test
- public void testRegularActivityStart_allowedByCallerBLPC_isAllowed() {
+ public void testRegularActivityStart_allowedByCallerAndRealCaller_returnsCallerVerdict() {
// setup state
- BalVerdict blpcVerdict = new BalVerdict(
- BackgroundActivityStartController.BAL_ALLOW_PERMISSION, true, "Allowed by BLPC");
- Mockito.when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt())).thenReturn(
- blpcVerdict);
+ BalVerdict callerVerdict =
+ new BalVerdict(BAL_ALLOW_PERMISSION, false, "CallerHasPermission");
+ BalVerdict realCallerVerdict =
+ new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, false, "RealCallerIsVisible");
+ mController.setCallerVerdict(callerVerdict);
+ mController.setRealCallerVerdict(realCallerVerdict);
// prepare call
int callingUid = REGULAR_UID_1;
int callingPid = REGULAR_PID_1;
final String callingPackage = REGULAR_PACKAGE_1;
- int realCallingUid = REGULAR_UID_2;
- int realCallingPid = REGULAR_PID_2;
- PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
+ int realCallingUid = NO_UID;
+ int realCallingPid = NO_PID;
+ PendingIntentRecord originatingPendingIntent = null;
BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
- Intent intent = new Intent();
+ Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = ActivityOptions.makeBasic();
// call
@@ -247,8 +361,248 @@ public class BackgroundActivityStartControllerTests {
checkedOptions);
// assertions
- assertThat(verdict).isEqualTo(blpcVerdict);
+ assertThat(verdict).isEqualTo(callerVerdict);
+ assertThat(mBalAllowedLogs).containsExactly(new BalAllowedLog("", callerVerdict.getCode()));
+ }
+
+ @Test
+ public void testPendingIntent_allowedByCallerAndRealCallerButOptOut_isBlocked() {
+ // setup state
+ BalVerdict callerVerdict =
+ new BalVerdict(BAL_ALLOW_PERMISSION, false, "CallerhasPermission");
+ BalVerdict realCallerVerdict =
+ new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, false, "RealCallerIsVisible");
+ mController.setCallerVerdict(callerVerdict);
+ mController.setRealCallerVerdict(realCallerVerdict);
+
+ // prepare call
+ int callingUid = REGULAR_UID_1;
+ int callingPid = REGULAR_PID_1;
+ final String callingPackage = REGULAR_PACKAGE_1;
+ int realCallingUid = NO_UID;
+ int realCallingPid = NO_PID;
+ PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
+ BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ Intent intent = TEST_INTENT;
+ ActivityOptions checkedOptions = ActivityOptions.makeBasic()
+ .setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED)
+ .setPendingIntentCreatorBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED);
+
+ // call
+ BalVerdict verdict = mController.checkBackgroundActivityStart(callingUid, callingPid,
+ callingPackage, realCallingUid, realCallingPid, mCallerApp,
+ originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ checkedOptions);
+
+ // assertions
+ assertThat(verdict).isEqualTo(BalVerdict.BLOCK);
+ assertThat(mBalAllowedLogs).isEmpty();
+ }
+
+ @Test
+ public void testPendingIntent_allowedByCallerAndOptIn_isAllowed() {
+ // setup state
+ BalVerdict callerVerdict =
+ new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, false, "CallerIsVisible");
+ mController.setCallerVerdict(callerVerdict);
+ mController.setRealCallerVerdict(BalVerdict.BLOCK);
+
+ // prepare call
+ int callingUid = REGULAR_UID_1;
+ int callingPid = REGULAR_PID_1;
+ final String callingPackage = REGULAR_PACKAGE_1;
+ int realCallingUid = NO_UID;
+ int realCallingPid = NO_PID;
+ PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
+ BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ Intent intent = TEST_INTENT;
+ ActivityOptions checkedOptions = ActivityOptions.makeBasic()
+ .setPendingIntentCreatorBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
+
+ // call
+ BalVerdict verdict = mController.checkBackgroundActivityStart(callingUid, callingPid,
+ callingPackage, realCallingUid, realCallingPid, mCallerApp,
+ originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ checkedOptions);
+
+ // assertions
+ assertThat(verdict).isEqualTo(callerVerdict);
+ assertThat(mBalAllowedLogs).isEmpty();
+ }
+
+ @Test
+ public void testPendingIntent_allowedByRealCallerAndOptIn_isAllowed() {
+ // setup state
+ BalVerdict realCallerVerdict =
+ new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, false, "RealCallerIsVisible");
+ mController.setCallerVerdict(BalVerdict.BLOCK);
+ mController.setRealCallerVerdict(realCallerVerdict);
+
+ // prepare call
+ int callingUid = REGULAR_UID_1;
+ int callingPid = REGULAR_PID_1;
+ final String callingPackage = REGULAR_PACKAGE_1;
+ int realCallingUid = NO_UID;
+ int realCallingPid = NO_PID;
+ PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
+ BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ Intent intent = TEST_INTENT;
+ ActivityOptions checkedOptions = ActivityOptions.makeBasic()
+ .setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
+
+ // call
+ BalVerdict verdict = mController.checkBackgroundActivityStart(callingUid, callingPid,
+ callingPackage, realCallingUid, realCallingPid, mCallerApp,
+ originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ checkedOptions);
+
+ // assertions
+ assertThat(verdict).isEqualTo(realCallerVerdict);
assertThat(mBalAllowedLogs).containsExactly(
- new BalAllowedLog("", BackgroundActivityStartController.BAL_ALLOW_PERMISSION));
+ new BalAllowedLog("package.app3/someClass", BAL_ALLOW_PENDING_INTENT));
+
+ }
+
+ // Tests for BackgroundActivityStartController.checkBackgroundActivityStartAllowedByCaller
+
+ // Tests for BackgroundActivityStartController.checkBackgroundActivityStartAllowedBySender
+
+ // Tests for BalState
+
+ @Test
+ public void testBalState_regularStart_isAutoOptIn() {
+ // setup state
+
+ // prepare call
+ int callingUid = REGULAR_UID_1;
+ int callingPid = REGULAR_PID_1;
+ final String callingPackage = REGULAR_PACKAGE_1;
+ int realCallingUid = NO_UID;
+ int realCallingPid = NO_PID;
+ PendingIntentRecord originatingPendingIntent = null;
+ BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ Intent intent = TEST_INTENT;
+ ActivityOptions checkedOptions = ActivityOptions.makeBasic();
+ WindowProcessController callerApp = mCallerApp;
+ ActivityRecord resultRecord = null;
+
+ // call
+ BackgroundActivityStartController.BalState balState = mController
+ .new BalState(callingUid, callingPid, callingPackage, realCallingUid,
+ realCallingPid, callerApp, originatingPendingIntent, forcedBalByPiSender,
+ resultRecord, intent, checkedOptions);
+
+ // assertions
+ assertThat(balState.mAutoOptInReason).isEqualTo("notPendingIntent");
+ assertThat(balState.mBalAllowedByPiCreator).isEqualTo(BackgroundStartPrivileges.ALLOW_BAL);
+ assertThat(balState.mBalAllowedByPiSender).isEqualTo(BackgroundStartPrivileges.ALLOW_BAL);
+ assertThat(balState.callerExplicitOptInOrAutoOptIn()).isTrue();
+ assertThat(balState.callerExplicitOptInOrOut()).isFalse();
+ assertThat(balState.realCallerExplicitOptInOrAutoOptIn()).isTrue();
+ assertThat(balState.realCallerExplicitOptInOrOut()).isFalse();
+ assertThat(balState.toString()).isEqualTo(
+ "[callingPackage: package.app1; callingPackageTargetSdk: -1; callingUid: 10001; "
+ + "callingPid: 11001; appSwitchState: 0; callingUidHasAnyVisibleWindow: "
+ + "false; callingUidProcState: NONEXISTENT; "
+ + "isCallingUidPersistentSystemProcess: false; forcedBalByPiSender: BSP"
+ + ".NONE; intent: Intent { cmp=package.app3/someClass }; callerApp: "
+ + "mCallerApp; inVisibleTask: false; balAllowedByPiCreator: BSP"
+ + ".ALLOW_BAL; balAllowedByPiCreatorWithHardening: BSP.ALLOW_BAL; "
+ + "resultIfPiCreatorAllowsBal: null; hasRealCaller: true; "
+ + "isCallForResult: false; isPendingIntent: false; autoOptInReason: "
+ + "notPendingIntent; realCallingPackage: uid=1[debugOnly]; "
+ + "realCallingPackageTargetSdk: -1; realCallingUid: 1; realCallingPid: 1;"
+ + " realCallingUidHasAnyVisibleWindow: false; realCallingUidProcState: "
+ + "NONEXISTENT; isRealCallingUidPersistentSystemProcess: false; "
+ + "originatingPendingIntent: null; realCallerApp: null; "
+ + "balAllowedByPiSender: BSP.ALLOW_BAL; resultIfPiSenderAllowsBal: null]");
+ }
+
+ @Test
+ public void testBalState_pendingIntentForResult_isOptedInForSenderOnly() {
+ // setup state
+ Mockito.when(mPendingIntentRecord.toString()).thenReturn("PendingIntentRecord");
+
+ // prepare call
+ int callingUid = REGULAR_UID_1;
+ int callingPid = REGULAR_PID_1;
+ final String callingPackage = REGULAR_PACKAGE_1;
+ int realCallingUid = NO_UID;
+ int realCallingPid = NO_PID;
+ PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
+ BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ Intent intent = TEST_INTENT;
+ ActivityOptions checkedOptions = ActivityOptions.makeBasic();
+ WindowProcessController callerApp = mCallerApp;
+ ActivityRecord resultRecord = mResultRecord;
+
+ // call
+ BackgroundActivityStartController.BalState balState = mController
+ .new BalState(callingUid, callingPid, callingPackage, realCallingUid,
+ realCallingPid, callerApp, originatingPendingIntent, forcedBalByPiSender,
+ resultRecord, intent, checkedOptions);
+
+ // assertions
+ assertThat(balState.mAutoOptInReason).isEqualTo("callForResult");
+ assertThat(balState.mBalAllowedByPiCreator).isEqualTo(BackgroundStartPrivileges.NONE);
+ assertThat(balState.mBalAllowedByPiSender).isEqualTo(BackgroundStartPrivileges.ALLOW_BAL);
+ assertThat(balState.callerExplicitOptInOrAutoOptIn()).isFalse();
+ assertThat(balState.callerExplicitOptInOrOut()).isFalse();
+ assertThat(balState.realCallerExplicitOptInOrAutoOptIn()).isTrue();
+ assertThat(balState.realCallerExplicitOptInOrOut()).isFalse();
+ }
+
+ @Test
+ public void testBalState_pendingIntentWithDefaults_isOptedOut() {
+ // setup state
+
+ // prepare call
+ int callingUid = REGULAR_UID_1;
+ int callingPid = REGULAR_PID_1;
+ final String callingPackage = REGULAR_PACKAGE_1;
+ int realCallingUid = NO_UID;
+ int realCallingPid = NO_PID;
+ PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
+ BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ Intent intent = TEST_INTENT;
+ ActivityOptions checkedOptions = ActivityOptions.makeBasic();
+ WindowProcessController callerApp = mCallerApp;
+ ActivityRecord resultRecord = null;
+
+ // call
+ BackgroundActivityStartController.BalState balState = mController
+ .new BalState(callingUid, callingPid, callingPackage, realCallingUid,
+ realCallingPid, callerApp, originatingPendingIntent, forcedBalByPiSender,
+ resultRecord, intent, checkedOptions);
+
+ // assertions
+ assertThat(balState.mAutoOptInReason).isNull();
+ assertThat(balState.mBalAllowedByPiCreator).isEqualTo(BackgroundStartPrivileges.NONE);
+ assertThat(balState.mBalAllowedByPiSender).isEqualTo(BackgroundStartPrivileges.ALLOW_FGS);
+ assertThat(balState.isPendingIntent()).isTrue();
+ assertThat(balState.callerExplicitOptInOrAutoOptIn()).isFalse();
+ assertThat(balState.callerExplicitOptInOrOut()).isFalse();
+ assertThat(balState.realCallerExplicitOptInOrAutoOptIn()).isFalse();
+ assertThat(balState.realCallerExplicitOptInOrOut()).isFalse();
+ assertThat(balState.toString()).isEqualTo(
+ "[callingPackage: package.app1; callingPackageTargetSdk: -1; callingUid: 10001; "
+ + "callingPid: 11001; appSwitchState: 0; callingUidHasAnyVisibleWindow: "
+ + "false; callingUidProcState: NONEXISTENT; "
+ + "isCallingUidPersistentSystemProcess: false; forcedBalByPiSender: BSP"
+ + ".NONE; intent: Intent { cmp=package.app3/someClass }; callerApp: "
+ + "mCallerApp; inVisibleTask: false; balAllowedByPiCreator: BSP"
+ + ".NONE; balAllowedByPiCreatorWithHardening: BSP.NONE; "
+ + "resultIfPiCreatorAllowsBal: null; hasRealCaller: true; "
+ + "isCallForResult: false; isPendingIntent: true; autoOptInReason: "
+ + "null; realCallingPackage: uid=1[debugOnly]; "
+ + "realCallingPackageTargetSdk: -1; realCallingUid: 1; realCallingPid: 1;"
+ + " realCallingUidHasAnyVisibleWindow: false; realCallingUidProcState: "
+ + "NONEXISTENT; isRealCallingUidPersistentSystemProcess: false; "
+ + "originatingPendingIntent: PendingIntentRecord; realCallerApp: null; "
+ + "balAllowedByPiSender: BSP.ALLOW_FGS; resultIfPiSenderAllowsBal: null]");
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
index dc4e47dfea30..ef36bff91a67 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
@@ -20,8 +20,8 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.util.DisplayMetrics.DENSITY_DEFAULT;
+import static com.android.server.wm.DesktopModeLaunchParamsModifier.DESKTOP_MODE_INITIAL_BOUNDS_SCALE;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_DISPLAY;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_DONE;
@@ -30,6 +30,7 @@ import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
+import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
@@ -113,9 +114,14 @@ public class DesktopModeLaunchParamsModifierTests extends WindowTestsBase {
public void testUsesDefaultBounds() {
final Task task = new TaskBuilder(mSupervisor).setActivityType(
ACTIVITY_TYPE_STANDARD).build();
+ final int displayHeight = 1600;
+ final int displayWidth = 2560;
+ task.getDisplayArea().setBounds(new Rect(0, 0, displayWidth, displayHeight));
+ final int desiredWidth = (int) (displayWidth * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+ final int desiredHeight = (int) (displayHeight * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
assertEquals(RESULT_DONE, new CalculateRequestBuilder().setTask(task).calculate());
- assertEquals(dpiToPx(task, 840), mResult.mBounds.width());
- assertEquals(dpiToPx(task, 630), mResult.mBounds.height());
+ assertEquals(desiredWidth, mResult.mBounds.width());
+ assertEquals(desiredHeight, mResult.mBounds.height());
}
@Test
@@ -131,11 +137,6 @@ public class DesktopModeLaunchParamsModifierTests extends WindowTestsBase {
assertEquals(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode);
}
- private int dpiToPx(Task task, int dpi) {
- float density = (float) task.getConfiguration().densityDpi / DENSITY_DEFAULT;
- return (int) (dpi * density + 0.5f);
- }
-
private class CalculateRequestBuilder {
private Task mTask;
private int mPhase = PHASE_BOUNDS;
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index 5d14334aaf69..5965fae74dcc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -39,6 +39,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM;
import static org.junit.Assert.assertEquals;
@@ -388,6 +389,24 @@ public class DisplayPolicyTests extends WindowTestsBase {
// The current insets are restored from cache directly.
assertEquals(prevConfigFrame, displayPolicy.getDecorInsetsInfo(info.rotation,
info.logicalWidth, info.logicalHeight).mConfigFrame);
+
+ // If screen is not fully turned on, then the cache should be preserved.
+ displayPolicy.screenTurnedOff();
+ final TransitionController transitionController = mDisplayContent.mTransitionController;
+ spyOn(transitionController);
+ doReturn(true).when(transitionController).isCollecting();
+ doReturn(Integer.MAX_VALUE).when(transitionController).getCollectingTransitionId();
+ // Make CachedDecorInsets.canPreserve return false.
+ displayPolicy.physicalDisplayUpdated();
+ assertFalse(displayPolicy.shouldKeepCurrentDecorInsets());
+ displayPolicy.getDecorInsetsInfo(info.rotation, info.logicalWidth, info.logicalHeight)
+ .mConfigFrame.offset(1, 1);
+ // Even if CachedDecorInsets.canPreserve returns false, the cache won't be cleared.
+ displayPolicy.updateDecorInsetsInfo();
+ // Successful to restore from cache.
+ displayPolicy.updateCachedDecorInsets();
+ assertEquals(prevConfigFrame, displayPolicy.getDecorInsetsInfo(info.rotation,
+ info.logicalWidth, info.logicalHeight).mConfigFrame);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java
index c404c77c8550..bb5887d12f4b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowPolicyControllerTests.java
@@ -187,7 +187,7 @@ public class DisplayWindowPolicyControllerTests extends WindowTestsBase {
/* options */null,
/* inTask */null,
/* inTaskFragment */ null,
- BalVerdict.ALLOW_BY_DEFAULT,
+ BalVerdict.ALLOW_PRIVILEGED,
/* intentGrants */null,
/* realCaiingUid */ -1);
@@ -217,7 +217,7 @@ public class DisplayWindowPolicyControllerTests extends WindowTestsBase {
/* options= */null,
/* inTask= */null,
/* inTaskFragment= */ null,
- BalVerdict.ALLOW_BY_DEFAULT,
+ BalVerdict.ALLOW_PRIVILEGED,
/* intentGrants= */null,
/* realCaiingUid */ -1);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 9e2b1eccc3b2..4c32a586ae78 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -67,7 +67,7 @@ import android.view.SurfaceSession;
import android.view.View;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
-import android.window.IUnhandledDragListener;
+import android.window.IGlobalDragListener;
import androidx.test.filters.SmallTest;
@@ -177,8 +177,8 @@ public class DragDropControllerTests extends WindowTestsBase {
TEST_PID, TEST_UID);
mWindow = createDropTargetWindow("Drag test window", 0);
doReturn(mWindow).when(mDisplayContent).getTouchableWinAtPointLocked(0, 0);
- when(mWm.mInputManager.transferTouchFocus(any(InputChannel.class),
- any(InputChannel.class), any(boolean.class))).thenReturn(true);
+ when(mWm.mInputManager.startDragAndDrop(any(InputChannel.class),
+ any(InputChannel.class))).thenReturn(true);
mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
}
@@ -544,9 +544,9 @@ public class DragDropControllerTests extends WindowTestsBase {
public void testUnhandledDragListenerNotCalledForNormalDrags() throws RemoteException {
assumeTrue(com.android.window.flags.Flags.delegateUnhandledDrags());
- final IUnhandledDragListener listener = mock(IUnhandledDragListener.class);
+ final IGlobalDragListener listener = mock(IGlobalDragListener.class);
doReturn(mock(Binder.class)).when(listener).asBinder();
- mTarget.setUnhandledDragListener(listener);
+ mTarget.setGlobalDragListener(listener);
doDragAndDrop(0, ClipData.newPlainText("label", "Test"), 0, 0);
verify(listener, times(0)).onUnhandledDrop(any(), any());
}
@@ -555,9 +555,9 @@ public class DragDropControllerTests extends WindowTestsBase {
public void testUnhandledDragListenerReceivesUnhandledDropOverWindow() {
assumeTrue(com.android.window.flags.Flags.delegateUnhandledDrags());
- final IUnhandledDragListener listener = mock(IUnhandledDragListener.class);
+ final IGlobalDragListener listener = mock(IGlobalDragListener.class);
doReturn(mock(Binder.class)).when(listener).asBinder();
- mTarget.setUnhandledDragListener(listener);
+ mTarget.setGlobalDragListener(listener);
final int invalidXY = 100_000;
startDrag(View.DRAG_FLAG_GLOBAL, ClipData.newPlainText("label", "Test"), () -> {
// Notify the unhandled drag listener
@@ -578,9 +578,9 @@ public class DragDropControllerTests extends WindowTestsBase {
public void testUnhandledDragListenerReceivesUnhandledDropOverNoValidWindow() {
assumeTrue(com.android.window.flags.Flags.delegateUnhandledDrags());
- final IUnhandledDragListener listener = mock(IUnhandledDragListener.class);
+ final IGlobalDragListener listener = mock(IGlobalDragListener.class);
doReturn(mock(Binder.class)).when(listener).asBinder();
- mTarget.setUnhandledDragListener(listener);
+ mTarget.setGlobalDragListener(listener);
final int invalidXY = 100_000;
startDrag(View.DRAG_FLAG_GLOBAL, ClipData.newPlainText("label", "Test"), () -> {
// Notify the unhandled drag listener
@@ -600,9 +600,9 @@ public class DragDropControllerTests extends WindowTestsBase {
public void testUnhandledDragListenerCallbackTimeout() {
assumeTrue(com.android.window.flags.Flags.delegateUnhandledDrags());
- final IUnhandledDragListener listener = mock(IUnhandledDragListener.class);
+ final IGlobalDragListener listener = mock(IGlobalDragListener.class);
doReturn(mock(Binder.class)).when(listener).asBinder();
- mTarget.setUnhandledDragListener(listener);
+ mTarget.setGlobalDragListener(listener);
final int invalidXY = 100_000;
startDrag(View.DRAG_FLAG_GLOBAL, ClipData.newPlainText("label", "Test"), () -> {
// Notify the unhandled drag listener
@@ -641,8 +641,8 @@ public class DragDropControllerTests extends WindowTestsBase {
.setFormat(PixelFormat.TRANSLUCENT)
.build();
- assertTrue(mWm.mInputManager.transferTouchFocus(new InputChannel(),
- new InputChannel(), true /* isDragDrop */));
+ assertTrue(mWm.mInputManager.startDragAndDrop(new InputChannel(),
+ new InputChannel()));
mToken = mTarget.performDrag(TEST_PID, 0, mWindow.mClient,
flag, surface, 0, 0, 0, 0, 0, 0, 0, data);
assertNotNull(mToken);
diff --git a/services/tests/wmtests/src/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncherTest.java b/services/tests/wmtests/src/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncherTest.java
index 08e63963c88f..402b704c1681 100644
--- a/services/tests/wmtests/src/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncherTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncherTest.java
@@ -16,33 +16,23 @@
package com.android.server.wm;
-import static android.view.WindowManager.TRANSIT_CHANGE;
-
+import static com.android.internal.R.bool.config_unfoldTransitionEnabled;
+import static com.android.server.wm.DeviceStateController.DeviceState.REAR;
import static com.android.server.wm.DeviceStateController.DeviceState.FOLDED;
import static com.android.server.wm.DeviceStateController.DeviceState.HALF_FOLDED;
import static com.android.server.wm.DeviceStateController.DeviceState.OPEN;
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;
import android.animation.ValueAnimator;
-import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
-import android.window.TransitionRequestInfo.DisplayChange;
-
-import static com.android.internal.R.bool.config_unfoldTransitionEnabled;
-import static com.android.server.wm.DeviceStateController.DeviceState.REAR;
import androidx.test.filters.SmallTest;
@@ -50,7 +40,6 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -62,20 +51,19 @@ import org.mockito.MockitoAnnotations;
*/
@SmallTest
@Presubmit
-public class PhysicalDisplaySwitchTransitionLauncherTest {
+@RunWith(WindowTestRunner.class)
+public class PhysicalDisplaySwitchTransitionLauncherTest extends WindowTestsBase {
@Mock
- DisplayContent mDisplayContent;
- @Mock
Context mContext;
@Mock
Resources mResources;
@Mock
- ActivityTaskManagerService mActivityTaskManagerService;
- @Mock
BLASTSyncEngine mSyncEngine;
- @Mock
+
+ WindowTestsBase.TestTransitionPlayer mPlayer;
TransitionController mTransitionController;
+ DisplayContent mDisplayContent;
private PhysicalDisplaySwitchTransitionLauncher mTarget;
private float mOriginalAnimationScale;
@@ -83,9 +71,14 @@ public class PhysicalDisplaySwitchTransitionLauncherTest {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mTransitionController = new WindowTestsBase.TestTransitionController(mAtm);
+ mTransitionController.setSyncEngine(mSyncEngine);
+ mPlayer = new WindowTestsBase.TestTransitionPlayer(
+ mTransitionController, mAtm.mWindowOrganizerController);
when(mContext.getResources()).thenReturn(mResources);
- mTarget = new PhysicalDisplaySwitchTransitionLauncher(mDisplayContent,
- mActivityTaskManagerService, mContext, mTransitionController);
+ mDisplayContent = new TestDisplayContent.Builder(mAtm, 100, 150).build();
+ mTarget = new PhysicalDisplaySwitchTransitionLauncher(mDisplayContent, mAtm, mContext,
+ mTransitionController);
mOriginalAnimationScale = ValueAnimator.getDurationScale();
}
@@ -100,24 +93,23 @@ public class PhysicalDisplaySwitchTransitionLauncherTest {
mTarget.foldStateChanged(FOLDED);
mTarget.foldStateChanged(OPEN);
+ final Rect origBounds = new Rect();
+ mDisplayContent.getBounds(origBounds);
+ origBounds.offsetTo(0, 0);
mTarget.requestDisplaySwitchTransitionIfNeeded(
- /* displayId= */ 123,
- /* oldDisplayWidth= */ 100,
- /* oldDisplayHeight= */ 150,
+ mDisplayContent.getDisplayId(),
+ origBounds.width(),
+ origBounds.height(),
/* newDisplayWidth= */ 200,
/* newDisplayHeight= */ 250
);
- ArgumentCaptor<DisplayChange> displayChangeArgumentCaptor =
- ArgumentCaptor.forClass(DisplayChange.class);
- verify(mTransitionController).requestTransitionIfNeeded(eq(TRANSIT_CHANGE), /* flags= */
- eq(0), eq(mDisplayContent), eq(mDisplayContent), /* remoteTransition= */ isNull(),
- displayChangeArgumentCaptor.capture());
- assertThat(displayChangeArgumentCaptor.getValue().getDisplayId()).isEqualTo(123);
- assertThat(displayChangeArgumentCaptor.getValue().getStartAbsBounds()).isEqualTo(
- new Rect(0, 0, 100, 150));
- assertThat(displayChangeArgumentCaptor.getValue().getEndAbsBounds()).isEqualTo(
- new Rect(0, 0, 200, 250));
+ assertNotNull(mPlayer.mLastRequest);
+ assertEquals(mDisplayContent.getDisplayId(),
+ mPlayer.mLastRequest.getDisplayChange().getDisplayId());
+ assertEquals(origBounds, mPlayer.mLastRequest.getDisplayChange().getStartAbsBounds());
+ assertEquals(new Rect(0, 0, 200, 250),
+ mPlayer.mLastRequest.getDisplayChange().getEndAbsBounds());
}
@Test
@@ -148,7 +140,7 @@ public class PhysicalDisplaySwitchTransitionLauncherTest {
mTarget.foldStateChanged(FOLDED);
mTarget.foldStateChanged(OPEN);
requestDisplaySwitch();
- clearInvocations(mTransitionController);
+ mPlayer.mLastRequest = null;
requestDisplaySwitch();
@@ -220,7 +212,6 @@ public class PhysicalDisplaySwitchTransitionLauncherTest {
@Test
public void testDisplaySwitchAfterUnfolding_otherCollectingTransition_collectsDisplaySwitch() {
- givenCollectingTransition(createTransition(TRANSIT_CHANGE));
givenAllAnimationsEnabled();
mTarget.foldStateChanged(FOLDED);
@@ -228,7 +219,8 @@ public class PhysicalDisplaySwitchTransitionLauncherTest {
requestDisplaySwitch();
// Collects to the current transition
- verify(mTransitionController).collect(mDisplayContent);
+ assertTrue(mTransitionController.getCollectingTransition().mParticipants.contains(
+ mDisplayContent));
}
@@ -245,20 +237,18 @@ public class PhysicalDisplaySwitchTransitionLauncherTest {
}
private void assertTransitionRequested() {
- verify(mTransitionController).requestTransitionIfNeeded(anyInt(), anyInt(), any(), any(),
- any(), any());
+ assertNotNull(mPlayer.mLastRequest);
}
private void assertTransitionNotRequested() {
- verify(mTransitionController, never()).requestTransitionIfNeeded(anyInt(), anyInt(), any(),
- any(), any(), any());
+ assertNull(mPlayer.mLastRequest);
}
private void requestDisplaySwitch() {
mTarget.requestDisplaySwitchTransitionIfNeeded(
- /* displayId= */ 123,
- /* oldDisplayWidth= */ 100,
- /* oldDisplayHeight= */ 150,
+ mDisplayContent.getDisplayId(),
+ mDisplayContent.getBounds().width(),
+ mDisplayContent.getBounds().height(),
/* newDisplayWidth= */ 200,
/* newDisplayHeight= */ 250
);
@@ -280,16 +270,11 @@ public class PhysicalDisplaySwitchTransitionLauncherTest {
}
private void givenShellTransitionsEnabled(boolean enabled) {
- when(mTransitionController.isShellTransitionsEnabled()).thenReturn(enabled);
- }
-
- private void givenCollectingTransition(@Nullable Transition transition) {
- when(mTransitionController.isCollecting()).thenReturn(transition != null);
- when(mTransitionController.getCollectingTransition()).thenReturn(transition);
- }
-
- private Transition createTransition(int type) {
- return new Transition(type, /* flags= */ 0, mTransitionController, mSyncEngine);
+ if (enabled) {
+ mTransitionController.registerTransitionPlayer(mPlayer, null /* proc */);
+ } else {
+ mTransitionController.detachPlayer();
+ }
}
private void givenDisplayContentHasContent(boolean hasContent) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ProtoLogIntegrationTest.java b/services/tests/wmtests/src/com/android/server/wm/ProtoLogIntegrationTest.java
index af0d32c764a4..c5bf78bb60b5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ProtoLogIntegrationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ProtoLogIntegrationTest.java
@@ -27,6 +27,8 @@ import androidx.test.filters.SmallTest;
import com.android.internal.protolog.ProtoLogGroup;
import com.android.internal.protolog.ProtoLogImpl;
+import com.android.internal.protolog.common.IProtoLog;
+import com.android.internal.protolog.common.LogLevel;
import com.android.internal.protolog.common.ProtoLog;
import org.junit.After;
@@ -47,9 +49,9 @@ public class ProtoLogIntegrationTest {
@Ignore("b/163095037")
@Test
public void testProtoLogToolIntegration() {
- ProtoLogImpl mockedProtoLog = mock(ProtoLogImpl.class);
+ IProtoLog mockedProtoLog = mock(IProtoLog.class);
runWith(mockedProtoLog, this::testProtoLog);
- verify(mockedProtoLog).log(eq(ProtoLogImpl.LogLevel.ERROR), eq(ProtoLogGroup.TEST_GROUP),
+ verify(mockedProtoLog).log(eq(LogLevel.ERROR), eq(ProtoLogGroup.TEST_GROUP),
anyInt(), eq(0b0010010111),
eq(com.android.internal.protolog.ProtoLogGroup.TEST_GROUP.isLogToLogcat()
? "Test completed successfully: %b %d %x %f %% %s"
@@ -66,18 +68,13 @@ public class ProtoLogIntegrationTest {
/**
* Starts protolog for the duration of {@code runnable}, with a ProtoLogImpl instance installed.
*/
- private void runWith(ProtoLogImpl mockInstance, Runnable runnable) {
- ProtoLogImpl original = ProtoLogImpl.getSingleInstance();
- original.startProtoLog(null);
+ private void runWith(IProtoLog mockInstance, Runnable runnable) {
+ IProtoLog original = ProtoLog.getSingleInstance();
+ ProtoLogImpl.setSingleInstance(mockInstance);
try {
- ProtoLogImpl.setSingleInstance(mockInstance);
- try {
- runnable.run();
- } finally {
- ProtoLogImpl.setSingleInstance(original);
- }
+ runnable.run();
} finally {
- original.stopProtoLog(null, false);
+ ProtoLogImpl.setSingleInstance(original);
}
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 9930c88b1e48..f7c253d9df03 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -46,7 +46,6 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
@@ -73,6 +72,7 @@ import android.os.SystemClock;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
import android.util.ArraySet;
+import android.util.IntArray;
import android.util.SparseBooleanArray;
import android.view.Surface;
import android.window.TaskSnapshot;
@@ -527,20 +527,20 @@ public class RecentTasksTest extends WindowTestsBase {
mTaskPersister.mUserTaskIdsOverride.put(1, true);
mTaskPersister.mUserTaskIdsOverride.put(2, true);
mTaskPersister.mUserTasksOverride = new ArrayList<>();
- mTaskPersister.mUserTasksOverride.add(createTaskBuilder(".UserTask1").build());
- mTaskPersister.mUserTasksOverride.add(createTaskBuilder(".UserTask2").build());
+ mTaskPersister.mUserTasksOverride.add(mTasks.get(0));
+ mTaskPersister.mUserTasksOverride.add(mTasks.get(1));
// Assert no user tasks are initially loaded
assertThat(mRecentTasks.usersWithRecentsLoadedLocked()).hasLength(0);
// Load user 0 tasks
- mRecentTasks.loadUserRecentsLocked(TEST_USER_0_ID);
+ mRecentTasks.loadRecentTasksIfNeeded(TEST_USER_0_ID);
assertThat(mRecentTasks.usersWithRecentsLoadedLocked()).asList().contains(TEST_USER_0_ID);
assertTrue(mRecentTasks.containsTaskId(1, TEST_USER_0_ID));
assertTrue(mRecentTasks.containsTaskId(2, TEST_USER_0_ID));
// Load user 1 tasks
- mRecentTasks.loadUserRecentsLocked(TEST_USER_1_ID);
+ mRecentTasks.loadRecentTasksIfNeeded(TEST_USER_1_ID);
assertThat(mRecentTasks.usersWithRecentsLoadedLocked()).asList().contains(TEST_USER_0_ID);
assertThat(mRecentTasks.usersWithRecentsLoadedLocked()).asList().contains(TEST_USER_1_ID);
assertTrue(mRecentTasks.containsTaskId(1, TEST_USER_0_ID));
@@ -575,15 +575,15 @@ public class RecentTasksTest extends WindowTestsBase {
mTaskPersister.mUserTaskIdsOverride.put(2, true);
mTaskPersister.mUserTaskIdsOverride.put(3, true);
mTaskPersister.mUserTasksOverride = new ArrayList<>();
- mTaskPersister.mUserTasksOverride.add(createTaskBuilder(".UserTask1").build());
- mTaskPersister.mUserTasksOverride.add(createTaskBuilder(".UserTask2").build());
- mTaskPersister.mUserTasksOverride.add(createTaskBuilder(".UserTask3").build());
+ mTaskPersister.mUserTasksOverride.add(mTasks.get(0));
+ mTaskPersister.mUserTasksOverride.add(mTasks.get(1));
+ mTaskPersister.mUserTasksOverride.add(mTasks.get(2));
// Assert no user tasks are initially loaded
assertThat(mRecentTasks.usersWithRecentsLoadedLocked()).hasLength(0);
// Load tasks
- mRecentTasks.loadUserRecentsLocked(TEST_USER_0_ID);
+ mRecentTasks.loadRecentTasksIfNeeded(TEST_USER_0_ID);
assertThat(mRecentTasks.usersWithRecentsLoadedLocked()).asList().contains(TEST_USER_0_ID);
// Sort the time descendingly so the order should be in-sync with task recency (most
@@ -1419,8 +1419,6 @@ public class RecentTasksTest extends WindowTestsBase {
}
private List<RecentTaskInfo> getRecentTasks(int flags) {
- doNothing().when(mRecentTasks).loadUserRecentsLocked(anyInt());
- doReturn(true).when(mRecentTasks).isUserRunning(anyInt(), anyInt());
return mRecentTasks.getRecentTasks(MAX_VALUE, flags, true /* getTasksAllowed */,
TEST_USER_0_ID, 0 /* callingUid */).getList();
}
@@ -1590,19 +1588,20 @@ public class RecentTasksTest extends WindowTestsBase {
}
@Override
- SparseBooleanArray loadPersistedTaskIdsForUser(int userId) {
+ SparseBooleanArray readPersistedTaskIdsFromFileForUser(int userId) {
if (mUserTaskIdsOverride != null) {
return mUserTaskIdsOverride;
}
- return super.loadPersistedTaskIdsForUser(userId);
+ return super.readPersistedTaskIdsFromFileForUser(userId);
}
@Override
- List<Task> restoreTasksForUserLocked(int userId, SparseBooleanArray preaddedTasks) {
+ ArrayList<Task> restoreTasksForUserLocked(int userId, RecentTaskFiles recentTaskFiles,
+ IntArray existedTaskIds) {
if (mUserTasksOverride != null) {
return mUserTasksOverride;
}
- return super.restoreTasksForUserLocked(userId, preaddedTasks);
+ return super.restoreTasksForUserLocked(userId, recentTaskFiles, existedTaskIds);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
index 0fcae92fdbfc..280fe4c30739 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
@@ -16,12 +16,14 @@
package com.android.server.wm;
+import static android.server.wm.CtsWindowInfoUtils.waitForStableWindowGeometry;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowInsets.Type.displayCutout;
import static android.view.WindowInsets.Type.statusBars;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -43,6 +45,7 @@ import android.os.Handler;
import android.os.Looper;
import android.os.ServiceManager;
import android.platform.test.annotations.Presubmit;
+import android.server.wm.BuildUtils;
import android.view.IWindowManager;
import android.view.PointerIcon;
import android.view.SurfaceControl;
@@ -50,7 +53,6 @@ import android.view.cts.surfacevalidator.BitmapPixelChecker;
import android.view.cts.surfacevalidator.SaveBitmapHelper;
import android.window.ScreenCapture;
import android.window.ScreenCapture.ScreenshotHardwareBuffer;
-import android.window.ScreenCapture.SynchronousScreenCaptureListener;
import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
@@ -74,6 +76,7 @@ import java.util.concurrent.TimeUnit;
@SmallTest
@Presubmit
public class ScreenshotTests {
+ private static final long WAIT_TIME_S = 5L * BuildUtils.HW_TIMEOUT_MULTIPLIER;
private static final int BUFFER_WIDTH = 100;
private static final int BUFFER_HEIGHT = 100;
@@ -119,32 +122,61 @@ public class ScreenshotTests {
canvas.drawColor(Color.RED);
buffer.unlockCanvasAndPost(canvas);
+ CountDownLatch countDownLatch = new CountDownLatch(1);
t.show(secureSC)
.setBuffer(secureSC, HardwareBuffer.createFromGraphicBuffer(buffer))
.setDataSpace(secureSC, DataSpace.DATASPACE_SRGB)
- .apply(true);
+ .addTransactionCommittedListener(Runnable::run, countDownLatch::countDown)
+ .apply();
+ assertTrue("Failed to wait for transaction to get committed",
+ countDownLatch.await(WAIT_TIME_S, TimeUnit.SECONDS));
+ assertTrue("Failed to wait for stable geometry",
+ waitForStableWindowGeometry(WAIT_TIME_S, TimeUnit.SECONDS));
ScreenCapture.LayerCaptureArgs args = new ScreenCapture.LayerCaptureArgs.Builder(secureSC)
.setCaptureSecureLayers(true)
.setChildrenOnly(false)
.build();
- ScreenCapture.ScreenshotHardwareBuffer hardwareBuffer = ScreenCapture.captureLayers(args);
- assertNotNull(hardwareBuffer);
- Bitmap screenshot = hardwareBuffer.asBitmap();
- assertNotNull(screenshot);
-
- Bitmap swBitmap = screenshot.copy(Bitmap.Config.ARGB_8888, false);
- screenshot.recycle();
-
- BitmapPixelChecker bitmapPixelChecker = new BitmapPixelChecker(Color.RED);
- Rect bounds = new Rect(0, 0, swBitmap.getWidth(), swBitmap.getHeight());
- int numMatchingPixels = bitmapPixelChecker.getNumMatchingPixels(swBitmap, bounds);
- int sizeOfBitmap = bounds.width() * bounds.height();
- boolean success = numMatchingPixels == sizeOfBitmap;
- swBitmap.recycle();
-
- assertTrue(success);
+ ScreenshotHardwareBuffer[] screenCapture = new ScreenshotHardwareBuffer[1];
+ Bitmap screenshot = null;
+ Bitmap swBitmap = null;
+ try {
+ CountDownLatch screenshotComplete = new CountDownLatch(1);
+ ScreenCapture.captureLayers(args, new ScreenCapture.ScreenCaptureListener(
+ (screenshotHardwareBuffer, result) -> {
+ if (result == 0) {
+ screenCapture[0] = screenshotHardwareBuffer;
+ }
+ screenshotComplete.countDown();
+ }));
+ assertTrue("Failed to wait for screen capture",
+ screenshotComplete.await(WAIT_TIME_S, TimeUnit.SECONDS));
+ assertNotNull("Screen capture buffer is null", screenCapture[0]);
+
+ screenshot = screenCapture[0].asBitmap();
+ assertNotNull("Screenshot from bitmap is null", screenshot);
+
+ swBitmap = screenshot.copy(Bitmap.Config.ARGB_8888, false);
+
+ BitmapPixelChecker bitmapPixelChecker = new BitmapPixelChecker(Color.RED);
+ Rect bounds = new Rect(0, 0, swBitmap.getWidth(), swBitmap.getHeight());
+ int numMatchingPixels = bitmapPixelChecker.getNumMatchingPixels(swBitmap, bounds);
+ int sizeOfBitmap = bounds.width() * bounds.height();
+
+ assertEquals("numMatchingPixels=" + numMatchingPixels + " sizeOfBitmap=" + sizeOfBitmap,
+ sizeOfBitmap, numMatchingPixels);
+ } finally {
+ if (screenshot != null) {
+ screenshot.recycle();
+ }
+ if (swBitmap != null) {
+ swBitmap.recycle();
+ }
+ if (screenCapture[0].getHardwareBuffer() != null) {
+ screenCapture[0].getHardwareBuffer().close();
+ }
+ }
}
@Test
@@ -169,36 +201,65 @@ public class ScreenshotTests {
buffer.unlockCanvasAndPost(canvas);
Point point = mActivity.getPositionBelowStatusBar();
+ CountDownLatch countDownLatch = new CountDownLatch(1);
t.show(sc)
.setBuffer(sc, HardwareBuffer.createFromGraphicBuffer(buffer))
.setDataSpace(sc, DataSpace.DATASPACE_SRGB)
.setPosition(sc, point.x, point.y)
- .apply(true);
-
- SynchronousScreenCaptureListener syncScreenCapture =
- ScreenCapture.createSyncCaptureListener();
- windowManager.captureDisplay(DEFAULT_DISPLAY, null, syncScreenCapture);
- ScreenshotHardwareBuffer hardwareBuffer = syncScreenCapture.getBuffer();
- assertNotNull(hardwareBuffer);
-
- Bitmap screenshot = hardwareBuffer.asBitmap();
- assertNotNull(screenshot);
-
- Bitmap swBitmap = screenshot.copy(Bitmap.Config.ARGB_8888, false);
- screenshot.recycle();
-
- BitmapPixelChecker bitmapPixelChecker = new BitmapPixelChecker(Color.RED);
- Rect bounds = new Rect(point.x, point.y, BUFFER_WIDTH + point.x, BUFFER_HEIGHT + point.y);
- int numMatchingPixels = bitmapPixelChecker.getNumMatchingPixels(swBitmap, bounds);
- int pixelMatchSize = bounds.width() * bounds.height();
- boolean success = numMatchingPixels == pixelMatchSize;
-
- if (!success) {
- SaveBitmapHelper.saveBitmap(swBitmap, getClass(), mTestName, "failedImage");
+ .addTransactionCommittedListener(Runnable::run, countDownLatch::countDown)
+ .apply();
+
+ assertTrue("Failed to wait for transaction to get committed",
+ countDownLatch.await(WAIT_TIME_S, TimeUnit.SECONDS));
+ assertTrue("Failed to wait for stable geometry",
+ waitForStableWindowGeometry(WAIT_TIME_S, TimeUnit.SECONDS));
+
+ ScreenshotHardwareBuffer[] screenCapture = new ScreenshotHardwareBuffer[1];
+ Bitmap screenshot = null;
+ Bitmap swBitmap = null;
+ try {
+ CountDownLatch screenshotComplete = new CountDownLatch(1);
+ windowManager.captureDisplay(DEFAULT_DISPLAY, null,
+ new ScreenCapture.ScreenCaptureListener(
+ (screenshotHardwareBuffer, result) -> {
+ if (result == 0) {
+ screenCapture[0] = screenshotHardwareBuffer;
+ }
+ screenshotComplete.countDown();
+ }));
+ assertTrue("Failed to wait for screen capture",
+ screenshotComplete.await(WAIT_TIME_S, TimeUnit.SECONDS));
+ assertNotNull("Screen capture buffer is null", screenCapture[0]);
+
+ screenshot = screenCapture[0].asBitmap();
+ assertNotNull("Screenshot from bitmap is null", screenshot);
+
+ swBitmap = screenshot.copy(Bitmap.Config.ARGB_8888, false);
+
+ BitmapPixelChecker bitmapPixelChecker = new BitmapPixelChecker(Color.RED);
+ Rect bounds = new Rect(point.x, point.y, BUFFER_WIDTH + point.x,
+ BUFFER_HEIGHT + point.y);
+ int numMatchingPixels = bitmapPixelChecker.getNumMatchingPixels(swBitmap, bounds);
+ int pixelMatchSize = bounds.width() * bounds.height();
+ boolean success = numMatchingPixels == pixelMatchSize;
+
+ if (!success) {
+ SaveBitmapHelper.saveBitmap(swBitmap, getClass(), mTestName, "failedImage");
+ }
+ assertTrue(
+ "numMatchingPixels=" + numMatchingPixels + " pixelMatchSize=" + pixelMatchSize,
+ success);
+ } finally {
+ if (screenshot != null) {
+ screenshot.recycle();
+ }
+ if (swBitmap != null) {
+ swBitmap.recycle();
+ }
+ if (screenCapture[0].getHardwareBuffer() != null) {
+ screenCapture[0].getHardwareBuffer().close();
+ }
}
- swBitmap.recycle();
- assertTrue("numMatchingPixels=" + numMatchingPixels + " pixelMatchSize=" + pixelMatchSize,
- success);
}
public static class ScreenshotActivity extends Activity {
diff --git a/services/tests/wmtests/src/com/android/server/wm/SensitiveContentPackagesTest.java b/services/tests/wmtests/src/com/android/server/wm/SensitiveContentPackagesTest.java
index 298637266cc3..824b8d7f6727 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SensitiveContentPackagesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SensitiveContentPackagesTest.java
@@ -16,10 +16,18 @@
package com.android.server.wm;
+import static android.permission.flags.Flags.FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION;
+import static android.view.flags.Flags.FLAG_SENSITIVE_CONTENT_APP_PROTECTION;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import android.os.Binder;
+import android.os.IBinder;
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.util.ArraySet;
import androidx.test.filters.SmallTest;
@@ -27,6 +35,7 @@ import androidx.test.filters.SmallTest;
import com.android.server.wm.SensitiveContentPackages.PackageInfo;
import org.junit.After;
+import org.junit.Rule;
import org.junit.Test;
import java.util.Set;
@@ -44,23 +53,30 @@ public class SensitiveContentPackagesTest {
private static final int APP_UID_1 = 5;
private static final int APP_UID_2 = 6;
- private static final int APP_UID_3 = 7;
+ private static final IBinder WINDOW_TOKEN_1 = new Binder();
+ private static final IBinder WINDOW_TOKEN_2 = new Binder();
private final SensitiveContentPackages mSensitiveContentPackages =
new SensitiveContentPackages();
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
@After
public void tearDown() {
mSensitiveContentPackages.clearBlockedApps();
}
+ private boolean shouldBlockScreenCaptureForApp(String pkg, int uid, IBinder windowToken) {
+ return mSensitiveContentPackages.shouldBlockScreenCaptureForApp(pkg, uid, windowToken);
+ }
+
@Test
- public void addBlockScreenCaptureForApps() {
+ @RequiresFlagsEnabled(FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION)
+ public void shouldBlockScreenCaptureForNotificationApps() {
ArraySet<PackageInfo> blockedApps = new ArraySet(
Set.of(new PackageInfo(APP_PKG_1, APP_UID_1),
- new PackageInfo(APP_PKG_1, APP_UID_2),
- new PackageInfo(APP_PKG_2, APP_UID_1),
new PackageInfo(APP_PKG_2, APP_UID_2)
));
@@ -68,17 +84,50 @@ public class SensitiveContentPackagesTest {
assertTrue(modified);
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1));
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_3));
+ assertTrue(shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1, WINDOW_TOKEN_1));
+ assertTrue(shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1, WINDOW_TOKEN_2));
+ assertFalse(shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_2, WINDOW_TOKEN_2));
+ assertTrue(shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2, WINDOW_TOKEN_1));
+ assertTrue(shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2, WINDOW_TOKEN_2));
+ assertFalse(shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_1, WINDOW_TOKEN_2));
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_SENSITIVE_CONTENT_APP_PROTECTION)
+ public void shouldBlockScreenCaptureForSensitiveContentOnScreenApps() {
+ ArraySet<PackageInfo> blockedApps = new ArraySet(
+ Set.of(new PackageInfo(APP_PKG_1, APP_UID_1, WINDOW_TOKEN_1),
+ new PackageInfo(APP_PKG_2, APP_UID_2, WINDOW_TOKEN_2)
+ ));
+
+ boolean modified = mSensitiveContentPackages.addBlockScreenCaptureForApps(blockedApps);
+ assertTrue(modified);
+
+ assertTrue(shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1, WINDOW_TOKEN_1));
+ assertFalse(shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1, WINDOW_TOKEN_2));
+
+ assertTrue(shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2, WINDOW_TOKEN_2));
+ assertFalse(shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2, WINDOW_TOKEN_1));
+ }
+
+ @Test
+ @RequiresFlagsEnabled(
+ {FLAG_SENSITIVE_CONTENT_APP_PROTECTION, FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION})
+ public void shouldBlockScreenCaptureForApps() {
+ ArraySet<PackageInfo> blockedApps = new ArraySet(
+ Set.of(new PackageInfo(APP_PKG_1, APP_UID_1, WINDOW_TOKEN_1),
+ new PackageInfo(APP_PKG_1, APP_UID_1),
+ new PackageInfo(APP_PKG_2, APP_UID_2, WINDOW_TOKEN_2)
+ ));
+
+ boolean modified = mSensitiveContentPackages.addBlockScreenCaptureForApps(blockedApps);
+ assertTrue(modified);
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_1));
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_3));
+ assertTrue(shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1, WINDOW_TOKEN_1));
+ assertTrue(shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1, WINDOW_TOKEN_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_1));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_3));
+ assertTrue(shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2, WINDOW_TOKEN_2));
+ assertFalse(shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2, WINDOW_TOKEN_1));
}
@Test
@@ -92,20 +141,8 @@ public class SensitiveContentPackagesTest {
mSensitiveContentPackages.addBlockScreenCaptureForApps(blockedApps);
boolean modified = mSensitiveContentPackages.addBlockScreenCaptureForApps(blockedApps);
-
assertFalse(modified);
-
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1));
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_3));
-
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_1));
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_3));
-
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_1));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_3));
+ assertTrue(mSensitiveContentPackages.size() == 4);
}
@Test
@@ -121,65 +158,28 @@ public class SensitiveContentPackagesTest {
boolean modified = mSensitiveContentPackages
.addBlockScreenCaptureForApps(
new ArraySet(Set.of(new PackageInfo(APP_PKG_3, APP_UID_1))));
-
assertTrue(modified);
-
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1));
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_3));
-
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_1));
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_3));
-
- assertTrue(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_1));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_3));
+ assertTrue(mSensitiveContentPackages.size() == 5);
}
@Test
public void clearBlockedApps() {
ArraySet<PackageInfo> blockedApps = new ArraySet(
Set.of(new PackageInfo(APP_PKG_1, APP_UID_1),
- new PackageInfo(APP_PKG_1, APP_UID_2),
- new PackageInfo(APP_PKG_2, APP_UID_1),
- new PackageInfo(APP_PKG_2, APP_UID_2)
+ new PackageInfo(APP_PKG_2, APP_UID_2, WINDOW_TOKEN_2)
));
mSensitiveContentPackages.addBlockScreenCaptureForApps(blockedApps);
boolean modified = mSensitiveContentPackages.clearBlockedApps();
assertTrue(modified);
-
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_3));
-
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_1));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_3));
-
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_1));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_3));
+ assertTrue(mSensitiveContentPackages.size() == 0);
}
@Test
public void clearBlockedApps_alreadyEmpty() {
boolean modified = mSensitiveContentPackages.clearBlockedApps();
-
assertFalse(modified);
-
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_1));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_1, APP_UID_3));
-
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_1));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_2, APP_UID_3));
-
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_1));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_2));
- assertFalse(mSensitiveContentPackages.shouldBlockScreenCaptureForApp(APP_PKG_3, APP_UID_3));
+ assertTrue(mSensitiveContentPackages.size() == 0);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index f42cdb88021e..b96f39d7a4e1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -22,6 +22,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
@@ -46,6 +47,7 @@ import static android.view.WindowInsets.Type.statusBars;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
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_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
@@ -100,6 +102,7 @@ import android.content.pm.ActivityInfo.ScreenOrientation;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Binder;
import android.os.RemoteException;
@@ -153,6 +156,8 @@ public class SizeCompatTests extends WindowTestsBase {
private static final String CONFIG_NEVER_CONSTRAIN_DISPLAY_APIS_ALL_PACKAGES =
"never_constrain_display_apis_all_packages";
+ private static final float DELTA_ASPECT_RATIO_TOLERANCE = 0.005f;
+
@Rule
public TestRule compatChangeRule = new PlatformCompatChangeRule();
@@ -211,90 +216,46 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testHorizontalReachabilityEnabledForTranslucentActivities() {
- setUpDisplaySizeWithApp(2500, 1000);
- mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
- final LetterboxConfiguration config = mWm.mLetterboxConfiguration;
- config.setTranslucentLetterboxingOverrideEnabled(true);
- config.setLetterboxHorizontalPositionMultiplier(0.5f);
- config.setIsHorizontalReachabilityEnabled(true);
-
- // Opaque activity
- prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
- addWindowToActivity(mActivity);
- mActivity.mRootWindowContainer.performSurfacePlacement();
-
- // Translucent Activity
- final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
- .setActivityTheme(android.R.style.Theme_Translucent)
- .setLaunchedFromUid(mActivity.getUid())
- .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
- .build();
- mTask.addChild(translucentActivity);
-
- spyOn(translucentActivity.mLetterboxUiController);
- doReturn(true).when(translucentActivity.mLetterboxUiController)
- .shouldShowLetterboxUi(any());
-
- addWindowToActivity(translucentActivity);
- translucentActivity.mRootWindowContainer.performSurfacePlacement();
-
- final Function<ActivityRecord, Rect> innerBoundsOf =
- (ActivityRecord a) -> {
- final Rect bounds = new Rect();
- a.mLetterboxUiController.getLetterboxInnerBounds(bounds);
- return bounds;
- };
- final Runnable checkLetterboxPositions = () -> assertEquals(innerBoundsOf.apply(mActivity),
- innerBoundsOf.apply(translucentActivity));
- final Runnable checkIsLeft = () -> assertThat(
- innerBoundsOf.apply(translucentActivity).left).isEqualTo(0);
- final Runnable checkIsRight = () -> assertThat(
- innerBoundsOf.apply(translucentActivity).right).isEqualTo(2500);
- final Runnable checkIsCentered = () -> assertThat(
- innerBoundsOf.apply(translucentActivity).left > 0
- && innerBoundsOf.apply(translucentActivity).right < 2500).isTrue();
-
- final Consumer<Integer> doubleClick =
- (Integer x) -> {
- mActivity.mLetterboxUiController.handleHorizontalDoubleTap(x);
- mActivity.mRootWindowContainer.performSurfacePlacement();
- };
-
- // Initial state
- checkIsCentered.run();
-
- // Double-click left
- doubleClick.accept(/* x */ 10);
- checkLetterboxPositions.run();
- checkIsLeft.run();
-
- // Double-click right
- doubleClick.accept(/* x */ 1990);
- checkLetterboxPositions.run();
- checkIsCentered.run();
-
- // Double-click right
- doubleClick.accept(/* x */ 1990);
- checkLetterboxPositions.run();
- checkIsRight.run();
+ testReachabilityEnabledForTranslucentActivity(/* dw */ 2500, /* dh */1000,
+ SCREEN_ORIENTATION_PORTRAIT, /* minAspectRatio */ 0f,
+ /* horizontalReachability */ true);
+ }
- // Double-click left
- doubleClick.accept(/* x */ 10);
- checkLetterboxPositions.run();
- checkIsCentered.run();
+ @Test
+ public void testHorizontalReachabilityEnabled_TranslucentPortraitActivities_portraitDisplay() {
+ testReachabilityEnabledForTranslucentActivity(/* dw */ 1400, /* dh */1600,
+ SCREEN_ORIENTATION_PORTRAIT, OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
+ /* horizontalReachability */ true);
}
@Test
public void testVerticalReachabilityEnabledForTranslucentActivities() {
- setUpDisplaySizeWithApp(1000, 2500);
+ testReachabilityEnabledForTranslucentActivity(/* dw */ 1000, /* dh */2500,
+ SCREEN_ORIENTATION_LANDSCAPE, /* minAspectRatio */ 0f,
+ /* horizontalReachability */ false);
+ }
+
+ @Test
+ public void testVerticalReachabilityEnabled_TranslucentLandscapeActivities_landscapeDisplay() {
+ testReachabilityEnabledForTranslucentActivity(/* dw */ 1600, /* dh */1400,
+ SCREEN_ORIENTATION_LANDSCAPE, OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
+ /* horizontalReachability */ false);
+ }
+
+ private void testReachabilityEnabledForTranslucentActivity(int displayWidth, int displayHeight,
+ @ScreenOrientation int screenOrientation, float minAspectRatio,
+ boolean horizontalReachability) {
+ setUpDisplaySizeWithApp(displayWidth, displayHeight);
mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
final LetterboxConfiguration config = mWm.mLetterboxConfiguration;
config.setTranslucentLetterboxingOverrideEnabled(true);
config.setLetterboxVerticalPositionMultiplier(0.5f);
config.setIsVerticalReachabilityEnabled(true);
+ config.setLetterboxHorizontalPositionMultiplier(0.5f);
+ config.setIsHorizontalReachabilityEnabled(true);
// Opaque activity
- prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
+ prepareMinAspectRatio(mActivity, minAspectRatio, screenOrientation);
addWindowToActivity(mActivity);
mActivity.mRootWindowContainer.performSurfacePlacement();
@@ -302,7 +263,7 @@ public class SizeCompatTests extends WindowTestsBase {
final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
.setActivityTheme(android.R.style.Theme_Translucent)
.setLaunchedFromUid(mActivity.getUid())
- .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
+ .setScreenOrientation(screenOrientation)
.build();
mTask.addChild(translucentActivity);
@@ -324,39 +285,78 @@ public class SizeCompatTests extends WindowTestsBase {
final Runnable checkIsTop = () -> assertThat(
innerBoundsOf.apply(translucentActivity).top).isEqualTo(0);
final Runnable checkIsBottom = () -> assertThat(
- innerBoundsOf.apply(translucentActivity).bottom).isEqualTo(2500);
- final Runnable checkIsCentered = () -> assertThat(
+ innerBoundsOf.apply(translucentActivity).bottom).isEqualTo(displayHeight);
+ final Runnable checkIsLeft = () -> assertThat(
+ innerBoundsOf.apply(translucentActivity).left).isEqualTo(0);
+ final Runnable checkIsRight = () -> assertThat(
+ innerBoundsOf.apply(translucentActivity).right).isEqualTo(displayWidth);
+ final Runnable checkIsHorizontallyCentered = () -> assertThat(
+ innerBoundsOf.apply(translucentActivity).left > 0
+ && innerBoundsOf.apply(translucentActivity).right < displayWidth).isTrue();
+ final Runnable checkIsVerticallyCentered = () -> assertThat(
innerBoundsOf.apply(translucentActivity).top > 0
- && innerBoundsOf.apply(translucentActivity).bottom < 2500).isTrue();
-
- final Consumer<Integer> doubleClick =
- (Integer y) -> {
- mActivity.mLetterboxUiController.handleVerticalDoubleTap(y);
- mActivity.mRootWindowContainer.performSurfacePlacement();
- };
-
- // Initial state
- checkIsCentered.run();
-
- // Double-click top
- doubleClick.accept(/* y */ 10);
- checkLetterboxPositions.run();
- checkIsTop.run();
-
- // Double-click bottom
- doubleClick.accept(/* y */ 1990);
- checkLetterboxPositions.run();
- checkIsCentered.run();
-
- // Double-click bottom
- doubleClick.accept(/* y */ 1990);
- checkLetterboxPositions.run();
- checkIsBottom.run();
-
- // Double-click top
- doubleClick.accept(/* y */ 10);
- checkLetterboxPositions.run();
- checkIsCentered.run();
+ && innerBoundsOf.apply(translucentActivity).bottom < displayHeight)
+ .isTrue();
+
+ if (horizontalReachability) {
+ final Consumer<Integer> doubleClick =
+ (Integer x) -> {
+ mActivity.mLetterboxUiController.handleHorizontalDoubleTap(x);
+ mActivity.mRootWindowContainer.performSurfacePlacement();
+ };
+
+ // Initial state
+ checkIsHorizontallyCentered.run();
+
+ // Double-click left
+ doubleClick.accept(/* x */ 10);
+ checkLetterboxPositions.run();
+ checkIsLeft.run();
+
+ // Double-click right
+ doubleClick.accept(/* x */ displayWidth - 100);
+ checkLetterboxPositions.run();
+ checkIsHorizontallyCentered.run();
+
+ // Double-click right
+ doubleClick.accept(/* x */ displayWidth - 100);
+ checkLetterboxPositions.run();
+ checkIsRight.run();
+
+ // Double-click left
+ doubleClick.accept(/* x */ 10);
+ checkLetterboxPositions.run();
+ checkIsHorizontallyCentered.run();
+ } else {
+ final Consumer<Integer> doubleClick =
+ (Integer y) -> {
+ mActivity.mLetterboxUiController.handleVerticalDoubleTap(y);
+ mActivity.mRootWindowContainer.performSurfacePlacement();
+ };
+
+ // Initial state
+ checkIsVerticallyCentered.run();
+
+ // Double-click top
+ doubleClick.accept(/* y */ 10);
+ checkLetterboxPositions.run();
+ checkIsTop.run();
+
+ // Double-click bottom
+ doubleClick.accept(/* y */ displayHeight - 100);
+ checkLetterboxPositions.run();
+ checkIsVerticallyCentered.run();
+
+ // Double-click bottom
+ doubleClick.accept(/* y */ displayHeight - 100);
+ checkLetterboxPositions.run();
+ checkIsBottom.run();
+
+ // Double-click top
+ doubleClick.accept(/* y */ 10);
+ checkLetterboxPositions.run();
+ checkIsVerticallyCentered.run();
+ }
}
@Test
@@ -2143,7 +2143,7 @@ public class SizeCompatTests extends WindowTestsBase {
final Rect afterBounds = mActivity.getBounds();
final float actualAspectRatio = 1f * afterBounds.height() / afterBounds.width();
assertEquals(LetterboxConfiguration.DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW,
- actualAspectRatio, 0.001f);
+ actualAspectRatio, DELTA_ASPECT_RATIO_TOLERANCE);
assertTrue(mActivity.areBoundsLetterboxed());
}
@@ -2179,7 +2179,7 @@ public class SizeCompatTests extends WindowTestsBase {
// default letterbox aspect ratio for multi-window.
final Rect afterBounds = mActivity.getBounds();
final float actualAspectRatio = 1f * afterBounds.height() / afterBounds.width();
- assertEquals(minAspectRatio, actualAspectRatio, 0.001f);
+ assertEquals(minAspectRatio, actualAspectRatio, DELTA_ASPECT_RATIO_TOLERANCE);
assertTrue(mActivity.areBoundsLetterboxed());
}
@@ -2487,7 +2487,7 @@ public class SizeCompatTests extends WindowTestsBase {
final float afterAspectRatio =
(float) Math.max(width, height) / (float) Math.min(width, height);
- assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
+ assertEquals(expectedAspectRatio, afterAspectRatio, DELTA_ASPECT_RATIO_TOLERANCE);
}
@Test
@@ -2512,7 +2512,7 @@ public class SizeCompatTests extends WindowTestsBase {
float expectedAspectRatio = 1f * displayWidth / getExpectedSplitSize(displayHeight);
final Rect afterBounds = activity.getBounds();
final float afterAspectRatio = (float) (afterBounds.height()) / afterBounds.width();
- assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
+ assertEquals(expectedAspectRatio, afterAspectRatio, DELTA_ASPECT_RATIO_TOLERANCE);
}
@Test
@@ -2537,7 +2537,7 @@ public class SizeCompatTests extends WindowTestsBase {
float expectedAspectRatio = 1f * displayHeight / getExpectedSplitSize(displayWidth);
final Rect afterBounds = activity.getBounds();
final float afterAspectRatio = (float) (afterBounds.height()) / afterBounds.width();
- assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
+ assertEquals(expectedAspectRatio, afterAspectRatio, DELTA_ASPECT_RATIO_TOLERANCE);
}
@Test
@@ -2563,7 +2563,7 @@ public class SizeCompatTests extends WindowTestsBase {
float expectedAspectRatio = 1f * displayWidth / getExpectedSplitSize(displayHeight);
final Rect afterBounds = activity.getBounds();
final float afterAspectRatio = (float) (afterBounds.width()) / afterBounds.height();
- assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
+ assertEquals(expectedAspectRatio, afterAspectRatio, DELTA_ASPECT_RATIO_TOLERANCE);
}
@Test
@@ -2589,7 +2589,7 @@ public class SizeCompatTests extends WindowTestsBase {
float expectedAspectRatio = 1f * displayHeight / getExpectedSplitSize(displayWidth);
final Rect afterBounds = activity.getBounds();
final float afterAspectRatio = (float) (afterBounds.width()) / afterBounds.height();
- assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
+ assertEquals(expectedAspectRatio, afterAspectRatio, DELTA_ASPECT_RATIO_TOLERANCE);
}
@Test
@@ -2629,7 +2629,7 @@ public class SizeCompatTests extends WindowTestsBase {
float expectedAspectRatio = 1f * screenHeight / getExpectedSplitSize(screenWidth);
final Rect afterBounds = activity.getBounds();
final float afterAspectRatio = (float) (afterBounds.height()) / afterBounds.width();
- assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
+ assertEquals(expectedAspectRatio, afterAspectRatio, DELTA_ASPECT_RATIO_TOLERANCE);
assertFalse(activity.areBoundsLetterboxed());
}
@@ -2670,7 +2670,7 @@ public class SizeCompatTests extends WindowTestsBase {
float expectedAspectRatio = 1f * screenWidth / getExpectedSplitSize(screenHeight);
final Rect afterBounds = activity.getBounds();
final float afterAspectRatio = (float) (afterBounds.width()) / afterBounds.height();
- assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
+ assertEquals(expectedAspectRatio, afterAspectRatio, DELTA_ASPECT_RATIO_TOLERANCE);
assertFalse(activity.areBoundsLetterboxed());
}
@@ -2847,9 +2847,8 @@ public class SizeCompatTests extends WindowTestsBase {
assertFitted();
// Check that the display aspect ratio is used by the app.
final float targetMinAspectRatio = 1f * displayHeight / displayWidth;
- final float delta = 0.01f;
assertEquals(targetMinAspectRatio, ActivityRecord
- .computeAspectRatio(mActivity.getBounds()), delta);
+ .computeAspectRatio(mActivity.getBounds()), DELTA_ASPECT_RATIO_TOLERANCE);
}
@Test
@@ -2883,9 +2882,8 @@ public class SizeCompatTests extends WindowTestsBase {
assertFitted();
// Check that the display aspect ratio is used by the app.
final float targetMinAspectRatio = 1f * displayWidth / displayHeight;
- final float delta = 0.01f;
assertEquals(targetMinAspectRatio, ActivityRecord
- .computeAspectRatio(mActivity.getBounds()), delta);
+ .computeAspectRatio(mActivity.getBounds()), DELTA_ASPECT_RATIO_TOLERANCE);
}
@Test
@@ -2910,9 +2908,8 @@ public class SizeCompatTests extends WindowTestsBase {
assertFitted();
// Check that the display aspect ratio is used by the app.
final float targetMinAspectRatio = 1f * displayHeight / displayWidth;
- final float delta = 0.01f;
assertEquals(targetMinAspectRatio, ActivityRecord
- .computeAspectRatio(mActivity.getBounds()), delta);
+ .computeAspectRatio(mActivity.getBounds()), DELTA_ASPECT_RATIO_TOLERANCE);
}
@Test
@@ -2937,9 +2934,8 @@ public class SizeCompatTests extends WindowTestsBase {
assertFitted();
// Check that the display aspect ratio is used by the app.
final float targetMinAspectRatio = 1f * displayWidth / displayHeight;
- final float delta = 0.01f;
assertEquals(targetMinAspectRatio, ActivityRecord
- .computeAspectRatio(mActivity.getBounds()), delta);
+ .computeAspectRatio(mActivity.getBounds()), DELTA_ASPECT_RATIO_TOLERANCE);
}
@Test
@@ -3609,6 +3605,32 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
+ public void testIsHorizontalReachabilityEnabled_portraitDisplayAndApp_true() {
+ // Portrait display
+ setUpDisplaySizeWithApp(1400, 1600);
+ mActivity.mWmService.mLetterboxConfiguration.setIsHorizontalReachabilityEnabled(true);
+
+ // 16:9f unresizable portrait app
+ prepareMinAspectRatio(mActivity, OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
+ SCREEN_ORIENTATION_PORTRAIT);
+
+ assertTrue(mActivity.mLetterboxUiController.isHorizontalReachabilityEnabled());
+ }
+
+ @Test
+ public void testIsVerticalReachabilityEnabled_landscapeDisplayAndApp_true() {
+ // Landscape display
+ setUpDisplaySizeWithApp(1600, 1500);
+ mActivity.mWmService.mLetterboxConfiguration.setIsVerticalReachabilityEnabled(true);
+
+ // 16:9f unresizable landscape app
+ prepareMinAspectRatio(mActivity, OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
+ SCREEN_ORIENTATION_LANDSCAPE);
+
+ assertTrue(mActivity.mLetterboxUiController.isVerticalReachabilityEnabled());
+ }
+
+ @Test
public void testIsHorizontalReachabilityEnabled_doesNotMatchParentHeight_false() {
setUpDisplaySizeWithApp(2800, 1000);
mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
@@ -4053,6 +4075,32 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
+ public void testPortraitCloseToSquareDisplayWithTaskbar_notLetterboxed() {
+ // Set up portrait close to square display
+ setUpDisplaySizeWithApp(2200, 2280);
+ final DisplayContent display = mActivity.mDisplayContent;
+ // Simulate taskbar, final app bounds are (0, 0, 2200, 2130) - landscape
+ final WindowState navbar = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent,
+ "navbar");
+ final Binder owner = new Binder();
+ navbar.mAttrs.providedInsets = new InsetsFrameProvider[] {
+ new InsetsFrameProvider(owner, 0, WindowInsets.Type.navigationBars())
+ .setInsetsSize(Insets.of(0, 0, 0, 150))
+ };
+ display.getDisplayPolicy().addWindowLw(navbar, navbar.mAttrs);
+ assertTrue(navbar.providesDisplayDecorInsets()
+ && display.getDisplayPolicy().updateDecorInsetsInfo());
+ display.sendNewConfiguration();
+
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+ // Activity is fullscreen even though orientation is not respected with insets, because
+ // the display still matches or is less than the activity aspect ratio
+ assertEquals(display.getBounds(), mActivity.getBounds());
+ assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+ }
+
+ @Test
public void testApplyAspectRatio_activityAlignWithParentAppVertical() {
// The display's app bounds will be (0, 100, 1000, 2350)
final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2500)
@@ -4275,7 +4323,7 @@ public class SizeCompatTests extends WindowTestsBase {
.getFixedOrientationLetterboxAspectRatio(parentConfig);
float expected = mActivity.mLetterboxUiController.getSplitScreenAspectRatio();
- assertEquals(expected, actual, 0.01);
+ assertEquals(expected, actual, DELTA_ASPECT_RATIO_TOLERANCE);
}
@Test
@@ -4671,13 +4719,12 @@ public class SizeCompatTests extends WindowTestsBase {
.windowConfiguration.getAppBounds());
// Check that aspect ratio of app bounds is equal to the min aspect ratio.
- final float delta = 0.01f;
assertEquals(targetMinAspectRatio, ActivityRecord
- .computeAspectRatio(fixedOrientationAppBounds), delta);
+ .computeAspectRatio(fixedOrientationAppBounds), DELTA_ASPECT_RATIO_TOLERANCE);
assertEquals(targetMinAspectRatio, ActivityRecord
- .computeAspectRatio(minAspectRatioAppBounds), delta);
+ .computeAspectRatio(minAspectRatioAppBounds), DELTA_ASPECT_RATIO_TOLERANCE);
assertEquals(targetMinAspectRatio, ActivityRecord
- .computeAspectRatio(sizeCompatAppBounds), delta);
+ .computeAspectRatio(sizeCompatAppBounds), DELTA_ASPECT_RATIO_TOLERANCE);
}
@Test
@@ -4859,6 +4906,12 @@ public class SizeCompatTests extends WindowTestsBase {
.build();
}
+ static void prepareMinAspectRatio(ActivityRecord activity, float minAspect,
+ int screenOrientation) {
+ prepareLimitedBounds(activity, -1 /* maxAspect */, minAspect, screenOrientation,
+ true /* isUnresizable */);
+ }
+
static void prepareUnresizable(ActivityRecord activity, int screenOrientation) {
prepareUnresizable(activity, -1 /* maxAspect */, screenOrientation);
}
@@ -4873,11 +4926,17 @@ public class SizeCompatTests extends WindowTestsBase {
prepareLimitedBounds(activity, -1 /* maxAspect */, screenOrientation, isUnresizable);
}
+ static void prepareLimitedBounds(ActivityRecord activity, float maxAspect,
+ int screenOrientation, boolean isUnresizable) {
+ prepareLimitedBounds(activity, maxAspect, -1 /* minAspect */, screenOrientation,
+ isUnresizable);
+ }
+
/**
- * Setups {@link #mActivity} with restriction on its bounds, such as maxAspect, fixed
- * orientation, and/or whether it is resizable.
+ * Setups {@link #mActivity} with restriction on its bounds, such as maxAspect, minAspect,
+ * fixed orientation, and/or whether it is resizable.
*/
- static void prepareLimitedBounds(ActivityRecord activity, float maxAspect,
+ static void prepareLimitedBounds(ActivityRecord activity, float maxAspect, float minAspect,
int screenOrientation, boolean isUnresizable) {
activity.info.resizeMode = isUnresizable
? RESIZE_MODE_UNRESIZEABLE
@@ -4892,6 +4951,9 @@ public class SizeCompatTests extends WindowTestsBase {
if (maxAspect >= 0) {
activity.info.setMaxAspectRatio(maxAspect);
}
+ if (minAspect >= 0) {
+ activity.info.setMinAspectRatio(minAspect);
+ }
if (screenOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
activity.info.screenOrientation = screenOrientation;
activity.setRequestedOrientation(screenOrientation);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTest.java
index d84620b4444a..ead36f1d353f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTest.java
@@ -25,6 +25,7 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.platform.test.annotations.Presubmit;
+import android.server.wm.BuildUtils;
import android.view.SurfaceControl;
import android.window.SurfaceSyncGroup;
@@ -370,6 +371,7 @@ public class SurfaceSyncGroupTest {
assertEquals(0, finishedLatch.getCount());
}
+ @Test
public void testSurfaceSyncGroupTimeout() throws InterruptedException {
final CountDownLatch finishedLatch = new CountDownLatch(1);
SurfaceSyncGroup syncGroup = new SurfaceSyncGroup(TAG);
@@ -386,7 +388,7 @@ public class SurfaceSyncGroupTest {
// Never finish syncTarget2 so it forces the timeout. Timeout is 1 second so wait a little
// over 1 second to make sure it completes.
- finishedLatch.await(1100, TimeUnit.MILLISECONDS);
+ finishedLatch.await(1100L * BuildUtils.HW_TIMEOUT_MULTIPLIER, TimeUnit.MILLISECONDS);
assertEquals(0, finishedLatch.getCount());
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 295b124c06e0..a8f6fe86c823 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -346,7 +346,7 @@ public class SystemServicesTestRule implements TestRule {
doReturn(true).when(amInternal).hasStartedUserState(anyInt());
doReturn(false).when(amInternal).shouldConfirmCredentials(anyInt());
doReturn(false).when(amInternal).isActivityStartsLoggingEnabled();
- doReturn(true).when(amInternal).getThemeOverlayReadiness();
+ doReturn(true).when(amInternal).isThemeOverlayReady(anyInt());
LocalServices.addService(ActivityManagerInternal.class, amInternal);
final ActivityManagerService amService =
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPersisterTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskPersisterTest.java
index 12ed3c28161f..319be89b2901 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPersisterTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPersisterTest.java
@@ -29,8 +29,6 @@ import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
import android.util.SparseBooleanArray;
-import androidx.test.filters.FlakyTest;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -81,7 +79,7 @@ public class TaskPersisterTest {
}
mTaskPersister.writePersistedTaskIdsForUser(taskIdsOnFile, mTestUserId);
SparseBooleanArray newTaskIdsOnFile = mTaskPersister
- .loadPersistedTaskIdsForUser(mTestUserId);
+ .readPersistedTaskIdsFromFileForUser(mTestUserId);
assertEquals("TaskIds written differ from TaskIds read back from file",
taskIdsOnFile, newTaskIdsOnFile);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
index db08eabac699..bfc13d3d2ef2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
@@ -61,10 +61,7 @@ public class TaskPositioningControllerTests extends WindowTestsBase {
assertNotNull(mWm.mTaskPositioningController);
mTarget = mWm.mTaskPositioningController;
- when(mWm.mInputManager.transferTouchFocus(
- any(InputChannel.class),
- any(InputChannel.class),
- any(boolean.class))).thenReturn(true);
+ when(mWm.mInputManager.transferTouchGesture(any(), any())).thenReturn(true);
mWindow = createWindow(null, TYPE_BASE_APPLICATION, "window");
mWindow.getTask().setResizeMode(RESIZE_MODE_RESIZEABLE);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
index 06d30fc98c6a..29f48b86a375 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
@@ -58,7 +58,7 @@ public class TaskSnapshotCacheTest extends TaskSnapshotPersisterTestBase {
public void setUp() {
super.setUp();
MockitoAnnotations.initMocks(this);
- mCache = new TaskSnapshotCache(mWm, mLoader);
+ mCache = new TaskSnapshotCache(mLoader);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java
index df5f3d149e88..7432537902a0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java
@@ -61,7 +61,7 @@ public class TaskSnapshotLowResDisabledTest extends TaskSnapshotPersisterTestBas
public void setUp() {
super.setUp();
MockitoAnnotations.initMocks(this);
- mCache = new TaskSnapshotCache(mWm, mLoader);
+ mCache = new TaskSnapshotCache(mLoader);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java b/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java
index 6a15b0594428..f1d84cfc636d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java
@@ -40,8 +40,10 @@ import android.view.WindowManager;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.server.wm.utils.CommonUtils;
import com.android.window.flags.Flags;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -77,6 +79,11 @@ public class TrustedOverlayTests {
});
}
+ @After
+ public void tearDown() {
+ CommonUtils.waitUntilActivityRemoved(mActivity);
+ }
+
@RequiresFlagsDisabled(Flags.FLAG_SURFACE_TRUSTED_OVERLAY)
@Test
public void setTrustedOverlayInputWindow() throws InterruptedException {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
index 73d386a328f5..80fb44a9b50f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -88,8 +88,6 @@ public class WallpaperControllerTests extends WindowTestsBase {
@Test
public void testWallpaperScreenshot() {
- WindowSurfaceController windowSurfaceController = mock(WindowSurfaceController.class);
-
// No wallpaper
final DisplayContent dc = createNewDisplay();
assertFalse(dc.mWallpaperController.canScreenshotWallpaper());
@@ -99,11 +97,9 @@ public class WallpaperControllerTests extends WindowTestsBase {
assertFalse(dc.mWallpaperController.canScreenshotWallpaper());
// Wallpaper with not visible WSA surface.
- wallpaperWindow.mWinAnimator.mSurfaceController = windowSurfaceController;
- wallpaperWindow.mWinAnimator.mLastAlpha = 1;
assertFalse(dc.mWallpaperController.canScreenshotWallpaper());
- when(windowSurfaceController.getShown()).thenReturn(true);
+ makeWallpaperWindowShown(wallpaperWindow);
// Wallpaper with WSA alpha set to 0.
wallpaperWindow.mWinAnimator.mLastAlpha = 0;
@@ -306,14 +302,25 @@ public class WallpaperControllerTests extends WindowTestsBase {
spyOn(mWm.mPolicy);
doReturn(true).when(mWm.mPolicy).isKeyguardLocked();
doReturn(true).when(mWm.mPolicy).isKeyguardOccluded();
- mDisplayContent.mWallpaperController.adjustWallpaperWindows();
+ final WallpaperController wallpaperController = mDisplayContent.mWallpaperController;
+ wallpaperController.adjustWallpaperWindows();
// Wallpaper is visible because the show-when-locked activity is translucent.
- assertTrue(mDisplayContent.mWallpaperController.isWallpaperTarget(wallpaperWindow));
+ assertTrue(wallpaperController.isWallpaperTarget(wallpaperWindow));
behind.mActivityRecord.setShowWhenLocked(true);
- mDisplayContent.mWallpaperController.adjustWallpaperWindows();
+ wallpaperController.adjustWallpaperWindows();
// Wallpaper is invisible because the lowest show-when-locked activity is opaque.
- assertTrue(mDisplayContent.mWallpaperController.isWallpaperTarget(null));
+ assertTrue(wallpaperController.isWallpaperTarget(null));
+
+ // A show-when-locked wallpaper is used for lockscreen. So the top wallpaper should
+ // be the one that is not show-when-locked.
+ final WindowState wallpaperWindow2 = createWallpaperWindow(mDisplayContent);
+ makeWallpaperWindowShown(wallpaperWindow2);
+ makeWallpaperWindowShown(wallpaperWindow);
+ assertEquals(wallpaperWindow2, wallpaperController.getTopVisibleWallpaper());
+ wallpaperWindow2.mToken.asWallpaperToken().setShowWhenLocked(true);
+ wallpaperWindow.mToken.asWallpaperToken().setShowWhenLocked(false);
+ assertEquals(wallpaperWindow, wallpaperController.getTopVisibleWallpaper());
}
/**
@@ -527,6 +534,13 @@ public class WallpaperControllerTests extends WindowTestsBase {
assertThat(wallpaperWindow.mYOffset).isEqualTo(0);
}
+ private static void makeWallpaperWindowShown(WindowState w) {
+ final WindowSurfaceController windowSurfaceController = mock(WindowSurfaceController.class);
+ w.mWinAnimator.mSurfaceController = windowSurfaceController;
+ w.mWinAnimator.mLastAlpha = 1;
+ when(windowSurfaceController.getShown()).thenReturn(true);
+ }
+
private WindowState createWallpaperWindow(DisplayContent dc, int width, int height) {
final WindowState wallpaperWindow = createWallpaperWindow(dc);
// Wallpaper is cropped to match first display.
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 4da519c212df..a0e64bf94393 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -21,9 +21,11 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.permission.flags.Flags.FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.FLAG_OWN_FOCUS;
import static android.view.Display.INVALID_DISPLAY;
+import static android.view.flags.Flags.FLAG_SENSITIVE_CONTENT_APP_PROTECTION;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_SPY;
@@ -80,6 +82,7 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
import android.util.ArraySet;
import android.util.MergedConfiguration;
import android.view.ContentRecordingSession;
@@ -240,6 +243,22 @@ public class WindowManagerServiceTests extends WindowTestsBase {
}
@Test
+ public void testTrackOverlayWindow() {
+ final WindowProcessController wpc = mSystemServicesTestRule.addProcess(
+ "pkgName", "processName", 1000 /* pid */, Process.SYSTEM_UID);
+ final Session session = createTestSession(mAtm, wpc.getPid(), wpc.mUid);
+ spyOn(session);
+ assertTrue(session.mCanAddInternalSystemWindow);
+ final WindowSurfaceController winSurface = mock(WindowSurfaceController.class);
+ session.onWindowSurfaceVisibilityChanged(winSurface, true /* visible */,
+ LayoutParams.TYPE_PHONE);
+ verify(session).setHasOverlayUi(true);
+ session.onWindowSurfaceVisibilityChanged(winSurface, false /* visible */,
+ LayoutParams.TYPE_PHONE);
+ verify(session).setHasOverlayUi(false);
+ }
+
+ @Test
public void testRelayoutExitingWindow() {
final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, "appWin");
final WindowSurfaceController surfaceController = mock(WindowSurfaceController.class);
@@ -824,7 +843,8 @@ public class WindowManagerServiceTests extends WindowTestsBase {
}
@Test
- public void addBlockScreenCaptureForApps() {
+ @RequiresFlagsEnabled(FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION)
+ public void shouldBlockScreenCaptureForNotificationApps() {
String testPackage = "test";
int ownerId1 = 20;
int ownerId2 = 21;
@@ -834,12 +854,59 @@ public class WindowManagerServiceTests extends WindowTestsBase {
WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
wmInternal.addBlockScreenCaptureForApps(blockedPackages);
+ verify(mWm).refreshScreenCaptureDisabled();
+
+ // window client token parameter is ignored for this feature.
+ assertTrue(mWm.mSensitiveContentPackages
+ .shouldBlockScreenCaptureForApp(testPackage, ownerId1, new Binder()));
+ assertFalse(mWm.mSensitiveContentPackages
+ .shouldBlockScreenCaptureForApp(testPackage, ownerId2, new Binder()));
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_SENSITIVE_CONTENT_APP_PROTECTION)
+ public void shouldBlockScreenCaptureForSensitiveContentOnScreenApps() {
+ String testPackage = "test";
+ int ownerId1 = 20;
+ final IBinder windowClientToken = new Binder("window client token");
+ PackageInfo blockedPackage = new PackageInfo(testPackage, ownerId1, windowClientToken);
+ ArraySet<PackageInfo> blockedPackages = new ArraySet();
+ blockedPackages.add(blockedPackage);
+
+ WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
+ wmInternal.addBlockScreenCaptureForApps(blockedPackages);
+ verify(mWm).refreshScreenCaptureDisabled();
assertTrue(mWm.mSensitiveContentPackages
- .shouldBlockScreenCaptureForApp(testPackage, ownerId1));
+ .shouldBlockScreenCaptureForApp(testPackage, ownerId1, windowClientToken));
assertFalse(mWm.mSensitiveContentPackages
- .shouldBlockScreenCaptureForApp(testPackage, ownerId2));
+ .shouldBlockScreenCaptureForApp(testPackage, ownerId1, new Binder()));
+ }
+
+ @Test
+ @RequiresFlagsEnabled(
+ {FLAG_SENSITIVE_CONTENT_APP_PROTECTION, FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION})
+ public void shouldBlockScreenCaptureForApps() {
+ String testPackage = "test";
+ int ownerId1 = 20;
+ int ownerId2 = 21;
+ final IBinder windowClientToken = new Binder("window client token");
+ PackageInfo blockedPackage1 = new PackageInfo(testPackage, ownerId1);
+ PackageInfo blockedPackage2 = new PackageInfo(testPackage, ownerId1, windowClientToken);
+ ArraySet<PackageInfo> blockedPackages = new ArraySet();
+ blockedPackages.add(blockedPackage1);
+ blockedPackages.add(blockedPackage2);
+
+ WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
+ wmInternal.addBlockScreenCaptureForApps(blockedPackages);
verify(mWm).refreshScreenCaptureDisabled();
+
+ assertTrue(mWm.mSensitiveContentPackages
+ .shouldBlockScreenCaptureForApp(testPackage, ownerId1, windowClientToken));
+ assertTrue(mWm.mSensitiveContentPackages
+ .shouldBlockScreenCaptureForApp(testPackage, ownerId1, new Binder()));
+ assertFalse(mWm.mSensitiveContentPackages
+ .shouldBlockScreenCaptureForApp(testPackage, ownerId2, new Binder()));
}
@Test
@@ -875,10 +942,6 @@ public class WindowManagerServiceTests extends WindowTestsBase {
wmInternal.addBlockScreenCaptureForApps(blockedPackages);
wmInternal.addBlockScreenCaptureForApps(blockedPackages2);
- assertTrue(mWm.mSensitiveContentPackages
- .shouldBlockScreenCaptureForApp(testPackage, ownerId1));
- assertTrue(mWm.mSensitiveContentPackages
- .shouldBlockScreenCaptureForApp(testPackage, ownerId2));
verify(mWm, times(2)).refreshScreenCaptureDisabled();
}
@@ -893,9 +956,6 @@ public class WindowManagerServiceTests extends WindowTestsBase {
WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
wmInternal.addBlockScreenCaptureForApps(blockedPackages);
wmInternal.clearBlockedApps();
-
- assertFalse(mWm.mSensitiveContentPackages
- .shouldBlockScreenCaptureForApp(testPackage, ownerId1));
verify(mWm, times(2)).refreshScreenCaptureDisabled();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index f02dd3f70feb..cd3ce9192509 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.permission.flags.Flags.FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.InsetsSource.ID_IME;
import static android.view.Surface.ROTATION_0;
@@ -55,7 +56,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.server.notification.Flags.FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION;
import static com.android.server.wm.DisplayContent.IME_TARGET_CONTROL;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
import static com.android.server.wm.WindowContainer.SYNC_STATE_WAITING_FOR_DRAW;
@@ -1424,6 +1424,25 @@ public class WindowStateTests extends WindowTestsBase {
assertFalse(window2.isSecureLocked());
}
+ @Test
+ @RequiresFlagsEnabled(FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION)
+ public void testIsSecureLocked_sensitiveContentBlockOrClearScreenCaptureForApp() {
+ String testPackage = "test";
+ int ownerId = 20;
+ final WindowState window = createWindow(null, TYPE_APPLICATION, "window", ownerId);
+ window.mAttrs.packageName = testPackage;
+ assertFalse(window.isSecureLocked());
+
+ PackageInfo blockedPackage = new PackageInfo(testPackage, ownerId);
+ ArraySet<PackageInfo> blockedPackages = new ArraySet();
+ blockedPackages.add(blockedPackage);
+ mWm.mSensitiveContentPackages.addBlockScreenCaptureForApps(blockedPackages);
+ assertTrue(window.isSecureLocked());
+
+ mWm.mSensitiveContentPackages.removeBlockScreenCaptureForApps(blockedPackages);
+ assertFalse(window.isSecureLocked());
+ }
+
private static class TestImeTargetChangeListener implements ImeTargetChangeListener {
private IBinder mImeTargetToken;
private boolean mIsRemoved;
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java
index 20a0850db6b8..ad7b9e69282e 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java
@@ -73,6 +73,8 @@ import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.SharedMemory;
+import android.service.voice.AlwaysOnHotwordDetector;
+import android.service.voice.HotwordAudioStream;
import android.service.voice.HotwordDetectedResult;
import android.service.voice.HotwordDetectionService;
import android.service.voice.HotwordDetectionServiceFailure;
@@ -81,6 +83,7 @@ import android.service.voice.HotwordRejectedResult;
import android.service.voice.IDspHotwordDetectionCallback;
import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
import android.service.voice.VisualQueryDetectionServiceFailure;
+import android.service.voice.VoiceInteractionManagerInternal.WearableHotwordDetectionCallback;
import android.text.TextUtils;
import android.util.Pair;
import android.util.Slog;
@@ -189,6 +192,7 @@ abstract class DetectorSession {
final Object mLock;
final int mVoiceInteractionServiceUid;
final Context mContext;
+ final int mUserId;
@Nullable AttentionManagerInternal mAttentionManagerInternal = null;
@@ -221,12 +225,13 @@ abstract class DetectorSession {
@NonNull IHotwordRecognitionStatusCallback callback, int voiceInteractionServiceUid,
Identity voiceInteractorIdentity,
@NonNull ScheduledExecutorService scheduledExecutorService, boolean logging,
- @NonNull DetectorRemoteExceptionListener listener) {
+ @NonNull DetectorRemoteExceptionListener listener, int userId) {
mRemoteExceptionListener = listener;
mRemoteDetectionService = remoteDetectionService;
mLock = lock;
mContext = context;
mToken = token;
+ mUserId = userId;
mCallback = callback;
mVoiceInteractionServiceUid = voiceInteractionServiceUid;
mVoiceInteractorIdentity = voiceInteractorIdentity;
@@ -405,7 +410,101 @@ abstract class DetectorSession {
audioStream,
audioFormat,
options,
- callback);
+ callback,
+ /* shouldCloseAudioStreamWithDelayOnDetect= */ true,
+ /* shouldCheckPermissionsAndAppOpsOnDetected= */ true);
+ }
+
+ void startListeningFromWearableLocked(
+ ParcelFileDescriptor audioStream,
+ AudioFormat audioFormat,
+ PersistableBundle options,
+ WearableHotwordDetectionCallback wearableCallback) {
+ if (DEBUG) {
+ Slog.d(TAG, "startListeningFromWearableLocked");
+ }
+ IMicrophoneHotwordDetectionVoiceInteractionCallback voiceInteractionCallback =
+ new IMicrophoneHotwordDetectionVoiceInteractionCallback() {
+ @Override
+ public void onDetected(
+ HotwordDetectedResult hotwordDetectedResult,
+ AudioFormat audioFormatFromCallback,
+ ParcelFileDescriptor audioStreamFromCallback) {
+ wearableCallback.onDetected();
+ try {
+ // This uses the DSP hotword code path to send the result to
+ // AlwaysOnHotwordDetector. DSP trigger and wearable trigger operates
+ // independently.
+ mCallback.onKeyphraseDetectedFromExternalSource(hotwordDetectedResult);
+ } catch (RemoteException ex) {
+ Slog.w(
+ TAG,
+ "RemoteException when sending HotwordDetectedResult to"
+ + " VoiceInteractionService.",
+ ex);
+ wearableCallback.onError(
+ "RemoteException when sending HotwordDetectedResult to"
+ + " VoiceInteractionService.");
+ notifyOnDetectorRemoteException();
+ }
+
+ // Close the local copies of the file descriptors after sending them to
+ // another process.
+ for (HotwordAudioStream resultAudioStream :
+ hotwordDetectedResult.getAudioStreams()) {
+ try {
+ resultAudioStream.getAudioStreamParcelFileDescriptor().close();
+ } catch (IOException ex) {
+ Slog.i(
+ TAG,
+ "Unable to close audio stream parcel file descriptor,",
+ ex);
+ }
+ }
+ }
+
+ @Override
+ public void onHotwordDetectionServiceFailure(
+ HotwordDetectionServiceFailure hotwordDetectionServiceFailure) {
+ wearableCallback.onError(
+ "onHotwordDetectionServiceFailure: "
+ + hotwordDetectionServiceFailure);
+ }
+
+ @Override
+ public void onRejected(HotwordRejectedResult hotwordRejectedResult) {
+ wearableCallback.onRejected();
+ }
+
+ @Override
+ public IBinder asBinder() {
+ // This callback will only be used locally within the same process.
+ return null;
+ }
+ };
+ /*
+ * By setting shouldCheckPermissionsAndAppOpsOnDetected to false, when the audio
+ * stream is sent from the sandboxed HotwordDetectionService to the non-sandboxed
+ * VoiceInteractionService as a result of second-stage hotword detection, audio-related
+ * permissions will not be checked against the VoiceInteractionService and the AppOpsManager
+ * will not be notified of the data flow to the VoiceInteractionService. These checks are
+ * not performed because the audio stream here originates from a remotely connected wearable
+ * device. It does not originate from the microphone of the device where this code runs on,
+ * or a microphone directly controlled by this system. Permission checks are expected to
+ * happen on the remote wearable device. From the perspective of this system, the audio
+ * stream is data received from an external source.
+ *
+ * Not notifying AppOpsManager allows this device's microphone indicator to remain off when
+ * this data flow happens. It avoids confusion since the audio does not originate from
+ * this device. The wearable is expected to turn on its own microphone indicator.
+ */
+ handleExternalSourceHotwordDetectionLocked(
+ audioStream,
+ audioFormat,
+ options,
+ voiceInteractionCallback,
+ /* shouldCloseAudioStreamWithDelayOnDetect= */ false,
+ /* shouldCheckPermissionsAndAppOpsOnDetected= */ false);
}
@SuppressWarnings("GuardedBy")
@@ -413,7 +512,9 @@ abstract class DetectorSession {
ParcelFileDescriptor audioStream,
AudioFormat audioFormat,
@Nullable PersistableBundle options,
- IMicrophoneHotwordDetectionVoiceInteractionCallback callback) {
+ IMicrophoneHotwordDetectionVoiceInteractionCallback callback,
+ boolean shouldCloseAudioStreamWithDelayOnDetect,
+ boolean shouldCheckPermissionsAndAppOpsOnDetected) {
if (DEBUG) {
Slog.d(TAG, "#handleExternalSourceHotwordDetectionLocked");
}
@@ -482,12 +583,22 @@ abstract class DetectorSession {
// TODO: what if we cancelled and started a new one?
mRemoteDetectionService.run(
service -> {
+ PersistableBundle optionsToSend = options;
+ if (android.app.wearable.Flags.enableHotwordWearableSensingApi()) {
+ if (optionsToSend == null) {
+ optionsToSend = new PersistableBundle();
+ }
+ optionsToSend.putBoolean(
+ HotwordDetectionService
+ .KEY_SYSTEM_WILL_CLOSE_AUDIO_STREAM_AFTER_CALLBACK,
+ shouldCloseAudioStreamWithDelayOnDetect);
+ }
service.detectFromMicrophoneSource(
serviceAudioSource,
// TODO: consider making a proxy callback + copy of audio format
AUDIO_SOURCE_EXTERNAL,
audioFormat,
- options,
+ optionsToSend,
new IDspHotwordDetectionCallback.Stub() {
@Override
public void onRejected(HotwordRejectedResult result)
@@ -530,41 +641,54 @@ abstract class DetectorSession {
getDetectorType(),
METRICS_EXTERNAL_SOURCE_DETECTED,
mVoiceInteractionServiceUid);
- mScheduledExecutorService.schedule(
- () -> {
- bestEffortClose(serviceAudioSink, audioSource);
- },
- EXTERNAL_HOTWORD_CLEANUP_MILLIS,
- TimeUnit.MILLISECONDS);
-
- try {
- enforcePermissionsForDataDelivery();
- } catch (SecurityException e) {
- Slog.w(TAG, "Ignoring #onDetected due to a "
- + "SecurityException", e);
- HotwordMetricsLogger.writeDetectorEvent(
- getDetectorType(),
- EXTERNAL_SOURCE_DETECT_SECURITY_EXCEPTION,
- mVoiceInteractionServiceUid);
+ if (shouldCloseAudioStreamWithDelayOnDetect) {
+ mScheduledExecutorService.schedule(
+ () -> {
+ bestEffortClose(
+ serviceAudioSink, audioSource);
+ },
+ EXTERNAL_HOTWORD_CLEANUP_MILLIS,
+ TimeUnit.MILLISECONDS);
+ }
+ if (shouldCheckPermissionsAndAppOpsOnDetected) {
try {
- callback.onHotwordDetectionServiceFailure(
+ enforcePermissionsForDataDelivery();
+ } catch (SecurityException e) {
+ Slog.w(
+ TAG,
+ "Ignoring #onDetected due to a "
+ + "SecurityException",
+ e);
+ HotwordMetricsLogger.writeDetectorEvent(
+ getDetectorType(),
+ EXTERNAL_SOURCE_DETECT_SECURITY_EXCEPTION,
+ mVoiceInteractionServiceUid);
+ try {
+ callback.onHotwordDetectionServiceFailure(
new HotwordDetectionServiceFailure(
ONDETECTED_GOT_SECURITY_EXCEPTION,
"Security exception occurs in "
+ "#onDetected method"));
- } catch (RemoteException e1) {
- notifyOnDetectorRemoteException();
- throw e1;
+ } catch (RemoteException e1) {
+ notifyOnDetectorRemoteException();
+ throw e1;
+ }
+ return;
}
- return;
}
HotwordDetectedResult newResult;
try {
- newResult = mHotwordAudioStreamCopier
- .startCopyingAudioStreams(triggerResult);
+ newResult =
+ mHotwordAudioStreamCopier
+ .startCopyingAudioStreams(
+ triggerResult,
+ shouldCheckPermissionsAndAppOpsOnDetected);
} catch (IOException e) {
- Slog.w(TAG, "Ignoring #onDetected due to a "
- + "IOException", e);
+ Slog.w(
+ TAG,
+ "Ignoring #onDetected due to a "
+ + "IOException",
+ e);
// TODO: Write event
try {
callback.onHotwordDetectionServiceFailure(
@@ -578,7 +702,12 @@ abstract class DetectorSession {
return;
}
try {
- callback.onDetected(newResult, /* audioFormat= */ null,
+ // The ParcelFileDescriptors in newResult might be
+ // closed after this call. Parcelling newResult can
+ // throw an exception
+ callback.onDetected(
+ newResult,
+ /* audioFormat= */ null,
/* audioStream= */ null);
} catch (RemoteException e) {
notifyOnDetectorRemoteException();
@@ -588,8 +717,7 @@ abstract class DetectorSession {
+ HotwordDetectedResult.getUsageSize(newResult)
+ " bits from hotword trusted process");
if (mDebugHotwordLogging) {
- Slog.i(TAG,
- "Egressed detected result: " + newResult);
+ Slog.i(TAG, "Egressed detected result: " + newResult);
}
}
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DspTrustedHotwordDetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DspTrustedHotwordDetectorSession.java
index 9a4fbdc4516a..8d08c6bb5e4b 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DspTrustedHotwordDetectorSession.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DspTrustedHotwordDetectorSession.java
@@ -87,10 +87,10 @@ final class DspTrustedHotwordDetectorSession extends DetectorSession {
@NonNull IHotwordRecognitionStatusCallback callback, int voiceInteractionServiceUid,
Identity voiceInteractorIdentity,
@NonNull ScheduledExecutorService scheduledExecutorService, boolean logging,
- @NonNull DetectorRemoteExceptionListener listener) {
+ @NonNull DetectorRemoteExceptionListener listener, int userId) {
super(remoteHotwordDetectionService, lock, context, token, callback,
voiceInteractionServiceUid, voiceInteractorIdentity, scheduledExecutorService,
- logging, listener);
+ logging, listener, userId);
}
@SuppressWarnings("GuardedBy")
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordAudioStreamCopier.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordAudioStreamCopier.java
index 65c95d1261f3..6f00dc82f42b 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordAudioStreamCopier.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordAudioStreamCopier.java
@@ -87,18 +87,31 @@ final class HotwordAudioStreamCopier {
}
/**
+ * Calls {@link #startCopyingAudioStreams(HotwordDetectedResult, boolean)} and notifies
+ * AppOpsManager of the {@link AppOpsManager#OPSTR_RECORD_AUDIO_HOTWORD} operation.
+ */
+ @NonNull
+ public HotwordDetectedResult startCopyingAudioStreams(@NonNull HotwordDetectedResult result)
+ throws IOException {
+ return startCopyingAudioStreams(result, /* shouldNotifyAppOpsManager= */ true);
+ }
+
+ /**
* Starts copying the audio streams in the given {@link HotwordDetectedResult}.
- * <p>
- * The returned {@link HotwordDetectedResult} is identical the one that was passed in, except
+ *
+ * <p>The returned {@link HotwordDetectedResult} is identical the one that was passed in, except
* that the {@link ParcelFileDescriptor}s within {@link HotwordDetectedResult#getAudioStreams()}
* are replaced with descriptors from pipes managed by {@link HotwordAudioStreamCopier}. The
* returned value should be passed on to the client (i.e., the voice interactor).
- * </p>
*
+ * @param result The {@link HotwordDetectedResult} to copy from.
+ * @param shouldNotifyAppOpsManager Whether the {@link AppOpsManager} should be notified of the
+ * {@link AppOpsManager#OPSTR_RECORD_AUDIO_HOTWORD} operation during the copy.
* @throws IOException If there was an error creating the managed pipe.
*/
@NonNull
- public HotwordDetectedResult startCopyingAudioStreams(@NonNull HotwordDetectedResult result)
+ public HotwordDetectedResult startCopyingAudioStreams(
+ @NonNull HotwordDetectedResult result, boolean shouldNotifyAppOpsManager)
throws IOException {
List<HotwordAudioStream> audioStreams = result.getAudioStreams();
if (audioStreams.isEmpty()) {
@@ -154,8 +167,12 @@ final class HotwordAudioStreamCopier {
String resultTaskId = TASK_ID_PREFIX + System.identityHashCode(result);
mExecutorService.execute(
- new HotwordDetectedResultCopyTask(resultTaskId, copyTaskInfos,
- totalMetadataBundleSizeBytes, totalInitialAudioSizeBytes));
+ new HotwordDetectedResultCopyTask(
+ resultTaskId,
+ copyTaskInfos,
+ totalMetadataBundleSizeBytes,
+ totalInitialAudioSizeBytes,
+ shouldNotifyAppOpsManager));
return result.buildUpon().setAudioStreams(newAudioStreams).build();
}
@@ -178,13 +195,19 @@ final class HotwordAudioStreamCopier {
private final int mTotalMetadataSizeBytes;
private final int mTotalInitialAudioSizeBytes;
private final ExecutorService mExecutorService = Executors.newCachedThreadPool();
-
- HotwordDetectedResultCopyTask(String resultTaskId, List<CopyTaskInfo> copyTaskInfos,
- int totalMetadataSizeBytes, int totalInitialAudioSizeBytes) {
+ private final boolean mShouldNotifyAppOpsManager;
+
+ HotwordDetectedResultCopyTask(
+ String resultTaskId,
+ List<CopyTaskInfo> copyTaskInfos,
+ int totalMetadataSizeBytes,
+ int totalInitialAudioSizeBytes,
+ boolean shouldNotifyAppOpsManager) {
mResultTaskId = resultTaskId;
mCopyTaskInfos = copyTaskInfos;
mTotalMetadataSizeBytes = totalMetadataSizeBytes;
mTotalInitialAudioSizeBytes = totalInitialAudioSizeBytes;
+ mShouldNotifyAppOpsManager = shouldNotifyAppOpsManager;
}
@Override
@@ -200,55 +223,14 @@ final class HotwordAudioStreamCopier {
mVoiceInteractorUid));
}
- if (mAppOpsManager.startOpNoThrow(AppOpsManager.OPSTR_RECORD_AUDIO_HOTWORD,
- mVoiceInteractorUid, mVoiceInteractorPackageName,
- mVoiceInteractorAttributionTag, OP_MESSAGE) == MODE_ALLOWED) {
- try {
- HotwordMetricsLogger.writeAudioEgressEvent(mDetectorType,
- HOTWORD_AUDIO_EGRESS_EVENT_REPORTED__EVENT__STARTED,
- mVoiceInteractorUid, mTotalInitialAudioSizeBytes,
- mTotalMetadataSizeBytes, size);
- // TODO(b/244599891): Set timeout, close after inactivity
- mExecutorService.invokeAll(tasks);
-
- // We are including the non-streamed initial audio
- // (HotwordAudioStream.getInitialAudio()) bytes in the "stream" size metrics.
- int totalStreamSizeBytes = mTotalInitialAudioSizeBytes;
- for (SingleAudioStreamCopyTask task : tasks) {
- totalStreamSizeBytes += task.mTotalCopiedBytes;
- }
-
- Slog.i(TAG, mResultTaskId + ": Task was completed. Total bytes egressed: "
- + totalStreamSizeBytes + " (including " + mTotalInitialAudioSizeBytes
- + " bytes NOT streamed), total metadata bundle size bytes: "
- + mTotalMetadataSizeBytes);
- HotwordMetricsLogger.writeAudioEgressEvent(mDetectorType,
- HOTWORD_AUDIO_EGRESS_EVENT_REPORTED__EVENT__ENDED,
- mVoiceInteractorUid, totalStreamSizeBytes, mTotalMetadataSizeBytes,
- size);
- } catch (InterruptedException e) {
- // We are including the non-streamed initial audio
- // (HotwordAudioStream.getInitialAudio()) bytes in the "stream" size metrics.
- int totalStreamSizeBytes = mTotalInitialAudioSizeBytes;
- for (SingleAudioStreamCopyTask task : tasks) {
- totalStreamSizeBytes += task.mTotalCopiedBytes;
- }
-
- HotwordMetricsLogger.writeAudioEgressEvent(mDetectorType,
- HOTWORD_AUDIO_EGRESS_EVENT_REPORTED__EVENT__INTERRUPTED_EXCEPTION,
- mVoiceInteractorUid, totalStreamSizeBytes, mTotalMetadataSizeBytes,
- size);
- Slog.i(TAG, mResultTaskId + ": Task was interrupted. Total bytes egressed: "
- + totalStreamSizeBytes + " (including " + mTotalInitialAudioSizeBytes
- + " bytes NOT streamed), total metadata bundle size bytes: "
- + mTotalMetadataSizeBytes);
- bestEffortPropagateError(e.getMessage());
- } finally {
- mAppOpsManager.finishOp(AppOpsManager.OPSTR_RECORD_AUDIO_HOTWORD,
- mVoiceInteractorUid, mVoiceInteractorPackageName,
- mVoiceInteractorAttributionTag);
- }
- } else {
+ if (mShouldNotifyAppOpsManager
+ && mAppOpsManager.startOpNoThrow(
+ AppOpsManager.OPSTR_RECORD_AUDIO_HOTWORD,
+ mVoiceInteractorUid,
+ mVoiceInteractorPackageName,
+ mVoiceInteractorAttributionTag,
+ OP_MESSAGE)
+ != MODE_ALLOWED) {
HotwordMetricsLogger.writeAudioEgressEvent(mDetectorType,
HOTWORD_AUDIO_EGRESS_EVENT_REPORTED__EVENT__NO_PERMISSION,
mVoiceInteractorUid, /* streamSizeBytes= */ 0, /* bundleSizeBytes= */ 0,
@@ -258,6 +240,56 @@ final class HotwordAudioStreamCopier {
+ " uid=" + mVoiceInteractorUid
+ " packageName=" + mVoiceInteractorPackageName
+ " attributionTag=" + mVoiceInteractorAttributionTag);
+ return;
+ }
+ try {
+ HotwordMetricsLogger.writeAudioEgressEvent(mDetectorType,
+ HOTWORD_AUDIO_EGRESS_EVENT_REPORTED__EVENT__STARTED,
+ mVoiceInteractorUid, mTotalInitialAudioSizeBytes,
+ mTotalMetadataSizeBytes, size);
+ // TODO(b/244599891): Set timeout, close after inactivity
+ mExecutorService.invokeAll(tasks);
+
+ // We are including the non-streamed initial audio
+ // (HotwordAudioStream.getInitialAudio()) bytes in the "stream" size metrics.
+ int totalStreamSizeBytes = mTotalInitialAudioSizeBytes;
+ for (SingleAudioStreamCopyTask task : tasks) {
+ totalStreamSizeBytes += task.mTotalCopiedBytes;
+ }
+
+ Slog.i(TAG, mResultTaskId + ": Task was completed. Total bytes egressed: "
+ + totalStreamSizeBytes + " (including " + mTotalInitialAudioSizeBytes
+ + " bytes NOT streamed), total metadata bundle size bytes: "
+ + mTotalMetadataSizeBytes);
+ HotwordMetricsLogger.writeAudioEgressEvent(mDetectorType,
+ HOTWORD_AUDIO_EGRESS_EVENT_REPORTED__EVENT__ENDED,
+ mVoiceInteractorUid, totalStreamSizeBytes, mTotalMetadataSizeBytes,
+ size);
+ } catch (InterruptedException e) {
+ // We are including the non-streamed initial audio
+ // (HotwordAudioStream.getInitialAudio()) bytes in the "stream" size metrics.
+ int totalStreamSizeBytes = mTotalInitialAudioSizeBytes;
+ for (SingleAudioStreamCopyTask task : tasks) {
+ totalStreamSizeBytes += task.mTotalCopiedBytes;
+ }
+
+ HotwordMetricsLogger.writeAudioEgressEvent(mDetectorType,
+ HOTWORD_AUDIO_EGRESS_EVENT_REPORTED__EVENT__INTERRUPTED_EXCEPTION,
+ mVoiceInteractorUid, totalStreamSizeBytes, mTotalMetadataSizeBytes,
+ size);
+ Slog.i(TAG, mResultTaskId + ": Task was interrupted. Total bytes egressed: "
+ + totalStreamSizeBytes + " (including " + mTotalInitialAudioSizeBytes
+ + " bytes NOT streamed), total metadata bundle size bytes: "
+ + mTotalMetadataSizeBytes);
+ bestEffortPropagateError(e.getMessage());
+ } finally {
+ if (mShouldNotifyAppOpsManager) {
+ mAppOpsManager.finishOp(
+ AppOpsManager.OPSTR_RECORD_AUDIO_HOTWORD,
+ mVoiceInteractorUid,
+ mVoiceInteractorPackageName,
+ mVoiceInteractorAttributionTag);
+ }
}
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index cd390a17fc4d..cfcc04b10107 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -63,6 +63,7 @@ import android.service.voice.SoundTriggerFailure;
import android.service.voice.VisualQueryDetectionService;
import android.service.voice.VisualQueryDetectionServiceFailure;
import android.service.voice.VoiceInteractionManagerInternal.HotwordDetectionServiceIdentity;
+import android.service.voice.VoiceInteractionManagerInternal.WearableHotwordDetectionCallback;
import android.speech.IRecognitionServiceManager;
import android.util.Slog;
import android.util.SparseArray;
@@ -71,6 +72,7 @@ import android.view.contentcapture.IContentCaptureManager;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IHotwordRecognitionStatusCallback;
import com.android.internal.app.IVisualQueryDetectionAttentionListener;
+import com.android.internal.app.IVoiceInteractionAccessibilitySettingsListener;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.infra.ServiceConnector;
import com.android.server.LocalServices;
@@ -146,8 +148,9 @@ final class HotwordDetectionConnection {
final int mVoiceInteractionServiceUid;
final ComponentName mHotwordDetectionComponentName;
final ComponentName mVisualQueryDetectionComponentName;
- final int mUser;
+ final int mUserId;
final Context mContext;
+ final AccessibilitySettingsListener mAccessibilitySettingsListener;
volatile HotwordDetectionServiceIdentity mIdentity;
//TODO: Consider rename this to SandboxedDetectionIdentity
private Instant mLastRestartInstant;
@@ -203,6 +206,27 @@ final class HotwordDetectionConnection {
}
};
+ /** Listen to changes of {@link Settings.Secure.VISUAL_QUERY_ACCESSIBILITY_DETECTION_ENABLED}.
+ *
+ * This is registered to the {@link VoiceInteractionManagerServiceImpl} where all settings
+ * listeners are centralized and notified.
+ */
+ private final class AccessibilitySettingsListener extends
+ IVoiceInteractionAccessibilitySettingsListener.Stub {
+ @Override
+ public void onAccessibilityDetectionChanged(boolean enable) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "Update settings change: " + enable);
+ }
+ VisualQueryDetectorSession session = getVisualQueryDetectorSessionLocked();
+ if (session != null) {
+ session.updateAccessibilityEgressStateLocked(enable);
+ }
+ }
+ }
+ }
+
HotwordDetectionConnection(Object lock, Context context, int voiceInteractionServiceUid,
Identity voiceInteractorIdentity, ComponentName hotwordDetectionServiceName,
@@ -215,11 +239,12 @@ final class HotwordDetectionConnection {
mVoiceInteractorIdentity = voiceInteractorIdentity;
mHotwordDetectionComponentName = hotwordDetectionServiceName;
mVisualQueryDetectionComponentName = visualQueryDetectionServiceName;
- mUser = userId;
+ mUserId = userId;
mDetectorType = detectorType;
mRemoteExceptionListener = listener;
mReStartPeriodSeconds = DeviceConfig.getInt(DeviceConfig.NAMESPACE_VOICE_INTERACTION,
KEY_RESTART_PERIOD_IN_SECONDS, 0);
+ mAccessibilitySettingsListener = new AccessibilitySettingsListener();
final Intent hotwordDetectionServiceIntent =
new Intent(HotwordDetectionService.SERVICE_INTERFACE);
@@ -451,6 +476,25 @@ final class HotwordDetectionConnection {
session.startListeningFromExternalSourceLocked(audioStream, audioFormat, options, callback);
}
+ public void startListeningFromWearableLocked(
+ ParcelFileDescriptor audioStream,
+ AudioFormat audioFormat,
+ PersistableBundle options,
+ WearableHotwordDetectionCallback callback) {
+ if (DEBUG) {
+ Slog.d(TAG, "startListeningFromWearableLocked");
+ }
+ DetectorSession trustedSession = getDspTrustedHotwordDetectorSessionLocked();
+ if (trustedSession == null) {
+ callback.onError(
+ "Unable to start listening from wearable because the trusted hotword detection"
+ + " session is not available.");
+ return;
+ }
+ trustedSession.startListeningFromWearableLocked(
+ audioStream, audioFormat, options, callback);
+ }
+
/**
* This method is only used by SoftwareHotwordDetector.
*/
@@ -772,7 +816,7 @@ final class HotwordDetectionConnection {
ServiceConnection createLocked() {
ServiceConnection connection =
- new ServiceConnection(mContext, mIntent, mBindingFlags, mUser,
+ new ServiceConnection(mContext, mIntent, mBindingFlags, mUserId,
ISandboxedDetectionService.Stub::asInterface,
mRestartCount % MAX_ISOLATED_PROCESS_NUMBER, mDetectionServiceType);
connection.connect();
@@ -978,7 +1022,7 @@ final class HotwordDetectionConnection {
session = new DspTrustedHotwordDetectorSession(mRemoteHotwordDetectionService,
mLock, mContext, token, callback, mVoiceInteractionServiceUid,
mVoiceInteractorIdentity, mScheduledExecutorService, mDebugHotwordLogging,
- mRemoteExceptionListener);
+ mRemoteExceptionListener, mUserId);
} else if (detectorType == HotwordDetector.DETECTOR_TYPE_VISUAL_QUERY_DETECTOR) {
if (mRemoteVisualQueryDetectionService == null) {
mRemoteVisualQueryDetectionService =
@@ -987,7 +1031,8 @@ final class HotwordDetectionConnection {
session = new VisualQueryDetectorSession(
mRemoteVisualQueryDetectionService, mLock, mContext, token, callback,
mVoiceInteractionServiceUid, mVoiceInteractorIdentity,
- mScheduledExecutorService, mDebugHotwordLogging, mRemoteExceptionListener);
+ mScheduledExecutorService, mDebugHotwordLogging, mRemoteExceptionListener,
+ mUserId);
} else {
if (mRemoteHotwordDetectionService == null) {
mRemoteHotwordDetectionService =
@@ -996,7 +1041,8 @@ final class HotwordDetectionConnection {
session = new SoftwareTrustedHotwordDetectorSession(
mRemoteHotwordDetectionService, mLock, mContext, token, callback,
mVoiceInteractionServiceUid, mVoiceInteractorIdentity,
- mScheduledExecutorService, mDebugHotwordLogging, mRemoteExceptionListener);
+ mScheduledExecutorService, mDebugHotwordLogging,
+ mRemoteExceptionListener, mUserId);
}
mHotwordRecognitionCallback = callback;
mDetectorSessions.put(detectorType, session);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoftwareTrustedHotwordDetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoftwareTrustedHotwordDetectorSession.java
index f06c99729a19..120c161c1903 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoftwareTrustedHotwordDetectorSession.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoftwareTrustedHotwordDetectorSession.java
@@ -74,10 +74,10 @@ final class SoftwareTrustedHotwordDetectorSession extends DetectorSession {
@NonNull IHotwordRecognitionStatusCallback callback, int voiceInteractionServiceUid,
Identity voiceInteractorIdentity,
@NonNull ScheduledExecutorService scheduledExecutorService, boolean logging,
- @NonNull DetectorRemoteExceptionListener listener) {
+ @NonNull DetectorRemoteExceptionListener listener, int userId) {
super(remoteHotwordDetectionService, lock, context, token, callback,
voiceInteractionServiceUid, voiceInteractorIdentity, scheduledExecutorService,
- logging, listener);
+ logging, listener, userId);
}
@SuppressWarnings("GuardedBy")
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VisualQueryDetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VisualQueryDetectorSession.java
index e4ac993f2d50..aef8e6fabc9b 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VisualQueryDetectorSession.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VisualQueryDetectorSession.java
@@ -29,6 +29,7 @@ import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.SharedMemory;
+import android.provider.Settings;
import android.service.voice.IDetectorSessionVisualQueryDetectionCallback;
import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
import android.service.voice.ISandboxedDetectionService;
@@ -60,6 +61,7 @@ final class VisualQueryDetectorSession extends DetectorSession {
private IVisualQueryDetectionAttentionListener mAttentionListener;
private boolean mEgressingData;
private boolean mQueryStreaming;
+ private boolean mEnableAccessibilityDataEgress;
//TODO(b/261783819): Determines actual functionalities, e.g., startRecognition etc.
VisualQueryDetectorSession(
@@ -68,13 +70,17 @@ final class VisualQueryDetectorSession extends DetectorSession {
@NonNull IHotwordRecognitionStatusCallback callback, int voiceInteractionServiceUid,
Identity voiceInteractorIdentity,
@NonNull ScheduledExecutorService scheduledExecutorService, boolean logging,
- @NonNull DetectorRemoteExceptionListener listener) {
+ @NonNull DetectorRemoteExceptionListener listener, int userId) {
super(remoteService, lock, context, token, callback,
voiceInteractionServiceUid, voiceInteractorIdentity, scheduledExecutorService,
- logging, listener);
+ logging, listener, userId);
mEgressingData = false;
mQueryStreaming = false;
mAttentionListener = null;
+ mEnableAccessibilityDataEgress = Settings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.VISUAL_QUERY_ACCESSIBILITY_DETECTION_ENABLED, 0,
+ mUserId) == 1;
// TODO: handle notify RemoteException to client
}
@@ -186,6 +192,16 @@ final class VisualQueryDetectorSession extends DetectorSession {
"Cannot stream results without attention signals."));
return;
}
+ if (!checkDetectedResultDataLocked(partialResult)) {
+ Slog.v(TAG, "Accessibility data can be egressed only when the "
+ + "isAccessibilityDetectionEnabled() is true.");
+ callback.onVisualQueryDetectionServiceFailure(
+ new VisualQueryDetectionServiceFailure(
+ ERROR_CODE_ILLEGAL_STREAMING_STATE,
+ "Cannot stream accessibility data without "
+ + "enabling the setting."));
+ return;
+ }
mQueryStreaming = true;
callback.onResultDetected(partialResult);
Slog.i(TAG, "Egressed from visual query detection process.");
@@ -227,6 +243,12 @@ final class VisualQueryDetectorSession extends DetectorSession {
mQueryStreaming = false;
}
}
+
+ @SuppressWarnings("GuardedBy")
+ private boolean checkDetectedResultDataLocked(VisualQueryDetectedResult result) {
+ return result.getAccessibilityDetectionData() == null
+ || mEnableAccessibilityDataEgress;
+ }
};
return mRemoteDetectionService.run(
service -> service.detectWithVisualSignals(internalCallback));
@@ -251,6 +273,12 @@ final class VisualQueryDetectorSession extends DetectorSession {
+ " should not be called from VisualQueryDetectorSession.");
}
+ void updateAccessibilityEgressStateLocked(boolean enable) {
+ if (DEBUG) {
+ Slog.d(TAG, "updateAccessibilityEgressStateLocked");
+ }
+ mEnableAccessibilityDataEgress = enable;
+ }
@SuppressWarnings("GuardedBy")
public void dumpLocked(String prefix, PrintWriter pw) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 4cdec705bb6c..c217780d90d6 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.voiceinteraction;
+
import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
@@ -74,6 +75,7 @@ import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback
import android.service.voice.IVisualQueryDetectionVoiceInteractionCallback;
import android.service.voice.IVoiceInteractionSession;
import android.service.voice.VoiceInteractionManagerInternal;
+import android.service.voice.VoiceInteractionManagerInternal.WearableHotwordDetectionCallback;
import android.service.voice.VoiceInteractionService;
import android.service.voice.VoiceInteractionServiceInfo;
import android.service.voice.VoiceInteractionSession;
@@ -89,6 +91,7 @@ import com.android.internal.app.IHotwordRecognitionStatusCallback;
import com.android.internal.app.IVisualQueryDetectionAttentionListener;
import com.android.internal.app.IVisualQueryRecognitionStatusListener;
import com.android.internal.app.IVoiceActionCheckCallback;
+import com.android.internal.app.IVoiceInteractionAccessibilitySettingsListener;
import com.android.internal.app.IVoiceInteractionManagerService;
import com.android.internal.app.IVoiceInteractionSessionListener;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
@@ -319,6 +322,46 @@ public class VoiceInteractionManagerService extends SystemService {
mServiceStub.mRoleObserver.onRoleHoldersChanged(RoleManager.ROLE_ASSISTANT,
UserHandle.of(userId));
}
+
+ @Override
+ public void startListeningFromWearable(
+ ParcelFileDescriptor audioStreamFromWearable,
+ AudioFormat audioFormatFromWearable,
+ PersistableBundle options,
+ ComponentName targetVisComponentName,
+ int userId,
+ WearableHotwordDetectionCallback callback) {
+ Slog.d(TAG, "#startListeningFromWearable");
+ VoiceInteractionManagerServiceImpl impl = mServiceStub.mImpl;
+ if (impl == null) {
+ callback.onError(
+ "Unable to start listening from wearable because the service impl is"
+ + " null.");
+ return;
+ }
+ if (targetVisComponentName != null && !targetVisComponentName.equals(impl.mComponent)) {
+ callback.onError(
+ TextUtils.formatSimple(
+ "Unable to start listening from wearable because the target"
+ + " VoiceInteractionService %s is different from the current"
+ + " VoiceInteractionService %s",
+ targetVisComponentName, impl.mComponent));
+ return;
+ }
+ if (userId != impl.mUser) {
+ callback.onError(
+ TextUtils.formatSimple(
+ "Unable to start listening from wearable because the target userId"
+ + " %s is different from the current"
+ + " VoiceInteractionManagerServiceImpl's userId %s",
+ userId, impl.mUser));
+ return;
+ }
+ synchronized (mServiceStub) {
+ impl.startListeningFromWearableLocked(
+ audioStreamFromWearable, audioFormatFromWearable, options, callback);
+ }
+ }
}
// implementation entry point and binder service
@@ -1706,7 +1749,10 @@ public class VoiceInteractionManagerService extends SystemService {
if (keyphrase.equals(phrase.getText())) {
ArraySet<Locale> locales = new ArraySet<>();
locales.add(phrase.getLocale());
- return new KeyphraseMetadata(phrase.getId(), phrase.getText(), locales,
+ return new KeyphraseMetadata(
+ phrase.getId(),
+ phrase.getText(),
+ locales,
phrase.getRecognitionModes());
}
}
@@ -2134,6 +2180,44 @@ public class VoiceInteractionManagerService extends SystemService {
}
}
+ public boolean getAccessibilityDetectionEnabled() {
+ synchronized (this) {
+ if (mImpl == null) {
+ Slog.w(TAG, "registerAccessibilityDetectionSettingsListener called without"
+ + " running voice interaction service");
+ return false;
+ }
+ return mImpl.getAccessibilityDetectionEnabled();
+ }
+ }
+
+ @Override
+ public void registerAccessibilityDetectionSettingsListener(
+ IVoiceInteractionAccessibilitySettingsListener listener) {
+ synchronized (this) {
+ if (mImpl == null) {
+ Slog.w(TAG, "registerAccessibilityDetectionSettingsListener called without"
+ + " running voice interaction service");
+ return;
+ }
+ mImpl.registerAccessibilityDetectionSettingsListenerLocked(listener);
+ }
+ }
+
+ @Override
+ public void unregisterAccessibilityDetectionSettingsListener(
+ IVoiceInteractionAccessibilitySettingsListener listener) {
+ synchronized (this) {
+ if (mImpl == null) {
+ Slog.w(TAG, "unregisterAccessibilityDetectionSettingsListener called "
+ + "without running voice interaction service");
+ return;
+ }
+ mImpl.unregisterAccessibilityDetectionSettingsListenerLocked(listener);
+ }
+ }
+
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
@@ -2498,7 +2582,6 @@ public class VoiceInteractionManagerService extends SystemService {
if (anyPackagesAppearing()) {
initRecognizer(userHandle);
}
- return;
}
if (curInteractor != null) {
@@ -2547,15 +2630,16 @@ public class VoiceInteractionManagerService extends SystemService {
}
}
- // There is no interactor, so just deal with a simple recognizer.
- int change = isPackageDisappearing(curRecognizer.getPackageName());
- if (change == PACKAGE_PERMANENT_CHANGE
- || change == PACKAGE_TEMPORARY_CHANGE) {
- setCurRecognizer(findAvailRecognizer(null, userHandle), userHandle);
+ if (curRecognizer != null) {
+ int change = isPackageDisappearing(curRecognizer.getPackageName());
+ if (change == PACKAGE_PERMANENT_CHANGE
+ || change == PACKAGE_TEMPORARY_CHANGE) {
+ setCurRecognizer(findAvailRecognizer(null, userHandle), userHandle);
- } else if (isPackageModified(curRecognizer.getPackageName())) {
- setCurRecognizer(findAvailRecognizer(curRecognizer.getPackageName(),
- userHandle), userHandle);
+ } else if (isPackageModified(curRecognizer.getPackageName())) {
+ setCurRecognizer(findAvailRecognizer(curRecognizer.getPackageName(),
+ userHandle), userHandle);
+ }
}
}
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 7538142d094f..e34e81908632 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -36,6 +36,7 @@ import android.app.IActivityManager;
import android.app.IActivityTaskManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -45,10 +46,12 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.ServiceInfo;
+import android.database.ContentObserver;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
import android.hardware.soundtrigger.SoundTrigger;
import android.media.AudioFormat;
import android.media.permission.Identity;
+import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -60,11 +63,13 @@ import android.os.ServiceManager;
import android.os.SharedMemory;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.provider.Settings;
import android.service.voice.HotwordDetector;
import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
import android.service.voice.IVisualQueryDetectionVoiceInteractionCallback;
import android.service.voice.IVoiceInteractionService;
import android.service.voice.IVoiceInteractionSession;
+import android.service.voice.VoiceInteractionManagerInternal.WearableHotwordDetectionCallback;
import android.service.voice.VoiceInteractionService;
import android.service.voice.VoiceInteractionServiceInfo;
import android.system.OsConstants;
@@ -76,6 +81,7 @@ import android.view.IWindowManager;
import com.android.internal.app.IHotwordRecognitionStatusCallback;
import com.android.internal.app.IVisualQueryDetectionAttentionListener;
import com.android.internal.app.IVoiceActionCheckCallback;
+import com.android.internal.app.IVoiceInteractionAccessibilitySettingsListener;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.util.function.pooled.PooledLambda;
@@ -198,6 +204,10 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
}
};
+ final ArrayList<
+ IVoiceInteractionAccessibilitySettingsListener> mAccessibilitySettingsListeners =
+ new ArrayList<IVoiceInteractionAccessibilitySettingsListener>();
+
VoiceInteractionManagerServiceImpl(Context context, Handler handler,
VoiceInteractionManagerService.VoiceInteractionManagerServiceStub stub,
int userHandle, ComponentName service) {
@@ -249,6 +259,7 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
mContext.registerReceiver(mBroadcastReceiver, filter, null, handler,
Context.RECEIVER_EXPORTED);
+ new AccessibilitySettingsContentObserver().register(mContext.getContentResolver());
}
public void grantImplicitAccessLocked(int grantRecipientUid, @Nullable Intent intent) {
@@ -744,6 +755,8 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
+ "exception occurred.");
}
});
+ registerAccessibilityDetectionSettingsListenerLocked(
+ mHotwordDetectionConnection.mAccessibilitySettingsListener);
} else if (detectorType != HotwordDetector.DETECTOR_TYPE_VISUAL_QUERY_DETECTOR) {
// TODO: Logger events should be handled in session instead. Temporary adding the
// checking to prevent confusion so VisualQueryDetection events won't be logged if the
@@ -781,6 +794,8 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
return;
}
mHotwordDetectionConnection.cancelLocked();
+ unregisterAccessibilityDetectionSettingsListenerLocked(
+ mHotwordDetectionConnection.mAccessibilitySettingsListener);
mHotwordDetectionConnection = null;
}
@@ -857,6 +872,24 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
options, token, callback);
}
+ public void startListeningFromWearableLocked(
+ ParcelFileDescriptor audioStream,
+ AudioFormat audioFormat,
+ PersistableBundle options,
+ WearableHotwordDetectionCallback callback) {
+ if (DEBUG) {
+ Slog.d(TAG, "startListeningFromWearable");
+ }
+ if (mHotwordDetectionConnection == null) {
+ callback.onError(
+ "Unable to start listening from wearable because the hotword detection"
+ + " connection is null.");
+ return;
+ }
+ mHotwordDetectionConnection.startListeningFromWearableLocked(
+ audioStream, audioFormat, options, callback);
+ }
+
public void stopListeningFromMicLocked() {
if (DEBUG) {
Slog.d(TAG, "stopListeningFromMicLocked");
@@ -955,6 +988,8 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
return;
}
mHotwordDetectionConnection.cancelLocked();
+ unregisterAccessibilityDetectionSettingsListenerLocked(
+ mHotwordDetectionConnection.mAccessibilitySettingsListener);
mHotwordDetectionConnection = null;
}
@@ -996,6 +1031,29 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
}
}
+ boolean getAccessibilityDetectionEnabled() {
+ return Settings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.VISUAL_QUERY_ACCESSIBILITY_DETECTION_ENABLED, 0,
+ mUser) == 1;
+ }
+
+ void registerAccessibilityDetectionSettingsListenerLocked(
+ IVoiceInteractionAccessibilitySettingsListener listener) {
+ if (DEBUG) {
+ Slog.d(TAG, "registerAccessibilityDetectionSettingsListener");
+ }
+ mAccessibilitySettingsListeners.add(listener);
+ }
+
+ void unregisterAccessibilityDetectionSettingsListenerLocked(
+ IVoiceInteractionAccessibilitySettingsListener listener) {
+ if (DEBUG) {
+ Slog.d(TAG, "unregisterAccessibilityDetectionSettingsListener");
+ }
+ mAccessibilitySettingsListeners.remove(listener);
+ }
+
void startLocked() {
Intent intent = new Intent(VoiceInteractionService.SERVICE_INTERFACE);
intent.setComponent(mComponent);
@@ -1036,6 +1094,8 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
}
if (mHotwordDetectionConnection != null) {
mHotwordDetectionConnection.cancelLocked();
+ unregisterAccessibilityDetectionSettingsListenerLocked(
+ mHotwordDetectionConnection.mAccessibilitySettingsListener);
mHotwordDetectionConnection = null;
}
if (mBound) {
@@ -1082,4 +1142,41 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
interface DetectorRemoteExceptionListener {
void onDetectorRemoteException(@NonNull IBinder token, int detectorType);
}
+
+ private final class AccessibilitySettingsContentObserver extends ContentObserver {
+ private Uri mAccessibilitySettingsEnabledUri = Settings.Secure.getUriFor(
+ Settings.Secure.VISUAL_QUERY_ACCESSIBILITY_DETECTION_ENABLED);
+
+ AccessibilitySettingsContentObserver() {
+ super(null);
+ }
+
+ public void register(ContentResolver contentResolver) {
+ contentResolver.registerContentObserver(
+ mAccessibilitySettingsEnabledUri, false, this, UserHandle.USER_ALL);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ Slog.i(TAG, "OnChange called with uri:" + uri);
+ if (mAccessibilitySettingsEnabledUri.equals(uri)) {
+ boolean enable = Settings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.VISUAL_QUERY_ACCESSIBILITY_DETECTION_ENABLED, 0,
+ mUser) == 1;
+ Slog.i(TAG, "Notifying listeners with Accessibility setting set to "
+ + enable);
+ mAccessibilitySettingsListeners.forEach(
+ listener -> {
+ try {
+ listener.onAccessibilityDetectionChanged(enable);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ );
+
+ }
+ }
+ }
}
diff --git a/telecomm/java/Android.bp b/telecomm/java/Android.bp
index 3bd595352a55..9e9dd7d00190 100644
--- a/telecomm/java/Android.bp
+++ b/telecomm/java/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_fwk_telephony",
// 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"
diff --git a/telecomm/java/android/telecom/CallAttributes.java b/telecomm/java/android/telecom/CallAttributes.java
index 8c6e101f2c03..afd34fc74e43 100644
--- a/telecomm/java/android/telecom/CallAttributes.java
+++ b/telecomm/java/android/telecom/CallAttributes.java
@@ -16,6 +16,7 @@
package android.telecom;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -24,6 +25,8 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import com.android.server.telecom.flags.Flags;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
@@ -113,7 +116,8 @@ public final class CallAttributes implements Parcelable {
public static final int VIDEO_CALL = 2;
/** @hide */
- @IntDef(value = {SUPPORTS_SET_INACTIVE, SUPPORTS_STREAM, SUPPORTS_TRANSFER}, flag = true)
+ @IntDef(value = {SUPPORTS_SET_INACTIVE, SUPPORTS_STREAM, SUPPORTS_TRANSFER,
+ SUPPORTS_VIDEO_CALLING}, flag = true)
@Retention(RetentionPolicy.SOURCE)
public @interface CallCapability {
}
@@ -133,6 +137,12 @@ public final class CallAttributes implements Parcelable {
* The call can be completely transferred from one endpoint to another.
*/
public static final int SUPPORTS_TRANSFER = 1 << 3;
+ /**
+ * The call supports video calling. This allows clients to gate video calling on a per call
+ * basis as opposed to re-registering the phone account.
+ */
+ @FlaggedApi(Flags.FLAG_TRANSACTIONAL_VIDEO_STATE)
+ public static final int SUPPORTS_VIDEO_CALLING = 1 << 4;
/**
* Build an instance of {@link CallAttributes}. In order to build a valid instance, a
diff --git a/telecomm/java/android/telecom/CallAudioState.java b/telecomm/java/android/telecom/CallAudioState.java
index c7cc1bd88bdf..49e9232ad535 100644
--- a/telecomm/java/android/telecom/CallAudioState.java
+++ b/telecomm/java/android/telecom/CallAudioState.java
@@ -199,6 +199,16 @@ public final class CallAudioState implements Parcelable {
}
/**
+ * @return Bit mask of all routes supported by this call, won't be changed by streaming state.
+ *
+ * @hide
+ */
+ @CallAudioRoute
+ public int getRawSupportedRouteMask() {
+ return supportedRouteMask;
+ }
+
+ /**
* @return The {@link BluetoothDevice} through which audio is being routed.
* Will not be {@code null} if {@link #getRoute()} returns {@link #ROUTE_BLUETOOTH}.
*/
diff --git a/telecomm/java/android/telecom/CallControl.java b/telecomm/java/android/telecom/CallControl.java
index a14078697c71..808a57589b47 100644
--- a/telecomm/java/android/telecom/CallControl.java
+++ b/telecomm/java/android/telecom/CallControl.java
@@ -293,12 +293,50 @@ public final class CallControl {
try {
mServerInterface.setMuteState(isMuted,
new CallControlResultReceiver("requestMuteState", executor, callback));
-
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
+ /**
+ * Request a new video state for the ongoing call. This can only be changed if the application
+ * has registered a {@link PhoneAccount} with the
+ * {@link PhoneAccount#CAPABILITY_SUPPORTS_VIDEO_CALLING} and set the
+ * {@link CallAttributes#SUPPORTS_VIDEO_CALLING} when adding the call via
+ * {@link TelecomManager#addCall(CallAttributes, Executor, OutcomeReceiver,
+ * CallControlCallback, CallEventCallback)}
+ *
+ * @param videoState to report to Telecom. To see the valid argument to pass,
+ * see {@link CallAttributes.CallType}.
+ * @param executor The {@link Executor} on which the {@link OutcomeReceiver} callback
+ * will be called on.
+ * @param callback that will be completed on the Telecom side that details success or failure
+ * of the requested operation.
+ *
+ * {@link OutcomeReceiver#onResult} will be called if Telecom has successfully
+ * switched the video state.
+ *
+ * {@link OutcomeReceiver#onError} will be called if Telecom has failed to set
+ * the new video state. A {@link CallException} will be passed
+ * that details why the operation failed.
+ * @throws IllegalArgumentException if the argument passed for videoState is invalid. To see a
+ * list of valid states, see {@link CallAttributes.CallType}.
+ */
+ @FlaggedApi(Flags.FLAG_TRANSACTIONAL_VIDEO_STATE)
+ public void requestVideoState(@CallAttributes.CallType int videoState,
+ @CallbackExecutor @NonNull Executor executor,
+ @NonNull OutcomeReceiver<Void, CallException> callback) {
+ validateVideoState(videoState);
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+ try {
+ mServerInterface.requestVideoState(videoState, mCallId,
+ new CallControlResultReceiver("requestVideoState", executor, callback));
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
/**
* Raises an event to the {@link android.telecom.InCallService} implementations tracking this
* call via {@link android.telecom.Call.Callback#onConnectionEvent(Call, String, Bundle)}.
diff --git a/telecomm/java/android/telecom/CallEventCallback.java b/telecomm/java/android/telecom/CallEventCallback.java
index a41c0113e933..b0438bfa0289 100644
--- a/telecomm/java/android/telecom/CallEventCallback.java
+++ b/telecomm/java/android/telecom/CallEventCallback.java
@@ -16,9 +16,12 @@
package android.telecom;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.os.Bundle;
+import com.android.server.telecom.flags.Flags;
+
import java.util.List;
/**
@@ -51,6 +54,14 @@ public interface CallEventCallback {
void onMuteStateChanged(boolean isMuted);
/**
+ * Called when the video state changes.
+ *
+ * @param videoState The current video state.
+ */
+ @FlaggedApi(Flags.FLAG_TRANSACTIONAL_VIDEO_STATE)
+ default void onVideoStateChanged(@CallAttributes.CallType int videoState) {}
+
+ /**
* Telecom is informing the client user requested call streaming but the stream can't be
* started.
*
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index 7ad26c901188..5ba5ee8836e1 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -169,8 +169,7 @@ public final class DisconnectCause implements Parcelable {
int toneToPlay) {
this(code, label, description, reason, toneToPlay,
android.telephony.DisconnectCause.ERROR_UNSPECIFIED,
- PreciseDisconnectCause.ERROR_UNSPECIFIED,
- null /* imsReasonInfo */);
+ PreciseDisconnectCause.ERROR_UNSPECIFIED, null /* imsReasonInfo */);
}
/**
@@ -187,8 +186,6 @@ public final class DisconnectCause implements Parcelable {
* @param imsReasonInfo The relevant {@link ImsReasonInfo}, or {@code null} if not available.
* @hide
*/
- @SystemApi
- @FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
public DisconnectCause(int code, @NonNull CharSequence label,
@NonNull CharSequence description, @NonNull String reason,
int toneToPlay, @Annotation.DisconnectCauses int telephonyDisconnectCause,
@@ -298,6 +295,117 @@ public final class DisconnectCause implements Parcelable {
return mToneToPlay;
}
+ /**
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
+ public static final class Builder {
+ private int mDisconnectCode;
+ private CharSequence mDisconnectLabel;
+ private CharSequence mDisconnectDescription;
+ private String mDisconnectReason;
+ private int mToneToPlay;
+ private int mTelephonyDisconnectCause;
+ private int mTelephonyPreciseDisconnectCause;
+ private ImsReasonInfo mImsReasonInfo;
+
+ /**
+ * Sets the code for the reason for this disconnect.
+ * @param code The code denoting the type of disconnect.
+ */
+ public @NonNull DisconnectCause.Builder setCode(int code) {
+ mDisconnectCode = code;
+ return this;
+ }
+
+ /**
+ * Sets a label which explains the reason for the disconnect cause, used for display in the
+ * user interface.
+ * @param label The label to associate with the disconnect cause.
+ */
+ public @NonNull DisconnectCause.Builder setLabel(@Nullable CharSequence label) {
+ mDisconnectLabel = label;
+ return this;
+ }
+
+ /**
+ * Sets a description which provides the reason for the disconnect cause, used for display
+ * in the user interface.
+ * @param description The description to associate with the disconnect cause.
+ */
+ public @NonNull DisconnectCause.Builder setDescription(
+ @Nullable CharSequence description) {
+ mDisconnectDescription = description;
+ return this;
+ }
+
+ /**
+ * Sets a reason providing explanation for the disconnect (intended for logging and not for
+ * displaying in the user interface).
+ * @param reason The reason for the disconnect.
+ */
+ public @NonNull DisconnectCause.Builder setReason(@NonNull String reason) {
+ mDisconnectReason = reason;
+ return this;
+ }
+
+ /**
+ * Sets the tone to play when disconnected.
+ * @param toneToPlay The tone as defined in {@link ToneGenerator} to play when disconnected.
+ */
+ public @NonNull DisconnectCause.Builder setTone(int toneToPlay) {
+ mToneToPlay = toneToPlay;
+ return this;
+ }
+
+ /**
+ * Sets the telephony {@link android.telephony.DisconnectCause} for the call (used
+ * internally by Telecom for providing extra debug information from Telephony).
+ * @param telephonyDisconnectCause The disconnect cause as provided by Telephony.
+ */
+ public @NonNull DisconnectCause.Builder setTelephonyDisconnectCause(
+ @Annotation.DisconnectCauses int telephonyDisconnectCause) {
+ mTelephonyDisconnectCause = telephonyDisconnectCause;
+ return this;
+ }
+
+ /**
+ * Sets the telephony {@link android.telephony.PreciseDisconnectCause} for the call (used
+ * internally by Telecom for providing extra debug information from Telephony).
+ * @param telephonyPreciseDisconnectCause The precise disconnect cause as provided by
+ * Telephony.
+ */
+
+ public @NonNull DisconnectCause.Builder setTelephonyPreciseDisconnectCause(
+ @Annotation.PreciseDisconnectCauses int telephonyPreciseDisconnectCause) {
+ mTelephonyPreciseDisconnectCause = telephonyPreciseDisconnectCause;
+ return this;
+ }
+
+ /**
+ * Returns the telephony {@link ImsReasonInfo} associated with the call disconnection. This
+ * is only used internally by Telecom for providing extra debug information from Telephony.
+ *
+ * @param imsReasonInfo The {@link ImsReasonInfo} or {@code null} if not known.
+ */
+ public @NonNull DisconnectCause.Builder setImsReasonInfo(
+ @Nullable ImsReasonInfo imsReasonInfo) {
+ mImsReasonInfo = imsReasonInfo;
+ return this;
+ }
+
+ /**
+ * Build the {@link DisconnectCause} from the provided Builder config.
+ * @return The {@link DisconnectCause} instance from provided builder.
+ */
+ public @NonNull DisconnectCause build() {
+ return new DisconnectCause(mDisconnectCode, mDisconnectLabel, mDisconnectDescription,
+ mDisconnectReason, mToneToPlay, mTelephonyDisconnectCause,
+ mTelephonyPreciseDisconnectCause, mImsReasonInfo);
+ }
+ }
+
public static final @android.annotation.NonNull Creator<DisconnectCause> CREATOR
= new Creator<DisconnectCause>() {
@Override
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 15a978d167e1..9792cdd80a00 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1434,6 +1434,31 @@ public class TelecomManager {
}
/**
+ * This API will return all {@link PhoneAccount}s registered via
+ * {@link TelecomManager#registerPhoneAccount(PhoneAccount)}. If a {@link PhoneAccount} appears
+ * to be missing from the list, Telecom has either unregistered the {@link PhoneAccount}
+ * or the caller registered the {@link PhoneAccount} under a different user and does not
+ * have the {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission.
+ *
+ * @return all the {@link PhoneAccount}s registered by the caller.
+ */
+ @SuppressLint("RequiresPermission")
+ @FlaggedApi(Flags.FLAG_GET_REGISTERED_PHONE_ACCOUNTS)
+ public @NonNull List<PhoneAccount> getRegisteredPhoneAccounts() {
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.getRegisteredPhoneAccounts(
+ mContext.getOpPackageName(),
+ mContext.getAttributionTag()).getList();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ throw new IllegalStateException("Telecom is not available");
+ }
+
+ /**
* Returns a list of {@link PhoneAccountHandle}s including those which have not been enabled
* by the user.
*
@@ -2772,13 +2797,10 @@ public class TelecomManager {
* calls for a given {@code packageName} and {@code userHandle}.
*
* @param packageName the package name of the app to check calls for.
- * @param userHandle the user handle on which to check for calls.
- * @param detectForAllUsers indicates if calls should be detected across all users. If it is
- * set to {@code true}, and the caller has the ability to interact
- * across users, the userHandle parameter is disregarded.
+ * @param userHandle the user handle to check calls for.
* @return {@code true} if there are ongoing calls, {@code false} otherwise.
- * @throws SecurityException if detectForAllUsers is true or userHandle is not the calling user
- * and the caller does not grant the ability to interact across users.
+ * @throws SecurityException if the userHandle is not the calling user and the caller does not
+ * grant the ability to interact across users.
* @hide
*/
@SystemApi
@@ -2786,11 +2808,45 @@ public class TelecomManager {
@RequiresPermission(allOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
public boolean isInSelfManagedCall(@NonNull String packageName,
- @NonNull UserHandle userHandle, boolean detectForAllUsers) {
+ @NonNull UserHandle userHandle) {
ITelecomService service = getTelecomService();
if (service != null) {
try {
return service.isInSelfManagedCall(packageName, userHandle,
+ mContext.getOpPackageName(), false);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException isInSelfManagedCall: " + e);
+ e.rethrowFromSystemServer();
+ return false;
+ }
+ } else {
+ throw new IllegalStateException("Telecom service is not present");
+ }
+ }
+
+ /**
+ * Determines whether there are any ongoing {@link PhoneAccount#CAPABILITY_SELF_MANAGED}
+ * calls for a given {@code packageName} amongst all users, given that detectForAllUsers is true
+ * and the caller has the ability to interact across users. If detectForAllUsers isn't enabled,
+ * the calls will be checked against the caller.
+ *
+ * @param packageName the package name of the app to check calls for.
+ * @param detectForAllUsers indicates if calls should be detected across all users.
+ * @return {@code true} if there are ongoing calls, {@code false} otherwise.
+ * @throws SecurityException if detectForAllUsers is true and the caller does not grant the
+ * ability to interact across users.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
+ @RequiresPermission(allOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
+ public boolean isInSelfManagedCall(@NonNull String packageName,
+ boolean detectForAllUsers) {
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.isInSelfManagedCall(packageName, null,
mContext.getOpPackageName(), detectForAllUsers);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException isInSelfManagedCall: " + e);
diff --git a/telecomm/java/com/android/internal/telecom/ClientTransactionalServiceWrapper.java b/telecomm/java/com/android/internal/telecom/ClientTransactionalServiceWrapper.java
index 467e89c78810..a2c60862f559 100644
--- a/telecomm/java/com/android/internal/telecom/ClientTransactionalServiceWrapper.java
+++ b/telecomm/java/com/android/internal/telecom/ClientTransactionalServiceWrapper.java
@@ -33,6 +33,8 @@ import android.telecom.PhoneAccountHandle;
import android.text.TextUtils;
import android.util.Log;
+import com.android.server.telecom.flags.Flags;
+
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
@@ -148,6 +150,7 @@ public class ClientTransactionalServiceWrapper {
private static final String ON_REQ_ENDPOINT_CHANGE = "onRequestEndpointChange";
private static final String ON_AVAILABLE_CALL_ENDPOINTS = "onAvailableCallEndpointsChanged";
private static final String ON_MUTE_STATE_CHANGED = "onMuteStateChanged";
+ private static final String ON_VIDEO_STATE_CHANGED = "onVideoStateChanged";
private static final String ON_CALL_STREAMING_FAILED = "onCallStreamingFailed";
private static final String ON_EVENT = "onEvent";
@@ -261,6 +264,11 @@ public class ClientTransactionalServiceWrapper {
handleEventCallback(callId, ON_MUTE_STATE_CHANGED, isMuted);
}
+ @Override
+ public void onVideoStateChanged(String callId, int videoState) {
+ handleEventCallback(callId, ON_VIDEO_STATE_CHANGED, videoState);
+ }
+
public void handleEventCallback(String callId, String action, Object arg) {
Log.d(TAG, TextUtils.formatSimple("hEC: [%s], callId=[%s]", action, callId));
// lookup the callEventCallback associated with the particular call
@@ -281,6 +289,11 @@ public class ClientTransactionalServiceWrapper {
case ON_MUTE_STATE_CHANGED:
callback.onMuteStateChanged((boolean) arg);
break;
+ case ON_VIDEO_STATE_CHANGED:
+ if (Flags.transactionalVideoState()) {
+ callback.onVideoStateChanged((int) arg);
+ }
+ break;
case ON_CALL_STREAMING_FAILED:
callback.onCallStreamingFailed((int) arg /* reason */);
break;
diff --git a/telecomm/java/com/android/internal/telecom/ICallControl.aidl b/telecomm/java/com/android/internal/telecom/ICallControl.aidl
index 372e4a12ff70..ac496607abe6 100644
--- a/telecomm/java/com/android/internal/telecom/ICallControl.aidl
+++ b/telecomm/java/com/android/internal/telecom/ICallControl.aidl
@@ -34,4 +34,5 @@ oneway interface ICallControl {
void requestCallEndpointChange(in CallEndpoint callEndpoint, in ResultReceiver callback);
void setMuteState(boolean isMuted, in ResultReceiver callback);
void sendEvent(String callId, String event, in Bundle extras);
+ void requestVideoState(int videoState, String callId, in ResultReceiver callback);
} \ No newline at end of file
diff --git a/telecomm/java/com/android/internal/telecom/ICallEventCallback.aidl b/telecomm/java/com/android/internal/telecom/ICallEventCallback.aidl
index 213cafbbf188..e4d6b0c79c49 100644
--- a/telecomm/java/com/android/internal/telecom/ICallEventCallback.aidl
+++ b/telecomm/java/com/android/internal/telecom/ICallEventCallback.aidl
@@ -45,6 +45,8 @@ oneway interface ICallEventCallback {
void onCallEndpointChanged(String callId, in CallEndpoint endpoint);
void onAvailableCallEndpointsChanged(String callId, in List<CallEndpoint> endpoint);
void onMuteStateChanged(String callId, boolean isMuted);
+ // -- Video Related
+ void onVideoStateChanged(String callId, int videoState);
// -- Events
void onEvent(String callId, String event, in Bundle extras);
// hidden methods that help with cleanup
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 7dba799e1057..302a472b77e4 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -93,6 +93,12 @@ interface ITelecomService {
PhoneAccount getPhoneAccount(in PhoneAccountHandle account, String callingPackage);
/**
+ * @see TelecomManager#getPhoneAccount
+ */
+ ParceledListSlice<PhoneAccount> getRegisteredPhoneAccounts(String callingPackage,
+ String callingFeatureId);
+
+ /**
* @see TelecomManager#getAllPhoneAccountsCount
*/
int getAllPhoneAccountsCount();
diff --git a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
index aed8fb8c4503..a63db88cb614 100644
--- a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
+++ b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
@@ -32,6 +32,8 @@ import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.Telephony;
+import android.provider.Telephony.Carriers.EditStatus;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyFrameworkInitializer;
import android.telephony.TelephonyManager;
@@ -226,6 +228,23 @@ public final class TelephonyUtils {
}
/**
+ * Convert APN edited status to string.
+ *
+ * @param apnEditStatus APN edited status.
+ * @return APN edited status in string format.
+ */
+ public static @NonNull String apnEditedStatusToString(@EditStatus int apnEditStatus) {
+ return switch (apnEditStatus) {
+ case Telephony.Carriers.UNEDITED -> "UNEDITED";
+ case Telephony.Carriers.USER_EDITED -> "USER_EDITED";
+ case Telephony.Carriers.USER_DELETED -> "USER_DELETED";
+ case Telephony.Carriers.CARRIER_EDITED -> "CARRIER_EDITED";
+ case Telephony.Carriers.CARRIER_DELETED -> "CARRIER_DELETED";
+ default -> "UNKNOWN(" + apnEditStatus + ")";
+ };
+ }
+
+ /**
* Utility method to get user handle associated with this subscription.
*
* This method should be used internally as it returns null instead of throwing
diff --git a/telephony/java/android/service/euicc/EuiccService.java b/telephony/java/android/service/euicc/EuiccService.java
index 5af2c3458368..55245419c570 100644
--- a/telephony/java/android/service/euicc/EuiccService.java
+++ b/telephony/java/android/service/euicc/EuiccService.java
@@ -856,10 +856,22 @@ public abstract class EuiccService extends Service {
int slotId, IGetAvailableMemoryInBytesCallback callback) {
mExecutor.execute(
() -> {
- long availableMemoryInBytes =
- EuiccService.this.onGetAvailableMemoryInBytes(slotId);
+ long availableMemoryInBytes = EuiccManager.EUICC_MEMORY_FIELD_UNAVAILABLE;
+ String unsupportedOperationMessage = "";
try {
- callback.onSuccess(availableMemoryInBytes);
+ availableMemoryInBytes =
+ EuiccService.this.onGetAvailableMemoryInBytes(slotId);
+ } catch (UnsupportedOperationException e) {
+ unsupportedOperationMessage = e.getMessage();
+ }
+
+ try {
+ if (!unsupportedOperationMessage.isEmpty()) {
+ callback.onUnsupportedOperationException(
+ unsupportedOperationMessage);
+ } else {
+ callback.onSuccess(availableMemoryInBytes);
+ }
} catch (RemoteException e) {
// Can't communicate with the phone process; ignore.
}
diff --git a/telephony/java/android/service/euicc/IGetAvailableMemoryInBytesCallback.aidl b/telephony/java/android/service/euicc/IGetAvailableMemoryInBytesCallback.aidl
index bd6d19b81d47..e550e77a3605 100644
--- a/telephony/java/android/service/euicc/IGetAvailableMemoryInBytesCallback.aidl
+++ b/telephony/java/android/service/euicc/IGetAvailableMemoryInBytesCallback.aidl
@@ -19,4 +19,5 @@ package android.service.euicc;
/** @hide */
oneway interface IGetAvailableMemoryInBytesCallback {
void onSuccess(long availableMemoryInBytes);
+ void onUnsupportedOperationException(String message);
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 697c8ecd96e7..5d99acd87dd3 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3844,13 +3844,27 @@ public class CarrierConfigManager {
* and if the 5G state changes to neither 'connected' not 'not_restricted_rrc_idle', the icon
* will change to reflect the true state.
*
+ * The value can be overridden by {@link #KEY_NR_ADVANCED_BANDS_SECONDARY_TIMER_SECONDS_INT}
* @hide
*/
public static final String KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING =
"5g_icon_display_secondary_grace_period_string";
/**
- * Whether device reset all of NR timers when device camped on a network that haven't 5G
+ * The secondary grace periods in seconds to use if NR advanced icon was shown due to connecting
+ * to bands specified in {@link #KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY}.
+ *
+ * The default value is 0, meaning the original value in
+ * {@link #KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING} is used. Otherwise, it overrides
+ * the value in {@link #KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING}.
+ *
+ * @hide
+ */
+ public static final String KEY_NR_ADVANCED_BANDS_SECONDARY_TIMER_SECONDS_INT =
+ "nr_advanced_bands_secondary_timer_seconds_int";
+
+ /**
+ * Whether device resets all of NR timers when device camped on a network that haven't 5G
* capability and RRC currently in IDLE state.
*
* The default value is false;
@@ -3861,6 +3875,30 @@ public class CarrierConfigManager {
"nr_timers_reset_if_non_endc_and_rrc_idle_bool";
/**
+ * Whether device resets all of NR timers when device is in a voice call and QOS is established.
+ * The default value is false;
+ *
+ * @see #KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING
+ * @see #KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING
+ *
+ * @hide
+ */
+ public static final String KEY_NR_TIMERS_RESET_ON_VOICE_QOS_BOOL =
+ "nr_timers_reset_on_voice_qos_bool";
+
+ /**
+ * Whether device resets all of NR timers when the PLMN changes.
+ * The default value is false;
+ *
+ * @see #KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING
+ * @see #KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING
+ *
+ * @hide
+ */
+ public static final String KEY_NR_TIMERS_RESET_ON_PLMN_CHANGE_BOOL =
+ "nr_timers_reset_on_plmn_change_bool";
+
+ /**
* A list of additional NR advanced band would map to
* {@link TelephonyDisplayInfo#OVERRIDE_NETWORK_TYPE_NR_ADVANCED} when the device is on that
* band.
@@ -4765,12 +4803,51 @@ public class CarrierConfigManager {
*/
public static final String KEY_FCM_SENDER_ID_STRING = KEY_PREFIX + "fcm_sender_id_string";
+ /**
+ * Indicates the supported protocol version in the parameter entitlement_version.
+ * The default value is 2. The possible value is 2 and 8.
+ *
+ * Reference: GSMA TS.43-v8 section 2.5 Protocol version control and
+ * Table 3. GET Parameters for Entitlement Configuration in section 2.3
+ * HTTP GET method Parameters.
+ * @hide
+ */
+ public static final String KEY_ENTITLEMENT_VERSION_INT =
+ KEY_PREFIX + "entitlement_version_int";
+
+ /**
+ * Controls the service entitlement status when receiving the VERS characteristic
+ * with both version and validity set to -1 or -2.
+ * If {@code true}, default service entitlement status is enabled.
+ * If {@code false}, default service entitlement status is disabled.
+ *
+ * Reference: GSMA TS.14-v8 section 2.1, overview
+ * @hide
+ */
+ public static final String KEY_DEFAULT_SERVICE_ENTITLEMENT_STATUS_BOOL =
+ KEY_PREFIX + "default_service_entitlement_status_bool";
+
+ /**
+ * Indicates if UE can skip service entitlement check when the user turns on Wi-Fi Calling.
+ * UE still shows Wi-Fi Calling emergency address update web view when the user clicks
+ * "Update Emergency Address" on the WiFi calling setting.
+ *
+ * Note: this is effective only if the {@link #KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING}
+ * is set to this app.
+ * @hide
+ */
+ public static final String KEY_SKIP_WFC_ACTIVATION_BOOL =
+ KEY_PREFIX + "skip_wfc_activation_bool";
+
private static PersistableBundle getDefaults() {
PersistableBundle defaults = new PersistableBundle();
defaults.putString(KEY_ENTITLEMENT_SERVER_URL_STRING, "");
defaults.putString(KEY_FCM_SENDER_ID_STRING, "");
defaults.putBoolean(KEY_SHOW_VOWIFI_WEBVIEW_BOOL, false);
defaults.putBoolean(KEY_IMS_PROVISIONING_BOOL, false);
+ defaults.putBoolean(KEY_DEFAULT_SERVICE_ENTITLEMENT_STATUS_BOOL, false);
+ defaults.putBoolean(KEY_SKIP_WFC_ACTIVATION_BOOL, false);
+ defaults.putInt(KEY_ENTITLEMENT_VERSION_INT, 2);
return defaults;
}
}
@@ -10688,7 +10765,10 @@ public class CarrierConfigManager {
+ "not_restricted_rrc_con:5G");
sDefaults.putString(KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING, "");
sDefaults.putString(KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING, "");
+ sDefaults.putInt(KEY_NR_ADVANCED_BANDS_SECONDARY_TIMER_SECONDS_INT, 0);
sDefaults.putBoolean(KEY_NR_TIMERS_RESET_IF_NON_ENDC_AND_RRC_IDLE_BOOL, false);
+ sDefaults.putBoolean(KEY_NR_TIMERS_RESET_ON_VOICE_QOS_BOOL, false);
+ sDefaults.putBoolean(KEY_NR_TIMERS_RESET_ON_PLMN_CHANGE_BOOL, false);
/* Default value is 1 hour. */
sDefaults.putLong(KEY_5G_WATCHDOG_TIME_MS_LONG, 3600000);
sDefaults.putIntArray(KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY, new int[0]);
diff --git a/telephony/java/android/telephony/DomainSelectionService.java b/telephony/java/android/telephony/DomainSelectionService.java
index 4ff9712f0907..633694ac97da 100644
--- a/telephony/java/android/telephony/DomainSelectionService.java
+++ b/telephony/java/android/telephony/DomainSelectionService.java
@@ -20,6 +20,7 @@ import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
@@ -855,7 +856,8 @@ public abstract class DomainSelectionService extends Service {
*
* @return an {@link Executor} used to execute methods called remotely by the framework.
*/
- public @NonNull Executor onCreateExecutor() {
+ @SuppressLint("OnNameExpected")
+ public @NonNull Executor getCreateExecutor() {
return Runnable::run;
}
@@ -869,7 +871,7 @@ public abstract class DomainSelectionService extends Service {
public final @NonNull Executor getCachedExecutor() {
synchronized (mExecutorLock) {
if (mExecutor == null) {
- Executor e = onCreateExecutor();
+ Executor e = getCreateExecutor();
mExecutor = (e != null) ? e : Runnable::run;
}
return mExecutor;
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index cd641b8be0b6..c5f2d42389e5 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -4690,7 +4690,6 @@ public class SubscriptionManager {
* @param subscriptionId the subId of the subscription
* @param userHandle user handle of the user
* @return {@code true} if subscription is associated with user
- * {code true} if there are no subscriptions on device
* else {@code false} if subscription is not associated with user.
*
* @throws IllegalArgumentException if subscription doesn't exist.
@@ -4721,6 +4720,37 @@ public class SubscriptionManager {
}
/**
+ * Returns whether the given subscription is associated with the calling user.
+ *
+ * @param subscriptionId the subscription ID of the subscription
+ * @return {@code true} if the subscription is associated with the user that the current process
+ * is running in; {@code false} otherwise.
+ *
+ * @throws IllegalArgumentException if subscription doesn't exist.
+ * @throws SecurityException if the caller doesn't have permissions required.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ @FlaggedApi(Flags.FLAG_SUBSCRIPTION_USER_ASSOCIATION_QUERY)
+ public boolean isSubscriptionAssociatedWithUser(int subscriptionId) {
+ if (!isValidSubscriptionId(subscriptionId)) {
+ throw new IllegalArgumentException("[isSubscriptionAssociatedWithCallingUser]: "
+ + "Invalid subscriptionId: " + subscriptionId);
+ }
+
+ try {
+ ISub iSub = TelephonyManager.getSubscriptionService();
+ if (iSub != null) {
+ return iSub.isSubscriptionAssociatedWithCallingUser(subscriptionId);
+ } else {
+ throw new IllegalStateException("subscription service unavailable.");
+ }
+ } catch (RemoteException ex) {
+ ex.rethrowAsRuntimeException();
+ }
+ return false;
+ }
+
+ /**
* Get list of subscriptions associated with user.
*
* @param userHandle user handle of the user
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 041822bf4ee9..626a2e574881 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -15011,6 +15011,29 @@ public class TelephonyManager {
}
/**
+ * Get the emergency assistance package name.
+ *
+ * @return the package name of the emergency assistance app.
+ * @throws IllegalStateException if emergency assistance is not enabled or the device is
+ * not voice capable.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @FlaggedApi(android.permission.flags.Flags.FLAG_GET_EMERGENCY_ROLE_HOLDER_API_ENABLED)
+ @NonNull
+ @SystemApi
+ public String getEmergencyAssistancePackage() {
+ if (!isEmergencyAssistanceEnabled() || !isVoiceCapable()) {
+ throw new IllegalStateException("isEmergencyAssistanceEnabled() is false or device"
+ + " not voice capable.");
+ }
+ String emergencyRole = mContext.getSystemService(RoleManager.class)
+ .getEmergencyRoleHolder(mContext.getUserId());
+ return Objects.requireNonNull(emergencyRole, "Emergency role holder must not be null");
+ }
+
+ /**
* Get the emergency number list based on current locale, sim, default, modem and network.
*
* <p>In each returned list, the emergency number {@link EmergencyNumber} coming from higher
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 8679bd4baf2e..44d3fca6aec6 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -29,6 +29,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.provider.Telephony;
import android.provider.Telephony.Carriers;
+import android.provider.Telephony.Carriers.EditStatus;
import android.telephony.Annotation.NetworkType;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
@@ -37,6 +38,7 @@ import android.util.ArrayMap;
import android.util.Log;
import com.android.internal.telephony.flags.Flags;
+import com.android.internal.telephony.util.TelephonyUtils;
import com.android.telephony.Rlog;
import java.lang.annotation.Retention;
@@ -571,6 +573,13 @@ public class ApnSetting implements Parcelable {
private final boolean mEsimBootstrapProvisioning;
/**
+ * The APN edited status.
+ *
+ * Note it is intended not using this field for {@link #equals(Object)} or {@link #hashCode()}.
+ */
+ private final @EditStatus int mEditedStatus;
+
+ /**
* Returns the default MTU (Maximum Transmission Unit) size in bytes of the IPv4 routes brought
* up by this APN setting. Note this value will only be used when MTU size is not provided
* in {@code DataCallResponse#getMtuV4()} during network bring up.
@@ -992,6 +1001,22 @@ public class ApnSetting implements Parcelable {
return mEsimBootstrapProvisioning;
}
+ /**
+ * @return APN edited status. APN could be added/edited/deleted by a user or carrier.
+ *
+ * @see Carriers#UNEDITED
+ * @see Carriers#USER_EDITED
+ * @see Carriers#USER_DELETED
+ * @see Carriers#CARRIER_EDITED
+ * @see Carriers#CARRIER_DELETED
+ *
+ * @hide
+ */
+ @EditStatus
+ public int getEditedStatus() {
+ return mEditedStatus;
+ }
+
private ApnSetting(Builder builder) {
this.mEntryName = builder.mEntryName;
this.mApnName = builder.mApnName;
@@ -1030,6 +1055,7 @@ public class ApnSetting implements Parcelable {
this.mAlwaysOn = builder.mAlwaysOn;
this.mInfrastructureBitmask = builder.mInfrastructureBitmask;
this.mEsimBootstrapProvisioning = builder.mEsimBootstrapProvisioning;
+ this.mEditedStatus = builder.mEditedStatus;
}
/**
@@ -1113,6 +1139,8 @@ public class ApnSetting implements Parcelable {
Telephony.Carriers.INFRASTRUCTURE_BITMASK)))
.setEsimBootstrapProvisioning(cursor.getInt(
cursor.getColumnIndexOrThrow(Carriers.ESIM_BOOTSTRAP_PROVISIONING)) == 1)
+ .setEditedStatus(cursor.getInt(
+ cursor.getColumnIndexOrThrow(Carriers.EDITED_STATUS)))
.buildWithoutCheck();
}
@@ -1154,6 +1182,7 @@ public class ApnSetting implements Parcelable {
.setAlwaysOn(apn.mAlwaysOn)
.setInfrastructureBitmask(apn.mInfrastructureBitmask)
.setEsimBootstrapProvisioning(apn.mEsimBootstrapProvisioning)
+ .setEditedStatus(apn.mEditedStatus)
.buildWithoutCheck();
}
@@ -1202,6 +1231,7 @@ public class ApnSetting implements Parcelable {
sb.append(", ").append(mInfrastructureBitmask);
sb.append(", ").append(Objects.hash(mUser, mPassword));
sb.append(", ").append(mEsimBootstrapProvisioning);
+ sb.append(", ").append(TelephonyUtils.apnEditedStatusToString(mEditedStatus));
return sb.toString();
}
@@ -1748,6 +1778,7 @@ public class ApnSetting implements Parcelable {
dest.writeBoolean(mAlwaysOn);
dest.writeInt(mInfrastructureBitmask);
dest.writeBoolean(mEsimBootstrapProvisioning);
+ dest.writeInt(mEditedStatus);
}
private static ApnSetting readFromParcel(Parcel in) {
@@ -1785,6 +1816,7 @@ public class ApnSetting implements Parcelable {
.setAlwaysOn(in.readBoolean())
.setInfrastructureBitmask(in.readInt())
.setEsimBootstrapProvisioning(in.readBoolean())
+ .setEditedStatus(in.readInt())
.buildWithoutCheck();
}
@@ -1868,6 +1900,7 @@ public class ApnSetting implements Parcelable {
private boolean mAlwaysOn;
private int mInfrastructureBitmask = INFRASTRUCTURE_CELLULAR | INFRASTRUCTURE_SATELLITE;
private boolean mEsimBootstrapProvisioning;
+ private @EditStatus int mEditedStatus = Carriers.UNEDITED;
/**
* Default constructor for Builder.
@@ -2310,6 +2343,8 @@ public class ApnSetting implements Parcelable {
*
* @param esimBootstrapProvisioning {@code true} if the APN is used for eSIM bootstrap
* provisioning, {@code false} otherwise.
+ *
+ * @return The builder.
* @hide
*/
@NonNull
@@ -2319,6 +2354,26 @@ public class ApnSetting implements Parcelable {
}
/**
+ * Set the edited status. APN could be added/edited/deleted by a user or carrier.
+ *
+ * @param editedStatus The APN edited status
+ * @return The builder.
+ *
+ * @see Carriers#UNEDITED
+ * @see Carriers#USER_EDITED
+ * @see Carriers#USER_DELETED
+ * @see Carriers#CARRIER_EDITED
+ * @see Carriers#CARRIER_DELETED
+ *
+ * @hide
+ */
+ @NonNull
+ public Builder setEditedStatus(@EditStatus int editedStatus) {
+ this.mEditedStatus = editedStatus;
+ return this;
+ }
+
+ /**
* Builds {@link ApnSetting} from this builder.
*
* @return {@code null} if {@link #setApnName(String)} or {@link #setEntryName(String)}
diff --git a/telephony/java/android/telephony/data/IQualifiedNetworksServiceCallback.aidl b/telephony/java/android/telephony/data/IQualifiedNetworksServiceCallback.aidl
index bdd212afd4b0..e69b60b3a37c 100644
--- a/telephony/java/android/telephony/data/IQualifiedNetworksServiceCallback.aidl
+++ b/telephony/java/android/telephony/data/IQualifiedNetworksServiceCallback.aidl
@@ -26,4 +26,5 @@ oneway interface IQualifiedNetworksServiceCallback
{
void onQualifiedNetworkTypesChanged(int apnTypes, in int[] qualifiedNetworkTypes);
void onNetworkValidationRequested(int networkCapability, IIntegerConsumer callback);
+ void onReconnectQualifedNetworkType(int apnTypes, int qualifiedNetworkType);
}
diff --git a/telephony/java/android/telephony/data/QualifiedNetworksService.java b/telephony/java/android/telephony/data/QualifiedNetworksService.java
index c3ba09248298..7bfe04d025c8 100644
--- a/telephony/java/android/telephony/data/QualifiedNetworksService.java
+++ b/telephony/java/android/telephony/data/QualifiedNetworksService.java
@@ -33,6 +33,7 @@ import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.telephony.Annotation.ApnType;
import android.telephony.Annotation.NetCapability;
import android.telephony.PreciseDataConnectionState;
+import android.telephony.TelephonyManager;
import android.util.Log;
import android.util.SparseArray;
@@ -82,6 +83,7 @@ public abstract class QualifiedNetworksService extends Service {
private static final int QNS_APN_THROTTLE_STATUS_CHANGED = 5;
private static final int QNS_EMERGENCY_DATA_NETWORK_PREFERRED_TRANSPORT_CHANGED = 6;
private static final int QNS_REQUEST_NETWORK_VALIDATION = 7;
+ private static final int QNS_RECONNECT_QUALIFIED_NETWORK = 8;
/** Feature flags */
private static final FeatureFlags sFeatureFlag = new FeatureFlagsImpl();
@@ -186,8 +188,42 @@ public abstract class QualifiedNetworksService extends Service {
qualifiedNetworkTypesArray).sendToTarget();
}
- private void onUpdateQualifiedNetworkTypes(@ApnType int apnTypes,
- int[] qualifiedNetworkTypes) {
+ /**
+ * Request to make a clean initial connection instead of handover to a transport type mapped
+ * to the {@code qualifiedNetworkType} for the {@code apnTypes}. This will update the
+ * preferred network type like {@link #updateQualifiedNetworkTypes(int, List)}, however if
+ * the data network for the {@code apnTypes} is not in the state {@link TelephonyManager
+ * #DATA_CONNECTED} or it's already connected on the transport type mapped to the
+ * qualified network type, forced reconnection will be ignored.
+ *
+ * <p>This will tear down current data network even though target transport type mapped to
+ * the {@code qualifiedNetworkType} is not available, and the data network will be connected
+ * to the transport type when it becomes available.
+ *
+ * <p>This is one shot request and does not mean further handover is not allowed to the
+ * qualified network type for this APN type.
+ *
+ * @param apnTypes APN type(s) of the qualified networks. This must be a bitmask combination
+ * of {@link ApnType}. The same qualified networks will be applicable to all APN types
+ * specified here.
+ * @param qualifiedNetworkType Access network types which are qualified for data connection
+ * setup for {@link ApnType}. Empty list means QNS has no suggestion to the frameworks, and
+ * for that APN type frameworks will route the corresponding network requests to
+ * {@link AccessNetworkConstants#TRANSPORT_TYPE_WWAN}.
+ *
+ * <p> If one of the element is invalid, for example, {@link AccessNetworkType#UNKNOWN},
+ * then this operation becomes a no-op.
+ *
+ * @hide
+ */
+ public final void reconnectQualifiedNetworkType(@ApnType int apnTypes,
+ @AccessNetworkConstants.RadioAccessNetworkType int qualifiedNetworkType) {
+ mHandler.obtainMessage(QNS_RECONNECT_QUALIFIED_NETWORK, mSlotIndex, apnTypes,
+ new Integer(qualifiedNetworkType)).sendToTarget();
+ }
+
+ private void onUpdateQualifiedNetworkTypes(
+ @ApnType int apnTypes, int[] qualifiedNetworkTypes) {
mQualifiedNetworkTypesList.put(apnTypes, qualifiedNetworkTypes);
if (mCallback != null) {
try {
@@ -198,6 +234,17 @@ public abstract class QualifiedNetworksService extends Service {
}
}
+ private void onReconnectQualifiedNetworkType(@ApnType int apnTypes,
+ @AccessNetworkConstants.RadioAccessNetworkType int qualifiedNetworkType) {
+ if (mCallback != null) {
+ try {
+ mCallback.onReconnectQualifedNetworkType(apnTypes, qualifiedNetworkType);
+ } catch (RemoteException e) {
+ loge("Failed to call onReconnectQualifiedNetworkType. " + e);
+ }
+ }
+ }
+
/**
* The framework calls this method when the throttle status of an APN changes.
*
@@ -366,6 +413,12 @@ public abstract class QualifiedNetworksService extends Service {
case QNS_REQUEST_NETWORK_VALIDATION:
if (provider == null) break;
provider.onRequestNetworkValidation((NetworkValidationRequestData) message.obj);
+ break;
+
+ case QNS_RECONNECT_QUALIFIED_NETWORK:
+ if (provider == null) break;
+ provider.onReconnectQualifiedNetworkType(message.arg2, (Integer) message.obj);
+ break;
}
}
}
diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java
index b429407d8f31..d44a43e42a14 100644
--- a/telephony/java/android/telephony/emergency/EmergencyNumber.java
+++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java
@@ -37,6 +37,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.stream.Collectors;
/**
* A parcelable class that wraps and retrieves the information of number, service category(s) and
@@ -300,8 +301,8 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu
dest.writeInt(mEmergencyCallRouting);
}
- public static final @android.annotation.NonNull Parcelable.Creator<EmergencyNumber> CREATOR =
- new Parcelable.Creator<EmergencyNumber>() {
+ public static final @NonNull Creator<EmergencyNumber> CREATOR =
+ new Creator<EmergencyNumber>() {
@Override
public EmergencyNumber createFromParcel(Parcel in) {
return new EmergencyNumber(in);
@@ -500,12 +501,94 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu
@Override
public String toString() {
- return "EmergencyNumber:" + "Number-" + mNumber + "|CountryIso-" + mCountryIso
- + "|Mnc-" + mMnc
- + "|ServiceCategories-" + Integer.toBinaryString(mEmergencyServiceCategoryBitmask)
- + "|Urns-" + mEmergencyUrns
- + "|Sources-" + Integer.toBinaryString(mEmergencyNumberSourceBitmask)
- + "|Routing-" + Integer.toBinaryString(mEmergencyCallRouting);
+ return String.format("[EmergencyNumber: %s, countryIso=%s, mnc=%s, src=%s, routing=%s, "
+ + "categories=%s, urns=%s]",
+ mNumber,
+ mCountryIso,
+ mMnc,
+ sourceBitmaskToString(mEmergencyNumberSourceBitmask),
+ routingToString(mEmergencyCallRouting),
+ categoriesToString(mEmergencyServiceCategoryBitmask),
+ (mEmergencyUrns == null ? "" :
+ mEmergencyUrns.stream().collect(Collectors.joining(","))));
+ }
+
+ /**
+ * @param categories emergency service category bitmask
+ * @return loggable string describing the category bitmask
+ */
+ private String categoriesToString(@EmergencyServiceCategories int categories) {
+ StringBuilder sb = new StringBuilder();
+ if ((categories & EMERGENCY_SERVICE_CATEGORY_AIEC) == EMERGENCY_SERVICE_CATEGORY_AIEC) {
+ sb.append("auto ");
+ }
+ if ((categories & EMERGENCY_SERVICE_CATEGORY_AMBULANCE)
+ == EMERGENCY_SERVICE_CATEGORY_AMBULANCE) {
+ sb.append("ambulance ");
+ }
+ if ((categories & EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE)
+ == EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE) {
+ sb.append("fire ");
+ }
+ if ((categories & EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD)
+ == EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD) {
+ sb.append("marine ");
+ }
+ if ((categories & EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE)
+ == EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE) {
+ sb.append("mountain ");
+ }
+ if ((categories & EMERGENCY_SERVICE_CATEGORY_POLICE) == EMERGENCY_SERVICE_CATEGORY_POLICE) {
+ sb.append("police ");
+ }
+ if ((categories & EMERGENCY_SERVICE_CATEGORY_MIEC) == EMERGENCY_SERVICE_CATEGORY_MIEC) {
+ sb.append("manual ");
+ }
+ return sb.toString();
+ }
+
+ /**
+ * @param routing emergency call routing type
+ * @return loggable string describing the routing type.
+ */
+ private String routingToString(@EmergencyCallRouting int routing) {
+ return switch(routing) {
+ case EMERGENCY_CALL_ROUTING_EMERGENCY -> "emergency";
+ case EMERGENCY_CALL_ROUTING_NORMAL -> "normal";
+ case EMERGENCY_CALL_ROUTING_UNKNOWN -> "unknown";
+ default -> "🤷";
+ };
+ }
+
+ /**
+ * Builds a string describing the sources for an emergency number.
+ * @param sourceBitmask the source bitmask
+ * @return loggable string describing the sources.
+ */
+ private String sourceBitmaskToString(@EmergencyNumberSources int sourceBitmask) {
+ StringBuilder sb = new StringBuilder();
+ if ((sourceBitmask & EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING)
+ == EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING) {
+ sb.append("net ");
+ }
+ if ((sourceBitmask & EMERGENCY_NUMBER_SOURCE_SIM) == EMERGENCY_NUMBER_SOURCE_SIM) {
+ sb.append("sim ");
+ }
+ if ((sourceBitmask & EMERGENCY_NUMBER_SOURCE_DATABASE)
+ == EMERGENCY_NUMBER_SOURCE_DATABASE) {
+ sb.append("db ");
+ }
+ if ((sourceBitmask & EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG)
+ == EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG) {
+ sb.append("mdm ");
+ }
+ if ((sourceBitmask & EMERGENCY_NUMBER_SOURCE_DEFAULT) == EMERGENCY_NUMBER_SOURCE_DEFAULT) {
+ sb.append("def ");
+ }
+ if ((sourceBitmask & EMERGENCY_NUMBER_SOURCE_TEST) == EMERGENCY_NUMBER_SOURCE_TEST) {
+ sb.append("tst ");
+ }
+ return sb.toString();
}
@Override
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index cc770aa65888..6678f408e720 100644
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -332,12 +332,23 @@ interface ISub {
UserHandle getSubscriptionUserHandle(int subId);
/**
+ * Returns whether the given subscription is associated with the calling user.
+ *
+ * @param subscriptionId the subscription ID of the subscription
+ * @return {@code true} if the subscription is associated with the user that the current process
+ * is running in; {@code false} otherwise.
+ *
+ * @throws IllegalArgumentException if subscription doesn't exist.
+ * @throws SecurityException if the caller doesn't have permissions required.
+ */
+ boolean isSubscriptionAssociatedWithCallingUser(int subscriptionId);
+
+ /**
* Check if subscription and user are associated with each other.
*
* @param subscriptionId the subId of the subscription
* @param userHandle user handle of the user
* @return {@code true} if subscription is associated with user
- * {code true} if there are no subscriptions on device
* else {@code false} if subscription is not associated with user.
*
* @throws IllegalArgumentException if subscription is invalid.
diff --git a/tests/BootImageProfileTest/AndroidTest.xml b/tests/BootImageProfileTest/AndroidTest.xml
index 7e97fa3a8ff1..9b527dc159ee 100644
--- a/tests/BootImageProfileTest/AndroidTest.xml
+++ b/tests/BootImageProfileTest/AndroidTest.xml
@@ -14,6 +14,7 @@
limitations under the License.
-->
<configuration description="Config for BootImageProfileTest">
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
<!-- do not use DeviceSetup#set-property because it reboots the device b/136200738.
furthermore the changes in /data/local.prop don't actually seem to get picked up.
-->
diff --git a/tests/EnforcePermission/Android.bp b/tests/EnforcePermission/Android.bp
index 719a89817a9d..6a5add0aa2df 100644
--- a/tests/EnforcePermission/Android.bp
+++ b/tests/EnforcePermission/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_responsible_apis",
default_applicable_licenses: ["frameworks_base_license"],
}
diff --git a/tests/EnforcePermission/perf-app/Android.bp b/tests/EnforcePermission/perf-app/Android.bp
index b494bb754370..6d04fdc821bb 100644
--- a/tests/EnforcePermission/perf-app/Android.bp
+++ b/tests/EnforcePermission/perf-app/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_responsible_apis",
default_applicable_licenses: ["frameworks_base_license"],
}
diff --git a/tests/EnforcePermission/service-app/Android.bp b/tests/EnforcePermission/service-app/Android.bp
index 787821546018..8910f2a4ba53 100644
--- a/tests/EnforcePermission/service-app/Android.bp
+++ b/tests/EnforcePermission/service-app/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_responsible_apis",
// 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"
diff --git a/tests/EnforcePermission/test-app/Android.bp b/tests/EnforcePermission/test-app/Android.bp
index cd53854189b7..065ab33448e3 100644
--- a/tests/EnforcePermission/test-app/Android.bp
+++ b/tests/EnforcePermission/test-app/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_responsible_apis",
default_applicable_licenses: ["frameworks_base_license"],
}
diff --git a/tests/FlickerTests/AppClose/Android.bp b/tests/FlickerTests/AppClose/Android.bp
index 93fdd652510d..d14a178fe316 100644
--- a/tests/FlickerTests/AppClose/Android.bp
+++ b/tests/FlickerTests/AppClose/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_windowing_animations_transitions",
// 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"
diff --git a/tests/FlickerTests/IME/Android.bp b/tests/FlickerTests/IME/Android.bp
index 1141e5f3ae2f..b02447198e14 100644
--- a/tests/FlickerTests/IME/Android.bp
+++ b/tests/FlickerTests/IME/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_input_method_framework",
// 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"
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
index 47a1619e0e9c..99e8ef5e4c3d 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
@@ -85,7 +85,6 @@ class CloseImeOnDismissPopupDialogTest(flicker: LegacyFlickerTest) : BaseTest(fl
val visibleAreas =
imeSnapshotLayers
.mapNotNull { imeSnapshotLayer -> imeSnapshotLayer.layer.visibleRegion }
- .toTypedArray()
val imeVisibleRegion = RegionSubject(visibleAreas, timestamp)
val appVisibleRegion = it.visibleRegion(imeTestApp)
if (imeVisibleRegion.region.isNotEmpty) {
diff --git a/tests/FlickerTests/Rotation/Android.bp b/tests/FlickerTests/Rotation/Android.bp
index 233a27691e21..b3eb934ef46d 100644
--- a/tests/FlickerTests/Rotation/Android.bp
+++ b/tests/FlickerTests/Rotation/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_windowing_animations_transitions",
// 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"
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index 4032121d4211..1abb8c217603 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -281,7 +281,6 @@ fun LegacyFlickerTest.snapshotStartingWindowLayerCoversExactlyOnApp(
val visibleAreas =
snapshotLayers
.mapNotNull { snapshotLayer -> snapshotLayer.layer.visibleRegion }
- .toTypedArray()
val snapshotRegion = RegionSubject(visibleAreas, it.timestamp)
val appVisibleRegion = it.visibleRegion(component)
// Verify the size of snapshotRegion covers appVisibleRegion exactly in animation.
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
index f628af14a0b9..452c98c65a7f 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
@@ -296,6 +296,10 @@ open class PipAppHelper(instrumentation: Instrumentation) :
clickObject(MEDIA_SESSION_START_RADIO_BUTTON_ID)
}
+ fun setSourceRectHint() {
+ clickObject(SOURCE_RECT_HINT)
+ }
+
fun checkWithCustomActionsCheckbox() =
uiDevice
.findObject(By.res(packageName, WITH_CUSTOM_ACTIONS_BUTTON_ID))
@@ -444,6 +448,7 @@ open class PipAppHelper(instrumentation: Instrumentation) :
private const val MEDIA_SESSION_START_RADIO_BUTTON_ID = "media_session_start"
private const val ENTER_PIP_ON_USER_LEAVE_HINT = "enter_pip_on_leave_manual"
private const val ENTER_PIP_AUTOENTER = "enter_pip_on_leave_autoenter"
+ private const val SOURCE_RECT_HINT = "set_source_rect_hint"
// minimum number of steps to take, when animating gestures, needs to be 2
// so that there is at least a single intermediate layer that flicker tests can check
private const val MIN_STEPS_TO_ANIMATE = 2
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml
index f7ba45b25d48..36cbf1a8fe84 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml
@@ -27,6 +27,15 @@
where things are arranged differently and to circle back up to the top once we reach the
bottom. -->
+ <!-- View used for testing sourceRectHint. -->
+ <View
+ android:id="@+id/source_rect"
+ android:layout_width="320dp"
+ android:layout_height="180dp"
+ android:visibility="gone"
+ android:background="@android:color/holo_green_light"
+ />
+
<Button
android:id="@+id/enter_pip"
android:layout_width="wrap_content"
@@ -113,6 +122,13 @@
android:onClick="onRatioSelected"/>
</RadioGroup>
+ <Button
+ android:id="@+id/set_source_rect_hint"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Set SourceRectHint"
+ android:onClick="setSourceRectHint"/>
+
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
index 12eaad108fc6..1ab8ddbe20e2 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
@@ -37,6 +37,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.graphics.Rect;
import android.graphics.drawable.Icon;
import android.media.MediaMetadata;
import android.media.session.MediaSession;
@@ -45,6 +46,7 @@ import android.os.Bundle;
import android.util.Log;
import android.util.Rational;
import android.view.View;
+import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowManager;
import android.widget.CheckBox;
@@ -248,6 +250,29 @@ public class PipActivity extends Activity {
}
}
+ /**
+ * Adds a temporary view used for testing sourceRectHint.
+ *
+ */
+ public void setSourceRectHint(View v) {
+ View rectView = findViewById(R.id.source_rect);
+ if (rectView != null) {
+ rectView.setVisibility(View.VISIBLE);
+ rectView.getViewTreeObserver().addOnGlobalLayoutListener(
+ new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ Rect boundingRect = new Rect();
+ rectView.getGlobalVisibleRect(boundingRect);
+ mPipParamsBuilder.setSourceRectHint(boundingRect);
+ setPictureInPictureParams(mPipParamsBuilder.build());
+ rectView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ }
+ });
+ rectView.invalidate(); // changing the visibility, invalidating to redraw the view
+ }
+ }
+
public void onRatioSelected(View v) {
switch (v.getId()) {
case R.id.ratio_default:
diff --git a/tests/FsVerityTest/Android.bp b/tests/FsVerityTest/Android.bp
index 53606a32b185..02268c37a5a3 100644
--- a/tests/FsVerityTest/Android.bp
+++ b/tests/FsVerityTest/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_platform_security",
// 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"
diff --git a/tests/FsVerityTest/FsVerityTestApp/Android.bp b/tests/FsVerityTest/FsVerityTestApp/Android.bp
index 43da3ff9fec1..71a7e4f57141 100644
--- a/tests/FsVerityTest/FsVerityTestApp/Android.bp
+++ b/tests/FsVerityTest/FsVerityTestApp/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_platform_security",
// 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"
diff --git a/tests/FsVerityTest/block_device_writer/Android.bp b/tests/FsVerityTest/block_device_writer/Android.bp
index 0002447d17f2..2a337deefcf4 100644
--- a/tests/FsVerityTest/block_device_writer/Android.bp
+++ b/tests/FsVerityTest/block_device_writer/Android.bp
@@ -15,6 +15,7 @@
// This is a cc_test just because it supports test_suites. This should be converted to something
// like cc_binary_test_helper once supported, thus auto_gen_config:false below.
package {
+ default_team: "trendy_team_platform_security",
// 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"
diff --git a/tests/FsVerityTest/testdata/Android.bp b/tests/FsVerityTest/testdata/Android.bp
index 2d578d36423d..21b63e755c23 100644
--- a/tests/FsVerityTest/testdata/Android.bp
+++ b/tests/FsVerityTest/testdata/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_platform_security",
// 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"
diff --git a/tests/HandwritingIme/Android.bp b/tests/HandwritingIme/Android.bp
index 1f552bf4dc6d..0d2422eb8674 100644
--- a/tests/HandwritingIme/Android.bp
+++ b/tests/HandwritingIme/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_input_method_framework",
// 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"
diff --git a/tests/Input/AndroidTest.xml b/tests/Input/AndroidTest.xml
index c62db1ea5ca9..f602c5124e77 100644
--- a/tests/Input/AndroidTest.xml
+++ b/tests/Input/AndroidTest.xml
@@ -4,6 +4,7 @@
-->
<configuration description="Runs Input Tests">
<option name="test-tag" value="InputTests" />
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
<target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
<!-- keeps the screen on during tests -->
<option name="screen-always-on" value="on" />
@@ -22,4 +23,9 @@
<option name="test-timeout" value="600s" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
</test>
+ <object class="com.android.tradefed.testtype.suite.module.TestFailureModuleController"
+ type="module_controller">
+ <!-- Take screenshot upon test failure -->
+ <option name="screenshot-on-failure" value="true" />
+ </object>
</configuration>
diff --git a/tests/InputScreenshotTest/Android.bp b/tests/InputScreenshotTest/Android.bp
index 83ced2c2258f..927b101012c5 100644
--- a/tests/InputScreenshotTest/Android.bp
+++ b/tests/InputScreenshotTest/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_input_framework",
// 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"
diff --git a/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png b/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png
index 70e4a7101c7f..443de8edc2d3 100644
--- a/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png
+++ b/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png
Binary files differ
diff --git a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-ansi.png b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-ansi.png
index 502c1b4499d4..cb69c0e44a03 100644
--- a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-ansi.png
+++ b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-ansi.png
Binary files differ
diff --git a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-jis.png b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-jis.png
index 591b2fa9608e..1c6d1b3a097d 100644
--- a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-jis.png
+++ b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-jis.png
Binary files differ
diff --git a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png
index 0137a853e538..c51da052f4bf 100644
--- a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png
+++ b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png
Binary files differ
diff --git a/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png b/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png
index 37a91e1fce53..ab23401bf629 100644
--- a/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png
+++ b/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png
Binary files differ
diff --git a/tests/InputScreenshotTest/robotests/Android.bp b/tests/InputScreenshotTest/robotests/Android.bp
index 912f4b8069b4..384f58aac15f 100644
--- a/tests/InputScreenshotTest/robotests/Android.bp
+++ b/tests/InputScreenshotTest/robotests/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_input_framework",
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/tests/Internal/Android.bp b/tests/Internal/Android.bp
index a4877999ff6f..827ff4fbd989 100644
--- a/tests/Internal/Android.bp
+++ b/tests/Internal/Android.bp
@@ -21,6 +21,9 @@ android_test {
"mockito-target-minus-junit4",
"truth",
"platform-test-annotations",
+ "flickerlib-parsers",
+ "perfetto_trace_java_protos",
+ "flickerlib-trace_processor_shell",
],
java_resource_dirs: ["res"],
certificate: "platform",
diff --git a/tests/Internal/AndroidManifest.xml b/tests/Internal/AndroidManifest.xml
index dbba24531769..9a3fe617e70a 100644
--- a/tests/Internal/AndroidManifest.xml
+++ b/tests/Internal/AndroidManifest.xml
@@ -19,7 +19,11 @@
package="com.android.internal.tests">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.BIND_WALLPAPER"/>
- <application>
+ <!-- Allow the test to connect to perfetto trace processor -->
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <application
+ android:requestLegacyExternalStorage="true"
+ android:networkSecurityConfig="@xml/network_security_config">
<uses-library android:name="android.test.runner"/>
<service android:name="stub.DummyWallpaperService"
diff --git a/tests/Internal/res/xml/network_security_config.xml b/tests/Internal/res/xml/network_security_config.xml
new file mode 100644
index 000000000000..fdf1dbbe7672
--- /dev/null
+++ b/tests/Internal/res/xml/network_security_config.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 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.
+ -->
+<network-security-config>
+ <domain-config cleartextTrafficPermitted="true">
+ <domain includeSubdomains="true">localhost</domain>
+ </domain-config>
+</network-security-config>
diff --git a/tests/Internal/src/com/android/internal/protolog/LegacyProtoLogImplTest.java b/tests/Internal/src/com/android/internal/protolog/LegacyProtoLogImplTest.java
new file mode 100644
index 000000000000..a64996c6838e
--- /dev/null
+++ b/tests/Internal/src/com/android/internal/protolog/LegacyProtoLogImplTest.java
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.protolog;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.internal.protolog.LegacyProtoLogImpl.PROTOLOG_VERSION;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
+import android.util.proto.ProtoInputStream;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.protolog.common.IProtoLogGroup;
+import com.android.internal.protolog.common.LogLevel;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.util.LinkedList;
+
+/**
+ * Test class for {@link ProtoLogImpl}.
+ */
+@SuppressWarnings("ConstantConditions")
+@SmallTest
+@Presubmit
+@RunWith(JUnit4.class)
+public class LegacyProtoLogImplTest {
+
+ private static final byte[] MAGIC_HEADER = new byte[]{
+ 0x9, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x4c, 0x4f, 0x47
+ };
+
+ private LegacyProtoLogImpl mProtoLog;
+ private File mFile;
+
+ @Mock
+ private LegacyProtoLogViewerConfigReader mReader;
+
+ private final String mViewerConfigFilename = "unused/file/path";
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ final Context testContext = getInstrumentation().getContext();
+ mFile = testContext.getFileStreamPath("tracing_test.dat");
+ //noinspection ResultOfMethodCallIgnored
+ mFile.delete();
+ mProtoLog = new LegacyProtoLogImpl(mFile, mViewerConfigFilename,
+ 1024 * 1024, mReader, 1024);
+ }
+
+ @After
+ public void tearDown() {
+ if (mFile != null) {
+ //noinspection ResultOfMethodCallIgnored
+ mFile.delete();
+ }
+ ProtoLogImpl.setSingleInstance(null);
+ }
+
+ @Test
+ public void isEnabled_returnsFalseByDefault() {
+ assertFalse(mProtoLog.isProtoEnabled());
+ }
+
+ @Test
+ public void isEnabled_returnsTrueAfterStart() {
+ mProtoLog.startProtoLog(mock(PrintWriter.class));
+ assertTrue(mProtoLog.isProtoEnabled());
+ }
+
+ @Test
+ public void isEnabled_returnsFalseAfterStop() {
+ mProtoLog.startProtoLog(mock(PrintWriter.class));
+ mProtoLog.stopProtoLog(mock(PrintWriter.class), true);
+ assertFalse(mProtoLog.isProtoEnabled());
+ }
+
+ @Test
+ public void logFile_startsWithMagicHeader() throws Exception {
+ mProtoLog.startProtoLog(mock(PrintWriter.class));
+ mProtoLog.stopProtoLog(mock(PrintWriter.class), true);
+
+ assertTrue("Log file should exist", mFile.exists());
+
+ byte[] header = new byte[MAGIC_HEADER.length];
+ try (InputStream is = new FileInputStream(mFile)) {
+ assertEquals(MAGIC_HEADER.length, is.read(header));
+ assertArrayEquals(MAGIC_HEADER, header);
+ }
+ }
+
+ @Test
+ public void log_logcatEnabledExternalMessage() {
+ when(mReader.getViewerString(anyLong())).thenReturn("test %b %d %% 0x%x %s %f");
+ LegacyProtoLogImpl implSpy = Mockito.spy(mProtoLog);
+ TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
+ TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
+
+ implSpy.log(
+ LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, null,
+ new Object[]{true, 10000, 30000, "test", 0.000003});
+
+ verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq(
+ LogLevel.INFO),
+ eq("test true 10000 % 0x7530 test 3.0E-6"));
+ verify(mReader).getViewerString(eq(1234L));
+ }
+
+ @Test
+ public void log_logcatEnabledInvalidMessage() {
+ when(mReader.getViewerString(anyLong())).thenReturn("test %b %d %% %x %s %f");
+ LegacyProtoLogImpl implSpy = Mockito.spy(mProtoLog);
+ TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
+ TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
+
+ implSpy.log(
+ LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, null,
+ new Object[]{true, 10000, 0.0001, 0.00002, "test"});
+
+ verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq(
+ LogLevel.INFO),
+ eq("UNKNOWN MESSAGE (1234) true 10000 1.0E-4 2.0E-5 test"));
+ verify(mReader).getViewerString(eq(1234L));
+ }
+
+ @Test
+ public void log_logcatEnabledInlineMessage() {
+ when(mReader.getViewerString(anyLong())).thenReturn("test %d");
+ LegacyProtoLogImpl implSpy = Mockito.spy(mProtoLog);
+ TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
+ TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
+
+ implSpy.log(
+ LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, "test %d",
+ new Object[]{5});
+
+ verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq(
+ LogLevel.INFO), eq("test 5"));
+ verify(mReader, never()).getViewerString(anyLong());
+ }
+
+ @Test
+ public void log_logcatEnabledNoMessage() {
+ when(mReader.getViewerString(anyLong())).thenReturn(null);
+ LegacyProtoLogImpl implSpy = Mockito.spy(mProtoLog);
+ TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
+ TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
+
+ implSpy.log(
+ LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, null,
+ new Object[]{5});
+
+ verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq(
+ LogLevel.INFO), eq("UNKNOWN MESSAGE (1234) 5"));
+ verify(mReader).getViewerString(eq(1234L));
+ }
+
+ @Test
+ public void log_logcatDisabled() {
+ when(mReader.getViewerString(anyLong())).thenReturn("test %d");
+ LegacyProtoLogImpl implSpy = Mockito.spy(mProtoLog);
+ TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false);
+ TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
+
+ implSpy.log(
+ LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, "test %d",
+ new Object[]{5});
+
+ verify(implSpy, never()).passToLogcat(any(), any(), any());
+ verify(mReader, never()).getViewerString(anyLong());
+ }
+
+ private static class ProtoLogData {
+ Long mMessageHash = null;
+ Long mElapsedTime = null;
+ LinkedList<String> mStrParams = new LinkedList<>();
+ LinkedList<Long> mSint64Params = new LinkedList<>();
+ LinkedList<Double> mDoubleParams = new LinkedList<>();
+ LinkedList<Boolean> mBooleanParams = new LinkedList<>();
+ }
+
+ private ProtoLogData readProtoLogSingle(ProtoInputStream ip) throws IOException {
+ while (ip.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ if (ip.getFieldNumber() == (int) ProtoLogFileProto.VERSION) {
+ assertEquals(PROTOLOG_VERSION, ip.readString(ProtoLogFileProto.VERSION));
+ continue;
+ }
+ if (ip.getFieldNumber() != (int) ProtoLogFileProto.LOG) {
+ continue;
+ }
+ long token = ip.start(ProtoLogFileProto.LOG);
+ ProtoLogData data = new ProtoLogData();
+ while (ip.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (ip.getFieldNumber()) {
+ case (int) ProtoLogMessage.MESSAGE_HASH: {
+ data.mMessageHash = ip.readLong(ProtoLogMessage.MESSAGE_HASH);
+ break;
+ }
+ case (int) ProtoLogMessage.ELAPSED_REALTIME_NANOS: {
+ data.mElapsedTime = ip.readLong(ProtoLogMessage.ELAPSED_REALTIME_NANOS);
+ break;
+ }
+ case (int) ProtoLogMessage.STR_PARAMS: {
+ data.mStrParams.add(ip.readString(ProtoLogMessage.STR_PARAMS));
+ break;
+ }
+ case (int) ProtoLogMessage.SINT64_PARAMS: {
+ data.mSint64Params.add(ip.readLong(ProtoLogMessage.SINT64_PARAMS));
+ break;
+ }
+ case (int) ProtoLogMessage.DOUBLE_PARAMS: {
+ data.mDoubleParams.add(ip.readDouble(ProtoLogMessage.DOUBLE_PARAMS));
+ break;
+ }
+ case (int) ProtoLogMessage.BOOLEAN_PARAMS: {
+ data.mBooleanParams.add(ip.readBoolean(ProtoLogMessage.BOOLEAN_PARAMS));
+ break;
+ }
+ }
+ }
+ ip.end(token);
+ return data;
+ }
+ return null;
+ }
+
+ @Test
+ public void log_protoEnabled() throws Exception {
+ TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false);
+ TestProtoLogGroup.TEST_GROUP.setLogToProto(true);
+ mProtoLog.startProtoLog(mock(PrintWriter.class));
+ long before = SystemClock.elapsedRealtimeNanos();
+ mProtoLog.log(
+ LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234,
+ 0b1110101001010100, null,
+ new Object[]{"test", 1, 2, 3, 0.4, 0.5, 0.6, true});
+ long after = SystemClock.elapsedRealtimeNanos();
+ mProtoLog.stopProtoLog(mock(PrintWriter.class), true);
+ try (InputStream is = new FileInputStream(mFile)) {
+ ProtoInputStream ip = new ProtoInputStream(is);
+ ProtoLogData data = readProtoLogSingle(ip);
+ assertNotNull(data);
+ assertEquals(1234, data.mMessageHash.longValue());
+ assertTrue(before <= data.mElapsedTime && data.mElapsedTime <= after);
+ assertArrayEquals(new String[]{"test"}, data.mStrParams.toArray());
+ assertArrayEquals(new Long[]{1L, 2L, 3L}, data.mSint64Params.toArray());
+ assertArrayEquals(new Double[]{0.4, 0.5, 0.6}, data.mDoubleParams.toArray());
+ assertArrayEquals(new Boolean[]{true}, data.mBooleanParams.toArray());
+ }
+ }
+
+ @Test
+ public void log_invalidParamsMask() throws Exception {
+ TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false);
+ TestProtoLogGroup.TEST_GROUP.setLogToProto(true);
+ mProtoLog.startProtoLog(mock(PrintWriter.class));
+ long before = SystemClock.elapsedRealtimeNanos();
+ mProtoLog.log(
+ LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234,
+ 0b01100100, null,
+ new Object[]{"test", 1, 0.1, true});
+ long after = SystemClock.elapsedRealtimeNanos();
+ mProtoLog.stopProtoLog(mock(PrintWriter.class), true);
+ try (InputStream is = new FileInputStream(mFile)) {
+ ProtoInputStream ip = new ProtoInputStream(is);
+ ProtoLogData data = readProtoLogSingle(ip);
+ assertNotNull(data);
+ assertEquals(1234, data.mMessageHash.longValue());
+ assertTrue(before <= data.mElapsedTime && data.mElapsedTime <= after);
+ assertArrayEquals(new String[]{"test", "(INVALID PARAMS_MASK) true"},
+ data.mStrParams.toArray());
+ assertArrayEquals(new Long[]{1L}, data.mSint64Params.toArray());
+ assertArrayEquals(new Double[]{0.1}, data.mDoubleParams.toArray());
+ assertArrayEquals(new Boolean[]{}, data.mBooleanParams.toArray());
+ }
+ }
+
+ @Test
+ public void log_protoDisabled() throws Exception {
+ TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false);
+ TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
+ mProtoLog.startProtoLog(mock(PrintWriter.class));
+ mProtoLog.log(LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234,
+ 0b11, null, new Object[]{true});
+ mProtoLog.stopProtoLog(mock(PrintWriter.class), true);
+ try (InputStream is = new FileInputStream(mFile)) {
+ ProtoInputStream ip = new ProtoInputStream(is);
+ ProtoLogData data = readProtoLogSingle(ip);
+ assertNull(data);
+ }
+ }
+
+ private enum TestProtoLogGroup implements IProtoLogGroup {
+ TEST_GROUP(true, true, false, "WindowManagetProtoLogTest");
+
+ private final boolean mEnabled;
+ private volatile boolean mLogToProto;
+ private volatile boolean mLogToLogcat;
+ private final String mTag;
+
+ /**
+ * @param enabled set to false to exclude all log statements for this group from
+ * compilation,
+ * they will not be available in runtime.
+ * @param logToProto enable binary logging for the group
+ * @param logToLogcat enable text logging for the group
+ * @param tag name of the source of the logged message
+ */
+ TestProtoLogGroup(boolean enabled, boolean logToProto, boolean logToLogcat, String tag) {
+ this.mEnabled = enabled;
+ this.mLogToProto = logToProto;
+ this.mLogToLogcat = logToLogcat;
+ this.mTag = tag;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+
+ @Override
+ public boolean isLogToProto() {
+ return mLogToProto;
+ }
+
+ @Override
+ public boolean isLogToLogcat() {
+ return mLogToLogcat;
+ }
+
+ @Override
+ public boolean isLogToAny() {
+ return mLogToLogcat || mLogToProto;
+ }
+
+ @Override
+ public String getTag() {
+ return mTag;
+ }
+
+ @Override
+ public void setLogToProto(boolean logToProto) {
+ this.mLogToProto = logToProto;
+ }
+
+ @Override
+ public void setLogToLogcat(boolean logToLogcat) {
+ this.mLogToLogcat = logToLogcat;
+ }
+
+ }
+}
diff --git a/tests/Internal/src/com/android/internal/protolog/OWNERS b/tests/Internal/src/com/android/internal/protolog/OWNERS
new file mode 100644
index 000000000000..18cf2be9f7df
--- /dev/null
+++ b/tests/Internal/src/com/android/internal/protolog/OWNERS
@@ -0,0 +1,3 @@
+# ProtoLog owners
+# Bug component: 1157642
+include platform/development:/tools/winscope/OWNERS
diff --git a/tests/Internal/src/com/android/internal/protolog/PerfettoDataSourceTest.java b/tests/Internal/src/com/android/internal/protolog/PerfettoDataSourceTest.java
new file mode 100644
index 000000000000..b9f1738f9bb7
--- /dev/null
+++ b/tests/Internal/src/com/android/internal/protolog/PerfettoDataSourceTest.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.protolog;
+
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assume.assumeTrue;
+
+import android.tracing.perfetto.CreateTlsStateArgs;
+import android.util.proto.ProtoInputStream;
+
+import com.android.internal.protolog.common.LogLevel;
+
+import com.google.common.truth.Truth;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import perfetto.protos.DataSourceConfigOuterClass;
+import perfetto.protos.ProtologCommon;
+import perfetto.protos.ProtologConfig;
+
+public class PerfettoDataSourceTest {
+ @Before
+ public void before() {
+ assumeTrue(android.tracing.Flags.perfettoProtolog());
+ }
+
+ @Test
+ public void noConfig() {
+ final ProtoLogDataSource.TlsState tlsState = createTlsState(
+ DataSourceConfigOuterClass.DataSourceConfig.newBuilder().build());
+
+ Truth.assertThat(tlsState.getLogFromLevel("SOME_TAG")).isEqualTo(LogLevel.WTF);
+ Truth.assertThat(tlsState.getShouldCollectStacktrace("SOME_TAG")).isFalse();
+ }
+
+ @Test
+ public void defaultTraceMode() {
+ final ProtoLogDataSource.TlsState tlsState = createTlsState(
+ DataSourceConfigOuterClass.DataSourceConfig.newBuilder()
+ .setProtologConfig(
+ ProtologConfig.ProtoLogConfig.newBuilder()
+ .setTracingMode(
+ ProtologConfig.ProtoLogConfig.TracingMode
+ .ENABLE_ALL)
+ .build()
+ ).build());
+
+ Truth.assertThat(tlsState.getLogFromLevel("SOME_TAG")).isEqualTo(LogLevel.DEBUG);
+ Truth.assertThat(tlsState.getShouldCollectStacktrace("SOME_TAG")).isFalse();
+ }
+
+ @Test
+ public void allEnabledTraceMode() {
+ final ProtoLogDataSource ds = new ProtoLogDataSource(() -> {}, () -> {}, () -> {});
+
+ final ProtoLogDataSource.TlsState tlsState = createTlsState(
+ DataSourceConfigOuterClass.DataSourceConfig.newBuilder().setProtologConfig(
+ ProtologConfig.ProtoLogConfig.newBuilder()
+ .setTracingMode(
+ ProtologConfig.ProtoLogConfig.TracingMode.ENABLE_ALL)
+ .build()
+ ).build()
+ );
+
+ Truth.assertThat(tlsState.getLogFromLevel("SOME_TAG")).isEqualTo(LogLevel.DEBUG);
+ Truth.assertThat(tlsState.getShouldCollectStacktrace("SOME_TAG")).isFalse();
+ }
+
+ @Test
+ public void requireGroupTagInOverrides() {
+ Exception exception = assertThrows(RuntimeException.class, () -> {
+ createTlsState(DataSourceConfigOuterClass.DataSourceConfig.newBuilder()
+ .setProtologConfig(
+ ProtologConfig.ProtoLogConfig.newBuilder()
+ .addGroupOverrides(
+ ProtologConfig.ProtoLogGroup.newBuilder()
+ .setLogFrom(
+ ProtologCommon.ProtoLogLevel
+ .PROTOLOG_LEVEL_WARN)
+ .setCollectStacktrace(true)
+ )
+ .build()
+ ).build());
+ });
+
+ Truth.assertThat(exception).hasMessageThat().contains("group override without a group tag");
+ }
+
+ @Test
+ public void stackTraceCollection() {
+ final ProtoLogDataSource.TlsState tlsState = createTlsState(
+ DataSourceConfigOuterClass.DataSourceConfig.newBuilder().setProtologConfig(
+ ProtologConfig.ProtoLogConfig.newBuilder()
+ .addGroupOverrides(
+ ProtologConfig.ProtoLogGroup.newBuilder()
+ .setGroupName("SOME_TAG")
+ .setCollectStacktrace(true)
+ )
+ .build()
+ ).build());
+
+ Truth.assertThat(tlsState.getShouldCollectStacktrace("SOME_TAG")).isTrue();
+ }
+
+ @Test
+ public void groupLogFromOverrides() {
+ final ProtoLogDataSource.TlsState tlsState = createTlsState(
+ DataSourceConfigOuterClass.DataSourceConfig.newBuilder().setProtologConfig(
+ ProtologConfig.ProtoLogConfig.newBuilder()
+ .addGroupOverrides(
+ ProtologConfig.ProtoLogGroup.newBuilder()
+ .setGroupName("SOME_TAG")
+ .setLogFrom(
+ ProtologCommon.ProtoLogLevel
+ .PROTOLOG_LEVEL_DEBUG)
+ .setCollectStacktrace(true)
+ )
+ .addGroupOverrides(
+ ProtologConfig.ProtoLogGroup.newBuilder()
+ .setGroupName("SOME_OTHER_TAG")
+ .setLogFrom(
+ ProtologCommon.ProtoLogLevel
+ .PROTOLOG_LEVEL_WARN)
+ )
+ .build()
+ ).build());
+
+ Truth.assertThat(tlsState.getLogFromLevel("SOME_TAG")).isEqualTo(LogLevel.DEBUG);
+ Truth.assertThat(tlsState.getShouldCollectStacktrace("SOME_TAG")).isTrue();
+
+ Truth.assertThat(tlsState.getLogFromLevel("SOME_OTHER_TAG")).isEqualTo(LogLevel.WARN);
+ Truth.assertThat(tlsState.getShouldCollectStacktrace("SOME_OTHER_TAG")).isFalse();
+
+ Truth.assertThat(tlsState.getLogFromLevel("UNKNOWN_TAG")).isEqualTo(LogLevel.WTF);
+ Truth.assertThat(tlsState.getShouldCollectStacktrace("UNKNOWN_TAG")).isFalse();
+ }
+
+ private ProtoLogDataSource.TlsState createTlsState(
+ DataSourceConfigOuterClass.DataSourceConfig config) {
+ final ProtoLogDataSource ds =
+ Mockito.spy(new ProtoLogDataSource(() -> {}, () -> {}, () -> {}));
+
+ ProtoInputStream configStream = new ProtoInputStream(config.toByteArray());
+ final ProtoLogDataSource.Instance dsInstance = Mockito.spy(
+ ds.createInstance(configStream, 8));
+ Mockito.doNothing().when(dsInstance).release();
+ final CreateTlsStateArgs mockCreateTlsStateArgs = Mockito.mock(CreateTlsStateArgs.class);
+ Mockito.when(mockCreateTlsStateArgs.getDataSourceInstanceLocked()).thenReturn(dsInstance);
+ return ds.createTlsState(mockCreateTlsStateArgs);
+ }
+}
diff --git a/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java b/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
new file mode 100644
index 000000000000..4c311050568b
--- /dev/null
+++ b/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
@@ -0,0 +1,586 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.protolog;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import static java.io.File.createTempFile;
+import static java.nio.file.Files.createTempDirectory;
+
+import android.content.Context;
+import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
+import android.tools.common.ScenarioBuilder;
+import android.tools.common.traces.protolog.ProtoLogTrace;
+import android.tools.device.traces.TraceConfig;
+import android.tools.device.traces.TraceConfigs;
+import android.tools.device.traces.io.ResultReader;
+import android.tools.device.traces.io.ResultWriter;
+import android.tools.device.traces.monitors.PerfettoTraceMonitor;
+import android.tracing.perfetto.DataSource;
+import android.util.proto.ProtoInputStream;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.protolog.common.IProtoLogGroup;
+import com.android.internal.protolog.common.LogDataType;
+import com.android.internal.protolog.common.LogLevel;
+
+import com.google.common.truth.Truth;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Random;
+
+import perfetto.protos.Protolog;
+import perfetto.protos.ProtologCommon;
+
+/**
+ * Test class for {@link ProtoLogImpl}.
+ */
+@SuppressWarnings("ConstantConditions")
+@SmallTest
+@Presubmit
+@RunWith(JUnit4.class)
+public class PerfettoProtoLogImplTest {
+ private final File mTracingDirectory = createTempDirectory("temp").toFile();
+
+ private final ResultWriter mWriter = new ResultWriter()
+ .forScenario(new ScenarioBuilder()
+ .forClass(createTempFile("temp", "").getName()).build())
+ .withOutputDir(mTracingDirectory)
+ .setRunComplete();
+
+ private final TraceConfigs mTraceConfig = new TraceConfigs(
+ new TraceConfig(false, true, false),
+ new TraceConfig(false, true, false),
+ new TraceConfig(false, true, false),
+ new TraceConfig(false, true, false)
+ );
+
+ private PerfettoProtoLogImpl mProtoLog;
+ private Protolog.ProtoLogViewerConfig.Builder mViewerConfigBuilder;
+ private File mFile;
+
+ private ProtoLogViewerConfigReader mReader;
+
+ public PerfettoProtoLogImplTest() throws IOException {
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ final Context testContext = getInstrumentation().getContext();
+ mFile = testContext.getFileStreamPath("tracing_test.dat");
+ //noinspection ResultOfMethodCallIgnored
+ mFile.delete();
+
+ mViewerConfigBuilder = Protolog.ProtoLogViewerConfig.newBuilder()
+ .addGroups(
+ Protolog.ProtoLogViewerConfig.Group.newBuilder()
+ .setId(1)
+ .setName(TestProtoLogGroup.TEST_GROUP.toString())
+ .setTag(TestProtoLogGroup.TEST_GROUP.getTag())
+ ).addMessages(
+ Protolog.ProtoLogViewerConfig.MessageData.newBuilder()
+ .setMessageId(1)
+ .setMessage("My Test Debug Log Message %b")
+ .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_DEBUG)
+ .setGroupId(1)
+ ).addMessages(
+ Protolog.ProtoLogViewerConfig.MessageData.newBuilder()
+ .setMessageId(2)
+ .setMessage("My Test Verbose Log Message %b")
+ .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_VERBOSE)
+ .setGroupId(1)
+ ).addMessages(
+ Protolog.ProtoLogViewerConfig.MessageData.newBuilder()
+ .setMessageId(3)
+ .setMessage("My Test Warn Log Message %b")
+ .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_WARN)
+ .setGroupId(1)
+ ).addMessages(
+ Protolog.ProtoLogViewerConfig.MessageData.newBuilder()
+ .setMessageId(4)
+ .setMessage("My Test Error Log Message %b")
+ .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_ERROR)
+ .setGroupId(1)
+ ).addMessages(
+ Protolog.ProtoLogViewerConfig.MessageData.newBuilder()
+ .setMessageId(5)
+ .setMessage("My Test WTF Log Message %b")
+ .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_WTF)
+ .setGroupId(1)
+ );
+
+ ViewerConfigInputStreamProvider viewerConfigInputStreamProvider = Mockito.mock(
+ ViewerConfigInputStreamProvider.class);
+ Mockito.when(viewerConfigInputStreamProvider.getInputStream())
+ .thenAnswer(it -> new ProtoInputStream(mViewerConfigBuilder.build().toByteArray()));
+
+ mReader = Mockito.spy(new ProtoLogViewerConfigReader(viewerConfigInputStreamProvider));
+ mProtoLog = new PerfettoProtoLogImpl(viewerConfigInputStreamProvider, mReader);
+ }
+
+ @After
+ public void tearDown() {
+ if (mFile != null) {
+ //noinspection ResultOfMethodCallIgnored
+ mFile.delete();
+ }
+ ProtoLogImpl.setSingleInstance(null);
+ }
+
+ @Test
+ public void isEnabled_returnsFalseByDefault() {
+ assertFalse(mProtoLog.isProtoEnabled());
+ }
+
+ @Test
+ public void isEnabled_returnsTrueAfterStart() {
+ PerfettoTraceMonitor traceMonitor =
+ PerfettoTraceMonitor.newBuilder().enableProtoLog().build();
+ try {
+ traceMonitor.start();
+ assertTrue(mProtoLog.isProtoEnabled());
+ } finally {
+ traceMonitor.stop(mWriter);
+ }
+ }
+
+ @Test
+ public void isEnabled_returnsFalseAfterStop() {
+ PerfettoTraceMonitor traceMonitor =
+ PerfettoTraceMonitor.newBuilder().enableProtoLog().build();
+ try {
+ traceMonitor.start();
+ assertTrue(mProtoLog.isProtoEnabled());
+ } finally {
+ traceMonitor.stop(mWriter);
+ }
+
+ assertFalse(mProtoLog.isProtoEnabled());
+ }
+
+ @Test
+ public void defaultMode() throws IOException {
+ PerfettoTraceMonitor traceMonitor =
+ PerfettoTraceMonitor.newBuilder().enableProtoLog(false).build();
+ try {
+ traceMonitor.start();
+ // Shouldn't be logging anything except WTF unless explicitly requested in the group
+ // override.
+ mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
+ LogDataType.BOOLEAN, null, new Object[]{true});
+ mProtoLog.log(LogLevel.VERBOSE, TestProtoLogGroup.TEST_GROUP, 2,
+ LogDataType.BOOLEAN, null, new Object[]{true});
+ mProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP, 3,
+ LogDataType.BOOLEAN, null, new Object[]{true});
+ mProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP, 4,
+ LogDataType.BOOLEAN, null, new Object[]{true});
+ mProtoLog.log(LogLevel.WTF, TestProtoLogGroup.TEST_GROUP, 5,
+ LogDataType.BOOLEAN, null, new Object[]{true});
+ } finally {
+ traceMonitor.stop(mWriter);
+ }
+
+ final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig);
+ final ProtoLogTrace protolog = reader.readProtoLogTrace();
+
+ Truth.assertThat(protolog.messages).hasSize(1);
+ Truth.assertThat(protolog.messages.getFirst().getLevel()).isEqualTo(LogLevel.WTF);
+ }
+
+ @Test
+ public void respectsOverrideConfigs_defaultMode() throws IOException {
+ PerfettoTraceMonitor traceMonitor =
+ PerfettoTraceMonitor.newBuilder().enableProtoLog(true,
+ List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride(
+ TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.DEBUG, true)))
+ .build();
+ try {
+ traceMonitor.start();
+ mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
+ LogDataType.BOOLEAN, null, new Object[]{true});
+ mProtoLog.log(LogLevel.VERBOSE, TestProtoLogGroup.TEST_GROUP, 2,
+ LogDataType.BOOLEAN, null, new Object[]{true});
+ mProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP, 3,
+ LogDataType.BOOLEAN, null, new Object[]{true});
+ mProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP, 4,
+ LogDataType.BOOLEAN, null, new Object[]{true});
+ mProtoLog.log(LogLevel.WTF, TestProtoLogGroup.TEST_GROUP, 5,
+ LogDataType.BOOLEAN, null, new Object[]{true});
+ } finally {
+ traceMonitor.stop(mWriter);
+ }
+
+ final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig);
+ final ProtoLogTrace protolog = reader.readProtoLogTrace();
+
+ Truth.assertThat(protolog.messages).hasSize(5);
+ Truth.assertThat(protolog.messages.get(0).getLevel()).isEqualTo(LogLevel.DEBUG);
+ Truth.assertThat(protolog.messages.get(1).getLevel()).isEqualTo(LogLevel.VERBOSE);
+ Truth.assertThat(protolog.messages.get(2).getLevel()).isEqualTo(LogLevel.WARN);
+ Truth.assertThat(protolog.messages.get(3).getLevel()).isEqualTo(LogLevel.ERROR);
+ Truth.assertThat(protolog.messages.get(4).getLevel()).isEqualTo(LogLevel.WTF);
+ }
+
+ @Test
+ public void respectsOverrideConfigs_allEnabledMode() throws IOException {
+ PerfettoTraceMonitor traceMonitor =
+ PerfettoTraceMonitor.newBuilder().enableProtoLog(true,
+ List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride(
+ TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.WARN, false)))
+ .build();
+ try {
+ traceMonitor.start();
+ mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
+ LogDataType.BOOLEAN, null, new Object[]{true});
+ mProtoLog.log(LogLevel.VERBOSE, TestProtoLogGroup.TEST_GROUP, 2,
+ LogDataType.BOOLEAN, null, new Object[]{true});
+ mProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP, 3,
+ LogDataType.BOOLEAN, null, new Object[]{true});
+ mProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP, 4,
+ LogDataType.BOOLEAN, null, new Object[]{true});
+ mProtoLog.log(LogLevel.WTF, TestProtoLogGroup.TEST_GROUP, 5,
+ LogDataType.BOOLEAN, null, new Object[]{true});
+ } finally {
+ traceMonitor.stop(mWriter);
+ }
+
+ final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig);
+ final ProtoLogTrace protolog = reader.readProtoLogTrace();
+
+ Truth.assertThat(protolog.messages).hasSize(3);
+ Truth.assertThat(protolog.messages.get(0).getLevel()).isEqualTo(LogLevel.WARN);
+ Truth.assertThat(protolog.messages.get(1).getLevel()).isEqualTo(LogLevel.ERROR);
+ Truth.assertThat(protolog.messages.get(2).getLevel()).isEqualTo(LogLevel.WTF);
+ }
+
+ @Test
+ public void respectsAllEnabledMode() throws IOException {
+ PerfettoTraceMonitor traceMonitor =
+ PerfettoTraceMonitor.newBuilder().enableProtoLog(true, List.of())
+ .build();
+ try {
+ traceMonitor.start();
+ mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
+ LogDataType.BOOLEAN, null, new Object[]{true});
+ mProtoLog.log(LogLevel.VERBOSE, TestProtoLogGroup.TEST_GROUP, 2,
+ LogDataType.BOOLEAN, null, new Object[]{true});
+ mProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP, 3,
+ LogDataType.BOOLEAN, null, new Object[]{true});
+ mProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP, 4,
+ LogDataType.BOOLEAN, null, new Object[]{true});
+ mProtoLog.log(LogLevel.WTF, TestProtoLogGroup.TEST_GROUP, 5,
+ LogDataType.BOOLEAN, null, new Object[]{true});
+ } finally {
+ traceMonitor.stop(mWriter);
+ }
+
+ final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig);
+ final ProtoLogTrace protolog = reader.readProtoLogTrace();
+
+ Truth.assertThat(protolog.messages).hasSize(5);
+ Truth.assertThat(protolog.messages.get(0).getLevel()).isEqualTo(LogLevel.DEBUG);
+ Truth.assertThat(protolog.messages.get(1).getLevel()).isEqualTo(LogLevel.VERBOSE);
+ Truth.assertThat(protolog.messages.get(2).getLevel()).isEqualTo(LogLevel.WARN);
+ Truth.assertThat(protolog.messages.get(3).getLevel()).isEqualTo(LogLevel.ERROR);
+ Truth.assertThat(protolog.messages.get(4).getLevel()).isEqualTo(LogLevel.WTF);
+ }
+
+ @Test
+ public void log_logcatEnabledExternalMessage() {
+ when(mReader.getViewerString(anyLong())).thenReturn("test %b %d %% 0x%x %s %f");
+ PerfettoProtoLogImpl implSpy = Mockito.spy(mProtoLog);
+ TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
+ TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
+
+ implSpy.log(
+ LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, null,
+ new Object[]{true, 10000, 30000, "test", 0.000003});
+
+ verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq(
+ LogLevel.INFO),
+ eq("test true 10000 % 0x7530 test 3.0E-6"));
+ verify(mReader).getViewerString(eq(1234L));
+ }
+
+ @Test
+ public void log_logcatEnabledInvalidMessage() {
+ when(mReader.getViewerString(anyLong())).thenReturn("test %b %d %% %x %s %f");
+ PerfettoProtoLogImpl implSpy = Mockito.spy(mProtoLog);
+ TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
+ TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
+
+ implSpy.log(
+ LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, null,
+ new Object[]{true, 10000, 0.0001, 0.00002, "test"});
+
+ verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq(
+ LogLevel.INFO),
+ eq("UNKNOWN MESSAGE (1234) true 10000 1.0E-4 2.0E-5 test"));
+ verify(mReader).getViewerString(eq(1234L));
+ }
+
+ @Test
+ public void log_logcatEnabledInlineMessage() {
+ when(mReader.getViewerString(anyLong())).thenReturn("test %d");
+ PerfettoProtoLogImpl implSpy = Mockito.spy(mProtoLog);
+ TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
+ TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
+
+ implSpy.log(
+ LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, "test %d",
+ new Object[]{5});
+
+ verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq(
+ LogLevel.INFO), eq("test 5"));
+ verify(mReader, never()).getViewerString(anyLong());
+ }
+
+ @Test
+ public void log_logcatEnabledNoMessage() {
+ when(mReader.getViewerString(anyLong())).thenReturn(null);
+ PerfettoProtoLogImpl implSpy = Mockito.spy(mProtoLog);
+ TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
+ TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
+
+ implSpy.log(
+ LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, null,
+ new Object[]{5});
+
+ verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq(
+ LogLevel.INFO), eq("UNKNOWN MESSAGE (1234) 5"));
+ verify(mReader).getViewerString(eq(1234L));
+ }
+
+ @Test
+ public void log_logcatDisabled() {
+ when(mReader.getViewerString(anyLong())).thenReturn("test %d");
+ PerfettoProtoLogImpl implSpy = Mockito.spy(mProtoLog);
+ TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false);
+
+ implSpy.log(
+ LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, "test %d",
+ new Object[]{5});
+
+ verify(implSpy, never()).passToLogcat(any(), any(), any());
+ verify(mReader, never()).getViewerString(anyLong());
+ }
+
+ @Test
+ public void log_protoEnabled() throws Exception {
+ final long messageHash = addMessageToConfig(
+ ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_INFO,
+ "My test message :: %s, %d, %o, %x, %f, %e, %g, %b");
+
+ PerfettoTraceMonitor traceMonitor =
+ PerfettoTraceMonitor.newBuilder().enableProtoLog().build();
+ long before;
+ long after;
+ try {
+ traceMonitor.start();
+ assertTrue(mProtoLog.isProtoEnabled());
+
+ before = SystemClock.elapsedRealtimeNanos();
+ mProtoLog.log(
+ LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, messageHash,
+ 0b1110101001010100, null,
+ new Object[]{"test", 1, 2, 3, 0.4, 0.5, 0.6, true});
+ after = SystemClock.elapsedRealtimeNanos();
+ } finally {
+ traceMonitor.stop(mWriter);
+ }
+
+ final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig);
+ final ProtoLogTrace protolog = reader.readProtoLogTrace();
+
+ Truth.assertThat(protolog.messages).hasSize(1);
+ Truth.assertThat(protolog.messages.getFirst().getTimestamp().getElapsedNanos())
+ .isAtLeast(before);
+ Truth.assertThat(protolog.messages.getFirst().getTimestamp().getElapsedNanos())
+ .isAtMost(after);
+ Truth.assertThat(protolog.messages.getFirst().getMessage())
+ .isEqualTo("My test message :: test, 2, 4, 6, 0.400000, 5.000000e-01, 0.6, true");
+ }
+
+ private long addMessageToConfig(ProtologCommon.ProtoLogLevel logLevel, String message) {
+ final long messageId = new Random().nextLong();
+ mViewerConfigBuilder.addMessages(Protolog.ProtoLogViewerConfig.MessageData.newBuilder()
+ .setMessageId(messageId)
+ .setMessage(message)
+ .setLevel(logLevel)
+ .setGroupId(1)
+ );
+
+ return messageId;
+ }
+
+ @Test
+ public void log_invalidParamsMask() {
+ final long messageHash = addMessageToConfig(
+ ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_INFO,
+ "My test message :: %s, %d, %f, %b");
+ PerfettoTraceMonitor traceMonitor =
+ PerfettoTraceMonitor.newBuilder().enableProtoLog().build();
+ long before;
+ long after;
+ try {
+ traceMonitor.start();
+ before = SystemClock.elapsedRealtimeNanos();
+ mProtoLog.log(
+ LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, messageHash,
+ 0b01100100, null,
+ new Object[]{"test", 1, 0.1, true});
+ after = SystemClock.elapsedRealtimeNanos();
+ } finally {
+ traceMonitor.stop(mWriter);
+ }
+
+ final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig);
+ assertThrows(IllegalStateException.class, reader::readProtoLogTrace);
+ }
+
+ @Test
+ public void log_protoDisabled() throws Exception {
+ PerfettoTraceMonitor traceMonitor =
+ PerfettoTraceMonitor.newBuilder().enableProtoLog(false).build();
+ try {
+ traceMonitor.start();
+ mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
+ 0b11, null, new Object[]{true});
+ } finally {
+ traceMonitor.stop(mWriter);
+ }
+
+ final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig);
+ final ProtoLogTrace protolog = reader.readProtoLogTrace();
+
+ Truth.assertThat(protolog.messages).isEmpty();
+ }
+
+ @Test
+ public void stackTraceTrimmed() throws IOException {
+ PerfettoTraceMonitor traceMonitor =
+ PerfettoTraceMonitor.newBuilder().enableProtoLog(true,
+ List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride(
+ TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.DEBUG, true)))
+ .build();
+ try {
+ traceMonitor.start();
+
+ ProtoLogImpl.setSingleInstance(mProtoLog);
+ ProtoLogImpl.d(TestProtoLogGroup.TEST_GROUP, 1,
+ 0b11, null, true);
+ } finally {
+ traceMonitor.stop(mWriter);
+ }
+
+ final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig);
+ final ProtoLogTrace protolog = reader.readProtoLogTrace();
+
+ Truth.assertThat(protolog.messages).hasSize(1);
+ String stacktrace = protolog.messages.getFirst().getStacktrace();
+ Truth.assertThat(stacktrace)
+ .doesNotContain(PerfettoProtoLogImpl.class.getSimpleName() + ".java");
+ Truth.assertThat(stacktrace).doesNotContain(DataSource.class.getSimpleName() + ".java");
+ Truth.assertThat(stacktrace)
+ .doesNotContain(ProtoLogImpl.class.getSimpleName() + ".java");
+ Truth.assertThat(stacktrace).contains(PerfettoProtoLogImplTest.class.getSimpleName());
+ Truth.assertThat(stacktrace).contains("stackTraceTrimmed");
+ }
+
+ private enum TestProtoLogGroup implements IProtoLogGroup {
+ TEST_GROUP(true, true, false, "TEST_TAG");
+
+ private final boolean mEnabled;
+ private volatile boolean mLogToProto;
+ private volatile boolean mLogToLogcat;
+ private final String mTag;
+
+ /**
+ * @param enabled set to false to exclude all log statements for this group from
+ * compilation,
+ * they will not be available in runtime.
+ * @param logToProto enable binary logging for the group
+ * @param logToLogcat enable text logging for the group
+ * @param tag name of the source of the logged message
+ */
+ TestProtoLogGroup(boolean enabled, boolean logToProto, boolean logToLogcat, String tag) {
+ this.mEnabled = enabled;
+ this.mLogToProto = logToProto;
+ this.mLogToLogcat = logToLogcat;
+ this.mTag = tag;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+
+ @Override
+ public boolean isLogToProto() {
+ return mLogToProto;
+ }
+
+ @Override
+ public boolean isLogToLogcat() {
+ return mLogToLogcat;
+ }
+
+ @Override
+ public boolean isLogToAny() {
+ return mLogToLogcat || mLogToProto;
+ }
+
+ @Override
+ public String getTag() {
+ return mTag;
+ }
+
+ @Override
+ public void setLogToProto(boolean logToProto) {
+ this.mLogToProto = logToProto;
+ }
+
+ @Override
+ public void setLogToLogcat(boolean logToLogcat) {
+ this.mLogToLogcat = logToLogcat;
+ }
+
+ }
+}
diff --git a/tests/Internal/src/com/android/internal/protolog/ProtoLogImplTest.java b/tests/Internal/src/com/android/internal/protolog/ProtoLogImplTest.java
index 7deb8c73d1fc..4267c2c127ae 100644
--- a/tests/Internal/src/com/android/internal/protolog/ProtoLogImplTest.java
+++ b/tests/Internal/src/com/android/internal/protolog/ProtoLogImplTest.java
@@ -16,49 +16,23 @@
package com.android.internal.protolog;
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
-import static com.android.internal.protolog.ProtoLogImpl.PROTOLOG_VERSION;
-
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import android.content.Context;
-import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
-import android.util.proto.ProtoInputStream;
import androidx.test.filters.SmallTest;
+import com.android.internal.protolog.common.IProtoLog;
import com.android.internal.protolog.common.IProtoLogGroup;
+import com.android.internal.protolog.common.LogLevel;
import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.PrintWriter;
-import java.util.LinkedList;
/**
* Test class for {@link ProtoLogImpl}.
@@ -68,336 +42,78 @@ import java.util.LinkedList;
@Presubmit
@RunWith(JUnit4.class)
public class ProtoLogImplTest {
-
- private static final byte[] MAGIC_HEADER = new byte[]{
- 0x9, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x4c, 0x4f, 0x47
- };
-
- private ProtoLogImpl mProtoLog;
- private File mFile;
-
- @Mock
- private ProtoLogViewerConfigReader mReader;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- final Context testContext = getInstrumentation().getContext();
- mFile = testContext.getFileStreamPath("tracing_test.dat");
- //noinspection ResultOfMethodCallIgnored
- mFile.delete();
- mProtoLog = new ProtoLogImpl(mFile, 1024 * 1024, mReader, 1024);
- }
-
@After
public void tearDown() {
- if (mFile != null) {
- //noinspection ResultOfMethodCallIgnored
- mFile.delete();
- }
ProtoLogImpl.setSingleInstance(null);
}
@Test
- public void isEnabled_returnsFalseByDefault() {
- assertFalse(mProtoLog.isProtoEnabled());
- }
-
- @Test
- public void isEnabled_returnsTrueAfterStart() {
- mProtoLog.startProtoLog(mock(PrintWriter.class));
- assertTrue(mProtoLog.isProtoEnabled());
- }
-
- @Test
- public void isEnabled_returnsFalseAfterStop() {
- mProtoLog.startProtoLog(mock(PrintWriter.class));
- mProtoLog.stopProtoLog(mock(PrintWriter.class), true);
- assertFalse(mProtoLog.isProtoEnabled());
- }
-
- @Test
- public void logFile_startsWithMagicHeader() throws Exception {
- mProtoLog.startProtoLog(mock(PrintWriter.class));
- mProtoLog.stopProtoLog(mock(PrintWriter.class), true);
-
- assertTrue("Log file should exist", mFile.exists());
-
- byte[] header = new byte[MAGIC_HEADER.length];
- try (InputStream is = new FileInputStream(mFile)) {
- assertEquals(MAGIC_HEADER.length, is.read(header));
- assertArrayEquals(MAGIC_HEADER, header);
- }
- }
-
- @Test
public void getSingleInstance() {
- ProtoLogImpl mockedProtoLog = mock(ProtoLogImpl.class);
+ IProtoLog mockedProtoLog = mock(IProtoLog.class);
ProtoLogImpl.setSingleInstance(mockedProtoLog);
assertSame(mockedProtoLog, ProtoLogImpl.getSingleInstance());
}
@Test
public void d_logCalled() {
- ProtoLogImpl mockedProtoLog = mock(ProtoLogImpl.class);
+ IProtoLog mockedProtoLog = mock(IProtoLog.class);
ProtoLogImpl.setSingleInstance(mockedProtoLog);
ProtoLogImpl.d(TestProtoLogGroup.TEST_GROUP, 1234, 4321, "test %d");
- verify(mockedProtoLog).log(eq(ProtoLogImpl.LogLevel.DEBUG), eq(
+ verify(mockedProtoLog).log(eq(LogLevel.DEBUG), eq(
TestProtoLogGroup.TEST_GROUP),
- eq(1234), eq(4321), eq("test %d"), eq(new Object[]{}));
+ eq(1234L), eq(4321), eq("test %d"), eq(new Object[]{}));
}
@Test
public void v_logCalled() {
- ProtoLogImpl mockedProtoLog = mock(ProtoLogImpl.class);
+ IProtoLog mockedProtoLog = mock(IProtoLog.class);
ProtoLogImpl.setSingleInstance(mockedProtoLog);
ProtoLogImpl.v(TestProtoLogGroup.TEST_GROUP, 1234, 4321, "test %d");
- verify(mockedProtoLog).log(eq(ProtoLogImpl.LogLevel.VERBOSE), eq(
+ verify(mockedProtoLog).log(eq(LogLevel.VERBOSE), eq(
TestProtoLogGroup.TEST_GROUP),
- eq(1234), eq(4321), eq("test %d"), eq(new Object[]{}));
+ eq(1234L), eq(4321), eq("test %d"), eq(new Object[]{}));
}
@Test
public void i_logCalled() {
- ProtoLogImpl mockedProtoLog = mock(ProtoLogImpl.class);
+ IProtoLog mockedProtoLog = mock(IProtoLog.class);
ProtoLogImpl.setSingleInstance(mockedProtoLog);
ProtoLogImpl.i(TestProtoLogGroup.TEST_GROUP, 1234, 4321, "test %d");
- verify(mockedProtoLog).log(eq(ProtoLogImpl.LogLevel.INFO), eq(
+ verify(mockedProtoLog).log(eq(LogLevel.INFO), eq(
TestProtoLogGroup.TEST_GROUP),
- eq(1234), eq(4321), eq("test %d"), eq(new Object[]{}));
+ eq(1234L), eq(4321), eq("test %d"), eq(new Object[]{}));
}
@Test
public void w_logCalled() {
- ProtoLogImpl mockedProtoLog = mock(ProtoLogImpl.class);
+ IProtoLog mockedProtoLog = mock(IProtoLog.class);
ProtoLogImpl.setSingleInstance(mockedProtoLog);
ProtoLogImpl.w(TestProtoLogGroup.TEST_GROUP, 1234,
4321, "test %d");
- verify(mockedProtoLog).log(eq(ProtoLogImpl.LogLevel.WARN), eq(
+ verify(mockedProtoLog).log(eq(LogLevel.WARN), eq(
TestProtoLogGroup.TEST_GROUP),
- eq(1234), eq(4321), eq("test %d"), eq(new Object[]{}));
+ eq(1234L), eq(4321), eq("test %d"), eq(new Object[]{}));
}
@Test
public void e_logCalled() {
- ProtoLogImpl mockedProtoLog = mock(ProtoLogImpl.class);
+ IProtoLog mockedProtoLog = mock(IProtoLog.class);
ProtoLogImpl.setSingleInstance(mockedProtoLog);
ProtoLogImpl.e(TestProtoLogGroup.TEST_GROUP, 1234, 4321, "test %d");
- verify(mockedProtoLog).log(eq(ProtoLogImpl.LogLevel.ERROR), eq(
+ verify(mockedProtoLog).log(eq(LogLevel.ERROR), eq(
TestProtoLogGroup.TEST_GROUP),
- eq(1234), eq(4321), eq("test %d"), eq(new Object[]{}));
+ eq(1234L), eq(4321), eq("test %d"), eq(new Object[]{}));
}
@Test
public void wtf_logCalled() {
- ProtoLogImpl mockedProtoLog = mock(ProtoLogImpl.class);
+ IProtoLog mockedProtoLog = mock(IProtoLog.class);
ProtoLogImpl.setSingleInstance(mockedProtoLog);
ProtoLogImpl.wtf(TestProtoLogGroup.TEST_GROUP,
1234, 4321, "test %d");
- verify(mockedProtoLog).log(eq(ProtoLogImpl.LogLevel.WTF), eq(
+ verify(mockedProtoLog).log(eq(LogLevel.WTF), eq(
TestProtoLogGroup.TEST_GROUP),
- eq(1234), eq(4321), eq("test %d"), eq(new Object[]{}));
- }
-
- @Test
- public void log_logcatEnabledExternalMessage() {
- when(mReader.getViewerString(anyInt())).thenReturn("test %b %d %% 0x%x %s %f");
- ProtoLogImpl implSpy = Mockito.spy(mProtoLog);
- TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
- TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
-
- implSpy.log(
- ProtoLogImpl.LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, null,
- new Object[]{true, 10000, 30000, "test", 0.000003});
-
- verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq(
- ProtoLogImpl.LogLevel.INFO),
- eq("test true 10000 % 0x7530 test 3.0E-6"));
- verify(mReader).getViewerString(eq(1234));
- }
-
- @Test
- public void log_logcatEnabledInvalidMessage() {
- when(mReader.getViewerString(anyInt())).thenReturn("test %b %d %% %x %s %f");
- ProtoLogImpl implSpy = Mockito.spy(mProtoLog);
- TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
- TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
-
- implSpy.log(
- ProtoLogImpl.LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, null,
- new Object[]{true, 10000, 0.0001, 0.00002, "test"});
-
- verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq(
- ProtoLogImpl.LogLevel.INFO),
- eq("UNKNOWN MESSAGE (1234) true 10000 1.0E-4 2.0E-5 test"));
- verify(mReader).getViewerString(eq(1234));
- }
-
- @Test
- public void log_logcatEnabledInlineMessage() {
- when(mReader.getViewerString(anyInt())).thenReturn("test %d");
- ProtoLogImpl implSpy = Mockito.spy(mProtoLog);
- TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
- TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
-
- implSpy.log(
- ProtoLogImpl.LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, "test %d",
- new Object[]{5});
-
- verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq(
- ProtoLogImpl.LogLevel.INFO), eq("test 5"));
- verify(mReader, never()).getViewerString(anyInt());
- }
-
- @Test
- public void log_logcatEnabledNoMessage() {
- when(mReader.getViewerString(anyInt())).thenReturn(null);
- ProtoLogImpl implSpy = Mockito.spy(mProtoLog);
- TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
- TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
-
- implSpy.log(
- ProtoLogImpl.LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, null,
- new Object[]{5});
-
- verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq(
- ProtoLogImpl.LogLevel.INFO), eq("UNKNOWN MESSAGE (1234) 5"));
- verify(mReader).getViewerString(eq(1234));
- }
-
- @Test
- public void log_logcatDisabled() {
- when(mReader.getViewerString(anyInt())).thenReturn("test %d");
- ProtoLogImpl implSpy = Mockito.spy(mProtoLog);
- TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false);
- TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
-
- implSpy.log(
- ProtoLogImpl.LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, "test %d",
- new Object[]{5});
-
- verify(implSpy, never()).passToLogcat(any(), any(), any());
- verify(mReader, never()).getViewerString(anyInt());
- }
-
- private static class ProtoLogData {
- Integer mMessageHash = null;
- Long mElapsedTime = null;
- LinkedList<String> mStrParams = new LinkedList<>();
- LinkedList<Long> mSint64Params = new LinkedList<>();
- LinkedList<Double> mDoubleParams = new LinkedList<>();
- LinkedList<Boolean> mBooleanParams = new LinkedList<>();
- }
-
- private ProtoLogData readProtoLogSingle(ProtoInputStream ip) throws IOException {
- while (ip.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
- if (ip.getFieldNumber() == (int) ProtoLogFileProto.VERSION) {
- assertEquals(PROTOLOG_VERSION, ip.readString(ProtoLogFileProto.VERSION));
- continue;
- }
- if (ip.getFieldNumber() != (int) ProtoLogFileProto.LOG) {
- continue;
- }
- long token = ip.start(ProtoLogFileProto.LOG);
- ProtoLogData data = new ProtoLogData();
- while (ip.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
- switch (ip.getFieldNumber()) {
- case (int) ProtoLogMessage.MESSAGE_HASH: {
- data.mMessageHash = ip.readInt(ProtoLogMessage.MESSAGE_HASH);
- break;
- }
- case (int) ProtoLogMessage.ELAPSED_REALTIME_NANOS: {
- data.mElapsedTime = ip.readLong(ProtoLogMessage.ELAPSED_REALTIME_NANOS);
- break;
- }
- case (int) ProtoLogMessage.STR_PARAMS: {
- data.mStrParams.add(ip.readString(ProtoLogMessage.STR_PARAMS));
- break;
- }
- case (int) ProtoLogMessage.SINT64_PARAMS: {
- data.mSint64Params.add(ip.readLong(ProtoLogMessage.SINT64_PARAMS));
- break;
- }
- case (int) ProtoLogMessage.DOUBLE_PARAMS: {
- data.mDoubleParams.add(ip.readDouble(ProtoLogMessage.DOUBLE_PARAMS));
- break;
- }
- case (int) ProtoLogMessage.BOOLEAN_PARAMS: {
- data.mBooleanParams.add(ip.readBoolean(ProtoLogMessage.BOOLEAN_PARAMS));
- break;
- }
- }
- }
- ip.end(token);
- return data;
- }
- return null;
- }
-
- @Test
- public void log_protoEnabled() throws Exception {
- TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false);
- TestProtoLogGroup.TEST_GROUP.setLogToProto(true);
- mProtoLog.startProtoLog(mock(PrintWriter.class));
- long before = SystemClock.elapsedRealtimeNanos();
- mProtoLog.log(
- ProtoLogImpl.LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234,
- 0b1110101001010100, null,
- new Object[]{"test", 1, 2, 3, 0.4, 0.5, 0.6, true});
- long after = SystemClock.elapsedRealtimeNanos();
- mProtoLog.stopProtoLog(mock(PrintWriter.class), true);
- try (InputStream is = new FileInputStream(mFile)) {
- ProtoInputStream ip = new ProtoInputStream(is);
- ProtoLogData data = readProtoLogSingle(ip);
- assertNotNull(data);
- assertEquals(1234, data.mMessageHash.longValue());
- assertTrue(before <= data.mElapsedTime && data.mElapsedTime <= after);
- assertArrayEquals(new String[]{"test"}, data.mStrParams.toArray());
- assertArrayEquals(new Long[]{1L, 2L, 3L}, data.mSint64Params.toArray());
- assertArrayEquals(new Double[]{0.4, 0.5, 0.6}, data.mDoubleParams.toArray());
- assertArrayEquals(new Boolean[]{true}, data.mBooleanParams.toArray());
- }
- }
-
- @Test
- public void log_invalidParamsMask() throws Exception {
- TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false);
- TestProtoLogGroup.TEST_GROUP.setLogToProto(true);
- mProtoLog.startProtoLog(mock(PrintWriter.class));
- long before = SystemClock.elapsedRealtimeNanos();
- mProtoLog.log(
- ProtoLogImpl.LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234,
- 0b01100100, null,
- new Object[]{"test", 1, 0.1, true});
- long after = SystemClock.elapsedRealtimeNanos();
- mProtoLog.stopProtoLog(mock(PrintWriter.class), true);
- try (InputStream is = new FileInputStream(mFile)) {
- ProtoInputStream ip = new ProtoInputStream(is);
- ProtoLogData data = readProtoLogSingle(ip);
- assertNotNull(data);
- assertEquals(1234, data.mMessageHash.longValue());
- assertTrue(before <= data.mElapsedTime && data.mElapsedTime <= after);
- assertArrayEquals(new String[]{"test", "(INVALID PARAMS_MASK) true"},
- data.mStrParams.toArray());
- assertArrayEquals(new Long[]{1L}, data.mSint64Params.toArray());
- assertArrayEquals(new Double[]{0.1}, data.mDoubleParams.toArray());
- assertArrayEquals(new Boolean[]{}, data.mBooleanParams.toArray());
- }
- }
-
- @Test
- public void log_protoDisabled() throws Exception {
- TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false);
- TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
- mProtoLog.startProtoLog(mock(PrintWriter.class));
- mProtoLog.log(ProtoLogImpl.LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234,
- 0b11, null, new Object[]{true});
- mProtoLog.stopProtoLog(mock(PrintWriter.class), true);
- try (InputStream is = new FileInputStream(mFile)) {
- ProtoInputStream ip = new ProtoInputStream(is);
- ProtoLogData data = readProtoLogSingle(ip);
- assertNull(data);
- }
+ eq(1234L), eq(4321), eq("test %d"), eq(new Object[]{}));
}
private enum TestProtoLogGroup implements IProtoLogGroup {
diff --git a/tests/Internal/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java b/tests/Internal/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java
index ae5021638745..dbd85d38b7f2 100644
--- a/tests/Internal/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java
+++ b/tests/Internal/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java
@@ -72,8 +72,8 @@ public class ProtoLogViewerConfigReaderTest {
+ "}\n";
- private ProtoLogViewerConfigReader
- mConfig = new ProtoLogViewerConfigReader();
+ private LegacyProtoLogViewerConfigReader
+ mConfig = new LegacyProtoLogViewerConfigReader();
private File mTestViewerConfig;
@Before
@@ -98,7 +98,7 @@ public class ProtoLogViewerConfigReaderTest {
@Test
public void loadViewerConfig() {
- mConfig.loadViewerConfig(null, mTestViewerConfig.getAbsolutePath());
+ mConfig.loadViewerConfig(msg -> {}, mTestViewerConfig.getAbsolutePath());
assertEquals("Test completed successfully: %b", mConfig.getViewerString(70933285));
assertEquals("Test 2", mConfig.getViewerString(1352021864));
assertEquals("Window %s is already added", mConfig.getViewerString(409412266));
@@ -107,7 +107,7 @@ public class ProtoLogViewerConfigReaderTest {
@Test
public void loadViewerConfig_invalidFile() {
- mConfig.loadViewerConfig(null, "/tmp/unknown/file/does/not/exist");
+ mConfig.loadViewerConfig(msg -> {}, "/tmp/unknown/file/does/not/exist");
// No exception is thrown.
assertNull(mConfig.getViewerString(1));
}
diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/EmbeddedWindowService.java b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/EmbeddedWindowService.java
index 14230fe4c323..0fb4f90f354f 100644
--- a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/EmbeddedWindowService.java
+++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/EmbeddedWindowService.java
@@ -42,6 +42,7 @@ import android.view.SurfaceControlViewHost;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.TextView;
+import android.window.InputTransferToken;
public class EmbeddedWindowService extends Service {
private static final String TAG = "EmbeddedWindowService";
@@ -118,7 +119,7 @@ public class EmbeddedWindowService extends Service {
@Override
public void attachEmbeddedSurfaceControl(SurfaceControl parentSc, int displayId,
- IBinder hostToken) {
+ InputTransferToken inputTransferToken) {
mHandler.post(() -> {
Paint paint = new Paint();
paint.setTextSize(40);
@@ -134,7 +135,7 @@ public class EmbeddedWindowService extends Service {
c.drawText("Remote", 250, 250, paint);
surface.unlockCanvasAndPost(c);
WindowManager wm = getSystemService(WindowManager.class);
- wm.registerBatchedSurfaceControlInputReceiver(displayId, hostToken,
+ wm.registerBatchedSurfaceControlInputReceiver(displayId, inputTransferToken,
mSurfaceControl,
Choreographer.getInstance(), event -> {
Log.d(TAG, "onInputEvent-remote " + event);
diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/IAttachEmbeddedWindow.aidl b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/IAttachEmbeddedWindow.aidl
index 6b65b40ef8c6..e81f5f81481a 100644
--- a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/IAttachEmbeddedWindow.aidl
+++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/IAttachEmbeddedWindow.aidl
@@ -20,10 +20,12 @@ import android.os.IBinder;
import com.android.test.viewembed.IAttachEmbeddedWindowCallback;
import android.view.WindowManager.LayoutParams;
import android.view.SurfaceControl;
+import android.window.InputTransferToken;
interface IAttachEmbeddedWindow {
void attachEmbedded(IBinder hostToken, int width, int height, in IAttachEmbeddedWindowCallback callback);
void relayout(in LayoutParams lp);
- oneway void attachEmbeddedSurfaceControl(in SurfaceControl parentSurfaceControl, int displayId, IBinder hostToken);
+ oneway void attachEmbeddedSurfaceControl(in SurfaceControl parentSurfaceControl, int displayId,
+ in InputTransferToken inputTransferToken);
oneway void tearDownEmbeddedSurfaceControl();
} \ No newline at end of file
diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java
index 7330ec14011b..e700bc2f3d21 100644
--- a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java
+++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java
@@ -139,7 +139,7 @@ public class SurfaceInputTestActivity extends Activity {
surface.unlockCanvasAndPost(c);
WindowManager wm = getSystemService(WindowManager.class);
wm.registerBatchedSurfaceControlInputReceiver(getDisplayId(),
- attachedSurfaceControl.getHostToken(), mLocalSurfaceControl,
+ attachedSurfaceControl.getInputTransferToken(), mLocalSurfaceControl,
Choreographer.getInstance(), event -> {
Log.d(TAG, "onInputEvent-sc " + event);
return false;
@@ -160,7 +160,8 @@ public class SurfaceInputTestActivity extends Activity {
WindowManager wm = getSystemService(WindowManager.class);
wm.registerBatchedSurfaceControlInputReceiver(getDisplayId(),
- mLocalSurfaceView.getHostToken(), mLocalSurfaceView.getSurfaceControl(),
+ mLocalSurfaceView.getRootSurfaceControl().getInputTransferToken(),
+ mLocalSurfaceView.getSurfaceControl(),
Choreographer.getInstance(), event -> {
Log.d(TAG, "onInputEvent-local " + event);
return false;
@@ -210,7 +211,8 @@ public class SurfaceInputTestActivity extends Activity {
}
try {
mIAttachEmbeddedWindow.attachEmbeddedSurfaceControl(mParentSurfaceControl,
- getDisplayId(), mRemoteSurfaceView.getHostToken());
+ getDisplayId(),
+ mRemoteSurfaceView.getRootSurfaceControl().getInputTransferToken());
} catch (RemoteException e) {
Log.e(TAG, "Failed to load embedded SurfaceControl", e);
}
diff --git a/tests/graphics/HwAccelerationTest/Android.bp b/tests/graphics/HwAccelerationTest/Android.bp
index 51848f2857c9..d95a9b9dc015 100644
--- a/tests/graphics/HwAccelerationTest/Android.bp
+++ b/tests/graphics/HwAccelerationTest/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_android_core_graphics_stack",
// 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"
diff --git a/tests/graphics/HwAccelerationTest/jni/Android.bp b/tests/graphics/HwAccelerationTest/jni/Android.bp
index 8edddab0ad1f..76e4f9cb6009 100644
--- a/tests/graphics/HwAccelerationTest/jni/Android.bp
+++ b/tests/graphics/HwAccelerationTest/jni/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_android_core_graphics_stack",
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/tests/graphics/RenderThreadTest/Android.bp b/tests/graphics/RenderThreadTest/Android.bp
index b18b04edb4c4..d6d85e85d820 100644
--- a/tests/graphics/RenderThreadTest/Android.bp
+++ b/tests/graphics/RenderThreadTest/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_android_core_graphics_stack",
// 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"
diff --git a/tests/graphics/SilkFX/Android.bp b/tests/graphics/SilkFX/Android.bp
index 1e467db44545..b149cf3253b5 100644
--- a/tests/graphics/SilkFX/Android.bp
+++ b/tests/graphics/SilkFX/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_android_core_graphics_stack",
// 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"
diff --git a/tests/graphics/VectorDrawableTest/Android.bp b/tests/graphics/VectorDrawableTest/Android.bp
index 9da7c5fdbb17..0042a438698b 100644
--- a/tests/graphics/VectorDrawableTest/Android.bp
+++ b/tests/graphics/VectorDrawableTest/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_android_gpu",
// 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"
diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp b/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp
index afaa3f0f293f..8d05a974dc40 100644
--- a/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp
+++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_input_method_framework",
default_applicable_licenses: ["frameworks_base_license"],
}
diff --git a/tests/vcn/Android.bp b/tests/vcn/Android.bp
index 228520e8545b..ee2e7cfcd480 100644
--- a/tests/vcn/Android.bp
+++ b/tests/vcn/Android.bp
@@ -3,6 +3,7 @@
//########################################################################
package {
+ default_team: "trendy_team_enigma",
// 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"
@@ -30,6 +31,7 @@ android_test {
"platform-test-annotations",
"services.core",
"service-connectivity-tiramisu-pre-jarjar",
+ "flag-junit",
],
libs: [
"android.test.runner",
diff --git a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
index 34f884b94296..887630b03a8c 100644
--- a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
@@ -38,6 +38,7 @@ import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
@@ -58,6 +59,7 @@ import android.os.HandlerExecutor;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
import android.os.test.TestLooper;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
@@ -71,7 +73,10 @@ import android.util.ArraySet;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.telephony.flags.Flags;
+
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -128,6 +133,9 @@ public class TelephonySubscriptionTrackerTest {
TEST_SUBID_TO_CARRIER_CONFIG_MAP = Collections.unmodifiableMap(subIdToCarrierConfigMap);
}
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+
@NonNull private final Context mContext;
@NonNull private final TestLooper mTestLooper;
@NonNull private final Handler mHandler;
@@ -185,6 +193,7 @@ public class TelephonySubscriptionTrackerTest {
@Before
public void setUp() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_FIX_CRASH_ON_GETTING_CONFIG_WHEN_PHONE_IS_GONE);
doReturn(2).when(mTelephonyManager).getActiveModemCount();
mCallback = mock(TelephonySubscriptionTrackerCallback.class);
@@ -594,4 +603,14 @@ public class TelephonySubscriptionTrackerTest {
new ArraySet<>(Arrays.asList(TEST_SUBSCRIPTION_ID_1, TEST_SUBSCRIPTION_ID_2)),
snapshot.getAllSubIdsInGroup(TEST_PARCEL_UUID));
}
+
+ @Test
+ public void testCarrierConfigChangeWhenPhoneIsGoneShouldNotCrash() throws Exception {
+ doThrow(new IllegalStateException("Carrier config loader is not available."))
+ .when(mCarrierConfigManager)
+ .getConfigForSubId(eq(TEST_SUBSCRIPTION_ID_1), any());
+
+ sendCarrierConfigChange(true /* hasValidSubscription */);
+ mTestLooper.dispatchAll();
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java
index 9daba6a79a27..1d7be2f4f039 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java
@@ -144,7 +144,7 @@ public class IpSecPacketLossDetectorTest extends NetworkEvaluationTestBase {
mTestLooper.dispatchAll();
verify(mIpSecTransform)
- .getIpSecTransformState(any(), mTransformStateReceiverCaptor.capture());
+ .requestIpSecTransformState(any(), mTransformStateReceiverCaptor.capture());
return mTransformStateReceiverCaptor.getValue();
}
@@ -210,7 +210,7 @@ public class IpSecPacketLossDetectorTest extends NetworkEvaluationTestBase {
assertNull(mIpSecPacketLossDetector.getLastTransformState());
mTestLooper.moveTimeForward(POLL_IPSEC_STATE_INTERVAL_MS);
mTestLooper.dispatchAll();
- verify(newTransform).getIpSecTransformState(any(), any());
+ verify(newTransform).requestIpSecTransformState(any(), any());
}
@Test
diff --git a/tools/hoststubgen/hoststubgen/Android.bp b/tools/hoststubgen/hoststubgen/Android.bp
index c6dd29ce7cc6..30333da5e86c 100644
--- a/tools/hoststubgen/hoststubgen/Android.bp
+++ b/tools/hoststubgen/hoststubgen/Android.bp
@@ -54,6 +54,7 @@ java_library {
// This library is _not_ specific to Android APIs.
java_library_host {
name: "hoststubgen-helper-runtime",
+ defaults: ["ravenwood-internal-only-visibility-java"],
srcs: [
"helper-runtime-src/**/*.java",
],
@@ -64,11 +65,11 @@ java_library_host {
"guava",
],
jarjar_rules: "jarjar-rules.txt",
- visibility: ["//visibility:public"],
}
java_library {
name: "hoststubgen-helper-runtime.ravenwood",
+ defaults: ["ravenwood-internal-only-visibility-java"],
srcs: [
"helper-runtime-src/**/*.java",
],
@@ -79,7 +80,6 @@ java_library {
"guava",
],
jarjar_rules: "jarjar-rules.txt",
- visibility: ["//visibility:public"],
}
// Host-side stub generator tool.
@@ -152,34 +152,3 @@ genrule_defaults {
"hoststubgen_dump.txt",
],
}
-
-java_library_host {
- name: "hoststubgen-helper-libcore-runtime",
- srcs: [
- "helper-framework-runtime-src/libcore-fake/**/*.java",
- ],
- visibility: ["//visibility:private"],
-}
-
-java_host_for_device {
- name: "hoststubgen-helper-libcore-runtime.ravenwood",
- libs: [
- "hoststubgen-helper-libcore-runtime",
- ],
- visibility: ["//visibility:private"],
-}
-
-java_library {
- name: "hoststubgen-helper-framework-runtime.ravenwood",
- defaults: ["ravenwood-internal-only-visibility-java"],
- srcs: [
- "helper-framework-runtime-src/framework/**/*.java",
- ],
- libs: [
- "hoststubgen-helper-runtime.ravenwood",
- "framework-minus-apex.ravenwood",
- ],
- static_libs: [
- "hoststubgen-helper-libcore-runtime.ravenwood",
- ],
-}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt
new file mode 100644
index 000000000000..83e09bf7043e
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.hoststubgen.filters
+
+import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.asm.toJvmClassName
+
+/**
+ * Filter to apply a policy to classes extending or implementing a class,
+ * either directly or indirectly. (with a breadth first search.)
+ *
+ * The policy won't apply to the super class itself.
+ */
+class SubclassFilter(
+ private val classes: ClassNodes,
+ fallback: OutputFilter
+) : DelegatingFilter(fallback) {
+ private val mPolicies: MutableMap<String, FilterPolicyWithReason> = mutableMapOf()
+
+ /**
+ * Add a policy to all classes extending or implementing a class, either directly or indirectly.
+ */
+ fun addPolicy(superClassName: String, policy: FilterPolicyWithReason) {
+ mPolicies[superClassName.toJvmClassName()] = policy
+ }
+
+ override fun getPolicyForClass(className: String): FilterPolicyWithReason {
+ return findPolicyForClass(className) ?: super.getPolicyForClass(className)
+ }
+
+ /**
+ * Find a policy for a class with a breadth-first search.
+ */
+ private fun findPolicyForClass(className: String): FilterPolicyWithReason? {
+ val cn = classes.findClass(className) ?: return null
+
+ if (cn.superName == null) {
+ return null
+ }
+ // First, check the direct super class / interfaces.
+ mPolicies[cn.superName]?.let { policy ->
+ return policy
+ }
+ cn.interfaces?.forEach { iface ->
+ mPolicies[iface]?.let { policy ->
+ return policy
+ }
+ }
+
+ // Then recurse.
+ cn.superName?.let { superName ->
+ findPolicyForClass(superName)?.let { policy ->
+ return policy
+ }
+ }
+ cn.interfaces?.forEach { iface ->
+ findPolicyForClass(iface)?.let { policy ->
+ return policy
+ }
+ }
+ return null
+ }
+} \ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
index 6ad83fbab083..75b5fc8f77ea 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
@@ -58,7 +58,8 @@ fun createFilterFromTextPolicyFile(
): OutputFilter {
log.i("Loading offloaded annotations from $filename ...")
log.withIndent {
- val imf = InMemoryOutputFilter(classes, fallback)
+ val subclassFilter = SubclassFilter(classes, fallback)
+ val imf = InMemoryOutputFilter(classes, subclassFilter)
var lineNo = 0
@@ -94,6 +95,10 @@ fun createFilterFromTextPolicyFile(
}
className = fields[1]
+ // superClass is set when the class name starts with a "*".
+ val superClass = resolveExtendingClass(className)
+
+ // :aidl, etc?
val classType = resolveSpecialClass(className)
if (fields[2].startsWith("!")) {
@@ -124,8 +129,14 @@ fun createFilterFromTextPolicyFile(
when (classType) {
SpecialClass.NotSpecial -> {
// TODO: Duplicate check, etc
- imf.setPolicyForClass(
- className, policy.withReason(FILTER_REASON))
+ if (superClass == null) {
+ imf.setPolicyForClass(
+ className, policy.withReason(FILTER_REASON)
+ )
+ } else {
+ subclassFilter.addPolicy(superClass,
+ policy.withReason("extends $superClass"))
+ }
}
SpecialClass.Aidl -> {
if (aidlPolicy != null) {
@@ -243,6 +254,13 @@ private fun resolveSpecialClass(className: String): SpecialClass {
throw ParseException("Invalid special class name \"$className\"")
}
+private fun resolveExtendingClass(className: String): String? {
+ if (!className.startsWith("*")) {
+ return null
+ }
+ return className.substring(1)
+}
+
private fun parsePolicy(s: String): FilterPolicy {
return when (s.lowercase()) {
"s", "stub" -> FilterPolicy.Stub
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
index 70f56ae37a97..78f277e8254c 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
@@ -2589,6 +2589,567 @@ SourceFile: "TinyFrameworkPackageRedirect.java"
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
+ Compiled from "C1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.C1();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/C1;
+}
+SourceFile: "C1.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C2.class
+ Compiled from "C2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+ interfaces: 0, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.C2();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/subclasstest/C1."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/C2;
+}
+SourceFile: "C2.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C3.class
+ Compiled from "C3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C3
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+ interfaces: 0, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.C3();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/subclasstest/C2."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/C3;
+}
+SourceFile: "C3.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CA.class
+ Compiled from "CA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.CA
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CA
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.CA();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/CA;
+}
+SourceFile: "CA.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CB.class
+ Compiled from "CB.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.CB
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CB
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.CB();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/CB;
+}
+SourceFile: "CB.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.class
+ Compiled from "Class_C1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C1 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+ interfaces: 0, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.Class_C1();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/subclasstest/C1."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_C1;
+}
+SourceFile: "Class_C1.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.class
+ Compiled from "Class_C2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+ interfaces: 0, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.Class_C2();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/subclasstest/C2."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_C2;
+}
+SourceFile: "Class_C2.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.class
+ Compiled from "Class_C3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C3
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C3
+ interfaces: 0, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.Class_C3();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/subclasstest/C3."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_C3;
+}
+SourceFile: "Class_C3.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.class
+ Compiled from "Class_CA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_CA extends com.android.hoststubgen.test.tinyframework.subclasstest.CA
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CA
+ interfaces: 0, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.Class_CA();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/subclasstest/CA."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_CA;
+}
+SourceFile: "Class_CA.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.class
+ Compiled from "Class_CB.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_CB extends com.android.hoststubgen.test.tinyframework.subclasstest.CB
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CB
+ interfaces: 0, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.Class_CB();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/subclasstest/CB."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_CB;
+}
+SourceFile: "Class_CB.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.class
+ Compiled from "Class_CB_IA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_CB_IA extends com.android.hoststubgen.test.tinyframework.subclasstest.CB implements com.android.hoststubgen.test.tinyframework.subclasstest.IA
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CB
+ interfaces: 1, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.Class_CB_IA();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/subclasstest/CB."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA;
+}
+SourceFile: "Class_CB_IA.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.class
+ Compiled from "Class_I1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1 implements com.android.hoststubgen.test.tinyframework.subclasstest.I1
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_I1;
+}
+SourceFile: "Class_I1.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.class
+ Compiled from "Class_I1_IA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1_IA implements com.android.hoststubgen.test.tinyframework.subclasstest.I1,com.android.hoststubgen.test.tinyframework.subclasstest.IA
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA
+ super_class: #x // java/lang/Object
+ interfaces: 2, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1_IA();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA;
+}
+SourceFile: "Class_I1_IA.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.class
+ Compiled from "Class_I2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I2 implements com.android.hoststubgen.test.tinyframework.subclasstest.I2
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I2();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_I2;
+}
+SourceFile: "Class_I2.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.class
+ Compiled from "Class_I3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3 implements com.android.hoststubgen.test.tinyframework.subclasstest.I3
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_I3;
+}
+SourceFile: "Class_I3.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.class
+ Compiled from "Class_I3_IA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3_IA implements com.android.hoststubgen.test.tinyframework.subclasstest.I3,com.android.hoststubgen.test.tinyframework.subclasstest.IA
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA
+ super_class: #x // java/lang/Object
+ interfaces: 2, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3_IA();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA;
+}
+SourceFile: "Class_I3_IA.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.class
+ Compiled from "Class_IA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA implements com.android.hoststubgen.test.tinyframework.subclasstest.IA
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_IA;
+}
+SourceFile: "Class_IA.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.class
+ Compiled from "Class_IA_I1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA_I1 implements com.android.hoststubgen.test.tinyframework.subclasstest.IA,com.android.hoststubgen.test.tinyframework.subclasstest.I1
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1
+ super_class: #x // java/lang/Object
+ interfaces: 2, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA_I1();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1;
+}
+SourceFile: "Class_IA_I1.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.class
+ Compiled from "Class_IA_I3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA_I3 implements com.android.hoststubgen.test.tinyframework.subclasstest.IA,com.android.hoststubgen.test.tinyframework.subclasstest.I3
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3
+ super_class: #x // java/lang/Object
+ interfaces: 2, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA_I3();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3;
+}
+SourceFile: "Class_IA_I3.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.class
+ Compiled from "Class_IB.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_IB implements com.android.hoststubgen.test.tinyframework.subclasstest.IB
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IB();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_IB;
+}
+SourceFile: "Class_IB.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.class
+ Compiled from "Class_IB_IA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_IB_IA implements com.android.hoststubgen.test.tinyframework.subclasstest.IB,com.android.hoststubgen.test.tinyframework.subclasstest.IA
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA
+ super_class: #x // java/lang/Object
+ interfaces: 2, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IB_IA();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA;
+}
+SourceFile: "Class_IB_IA.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.class
+ Compiled from "Class_None.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_None
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_None
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.Class_None();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_None;
+}
+SourceFile: "Class_None.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I1.class
+ Compiled from "I1.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I1
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I1
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 1
+}
+SourceFile: "I1.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I2.class
+ Compiled from "I2.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I2 extends com.android.hoststubgen.test.tinyframework.subclasstest.I1
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I2
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 1
+}
+SourceFile: "I2.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I3.class
+ Compiled from "I3.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I3 extends com.android.hoststubgen.test.tinyframework.subclasstest.I2
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I3
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 1
+}
+SourceFile: "I3.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IA.class
+ Compiled from "IA.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.IA
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/IA
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 1
+}
+SourceFile: "IA.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IB.class
+ Compiled from "IB.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.IB
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/IB
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 1
+}
+SourceFile: "IB.java"
## Class: com/supported/UnsupportedClass.class
Compiled from "UnsupportedClass.java"
public class com.supported.UnsupportedClass
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
index b0db48347d46..406cb74bb60c 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
@@ -2055,6 +2055,166 @@ RuntimeVisibleAnnotations:
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
+ Compiled from "C1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "C1.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C2.class
+ Compiled from "C2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "C2.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C3.class
+ Compiled from "C3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C3
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "C3.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CA.class
+ Compiled from "CA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.CA
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CA
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "CA.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CB.class
+ Compiled from "CB.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.CB
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CB
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "CB.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I1.class
+ Compiled from "I1.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I1
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I1
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "I1.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I2.class
+ Compiled from "I2.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I2 extends com.android.hoststubgen.test.tinyframework.subclasstest.I1
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I2
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "I2.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I3.class
+ Compiled from "I3.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I3 extends com.android.hoststubgen.test.tinyframework.subclasstest.I2
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I3
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "I3.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IA.class
+ Compiled from "IA.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.IA
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/IA
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "IA.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IB.class
+ Compiled from "IB.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.IB
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/IB
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "IB.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
## Class: com/unsupported/UnsupportedClass.class
Compiled from "UnsupportedClass.java"
public class com.unsupported.UnsupportedClass
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
index 112f69e43c69..c67326289477 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
@@ -3371,6 +3371,264 @@ RuntimeVisibleAnnotations:
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
+ Compiled from "C1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "C1.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C2.class
+ Compiled from "C2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "C2.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C3.class
+ Compiled from "C3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C3
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "C3.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CA.class
+ Compiled from "CA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.CA
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CA
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "CA.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CB.class
+ Compiled from "CB.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.CB
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CB
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "CB.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.class
+ Compiled from "Class_C1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C1 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "Class_C1.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.class
+ Compiled from "Class_C2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "Class_C2.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.class
+ Compiled from "Class_C3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C3
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C3
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "Class_C3.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.class
+ Compiled from "Class_I1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1 implements com.android.hoststubgen.test.tinyframework.subclasstest.I1
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "Class_I1.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.class
+ Compiled from "Class_I1_IA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1_IA implements com.android.hoststubgen.test.tinyframework.subclasstest.I1,com.android.hoststubgen.test.tinyframework.subclasstest.IA
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA
+ super_class: #x // java/lang/Object
+ interfaces: 2, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "Class_I1_IA.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.class
+ Compiled from "Class_I2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I2 implements com.android.hoststubgen.test.tinyframework.subclasstest.I2
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "Class_I2.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.class
+ Compiled from "Class_I3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3 implements com.android.hoststubgen.test.tinyframework.subclasstest.I3
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "Class_I3.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I1.class
+ Compiled from "I1.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I1
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I1
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "I1.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I2.class
+ Compiled from "I2.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I2 extends com.android.hoststubgen.test.tinyframework.subclasstest.I1
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I2
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "I2.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I3.class
+ Compiled from "I3.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I3 extends com.android.hoststubgen.test.tinyframework.subclasstest.I2
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I3
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "I3.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IA.class
+ Compiled from "IA.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.IA
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/IA
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "IA.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IB.class
+ Compiled from "IB.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.IB
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/IB
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "IB.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
## Class: com/supported/UnsupportedClass.class
Compiled from "UnsupportedClass.java"
public class com.supported.UnsupportedClass
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
index b0db48347d46..406cb74bb60c 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
@@ -2055,6 +2055,166 @@ RuntimeVisibleAnnotations:
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
+ Compiled from "C1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "C1.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C2.class
+ Compiled from "C2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "C2.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C3.class
+ Compiled from "C3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C3
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "C3.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CA.class
+ Compiled from "CA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.CA
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CA
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "CA.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CB.class
+ Compiled from "CB.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.CB
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CB
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "CB.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I1.class
+ Compiled from "I1.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I1
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I1
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "I1.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I2.class
+ Compiled from "I2.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I2 extends com.android.hoststubgen.test.tinyframework.subclasstest.I1
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I2
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "I2.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I3.class
+ Compiled from "I3.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I3 extends com.android.hoststubgen.test.tinyframework.subclasstest.I2
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I3
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "I3.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IA.class
+ Compiled from "IA.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.IA
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/IA
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "IA.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IB.class
+ Compiled from "IB.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.IB
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/IB
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "IB.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
## Class: com/unsupported/UnsupportedClass.class
Compiled from "UnsupportedClass.java"
public class com.unsupported.UnsupportedClass
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
index 2357844c1e10..4fd570157071 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
@@ -4201,6 +4201,417 @@ RuntimeVisibleAnnotations:
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
+ Compiled from "C1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/C1
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "C1.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C2.class
+ Compiled from "C2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+ interfaces: 0, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/C2
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "C2.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C3.class
+ Compiled from "C3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C3
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+ interfaces: 0, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/C3
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "C3.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CA.class
+ Compiled from "CA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.CA
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CA
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/CA
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "CA.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CB.class
+ Compiled from "CB.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.CB
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CB
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/CB
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "CB.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.class
+ Compiled from "Class_C1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C1 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+ interfaces: 0, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "Class_C1.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.class
+ Compiled from "Class_C2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+ interfaces: 0, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "Class_C2.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.class
+ Compiled from "Class_C3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C3
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C3
+ interfaces: 0, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "Class_C3.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.class
+ Compiled from "Class_I1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1 implements com.android.hoststubgen.test.tinyframework.subclasstest.I1
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "Class_I1.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.class
+ Compiled from "Class_I1_IA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1_IA implements com.android.hoststubgen.test.tinyframework.subclasstest.I1,com.android.hoststubgen.test.tinyframework.subclasstest.IA
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA
+ super_class: #x // java/lang/Object
+ interfaces: 2, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "Class_I1_IA.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.class
+ Compiled from "Class_I2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I2 implements com.android.hoststubgen.test.tinyframework.subclasstest.I2
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "Class_I2.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.class
+ Compiled from "Class_I3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3 implements com.android.hoststubgen.test.tinyframework.subclasstest.I3
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "Class_I3.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I1.class
+ Compiled from "I1.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I1
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I1
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/I1
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "I1.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I2.class
+ Compiled from "I2.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I2 extends com.android.hoststubgen.test.tinyframework.subclasstest.I1
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I2
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/I2
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "I2.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I3.class
+ Compiled from "I3.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I3 extends com.android.hoststubgen.test.tinyframework.subclasstest.I2
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I3
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/I3
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "I3.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IA.class
+ Compiled from "IA.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.IA
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/IA
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/IA
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "IA.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IB.class
+ Compiled from "IB.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.IB
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/IB
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/IB
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "IB.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
## Class: com/supported/UnsupportedClass.class
Compiled from "UnsupportedClass.java"
public class com.supported.UnsupportedClass
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
index 9b6b6e4c9f5b..d30208452a40 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
@@ -17,4 +17,23 @@ class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy stub
class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy ~com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
# Heuristics rule: Stub all the AIDL classes.
-class :aidl stubclass \ No newline at end of file
+class :aidl stubclass
+
+# Default is "remove", so let's put all the base classes / interfaces in the stub first.
+class com.android.hoststubgen.test.tinyframework.subclasstest.C1 stub
+class com.android.hoststubgen.test.tinyframework.subclasstest.C2 stub
+class com.android.hoststubgen.test.tinyframework.subclasstest.C3 stub
+class com.android.hoststubgen.test.tinyframework.subclasstest.CA stub
+class com.android.hoststubgen.test.tinyframework.subclasstest.CB stub
+class com.android.hoststubgen.test.tinyframework.subclasstest.I1 stub
+class com.android.hoststubgen.test.tinyframework.subclasstest.I2 stub
+class com.android.hoststubgen.test.tinyframework.subclasstest.I3 stub
+class com.android.hoststubgen.test.tinyframework.subclasstest.IA stub
+class com.android.hoststubgen.test.tinyframework.subclasstest.IB stub
+
+# Then define inheritance based policies.
+class *com.android.hoststubgen.test.tinyframework.subclasstest.C1 keep
+class *com.android.hoststubgen.test.tinyframework.subclasstest.CA remove
+
+class *com.android.hoststubgen.test.tinyframework.subclasstest.I1 keep
+class *com.android.hoststubgen.test.tinyframework.subclasstest.IA remove
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C1.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C1.java
new file mode 100644
index 000000000000..03c9e2a7b5bb
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C1.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class C1 {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C2.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C2.java
new file mode 100644
index 000000000000..3ca8f1f172bd
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C2.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class C2 extends C1 {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C3.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C3.java
new file mode 100644
index 000000000000..a6c14f09b680
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C3.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class C3 extends C2 {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CA.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CA.java
new file mode 100644
index 000000000000..2e353709fd1e
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CA.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class CA {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CB.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CB.java
new file mode 100644
index 000000000000..fe4cee64b2f9
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CB.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class CB {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.java
new file mode 100644
index 000000000000..12012fcdf6ca
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class Class_C1 extends C1 {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.java
new file mode 100644
index 000000000000..8d48ee6f6858
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class Class_C2 extends C2 {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.java
new file mode 100644
index 000000000000..6748430a8e6f
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class Class_C3 extends C3 {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.java
new file mode 100644
index 000000000000..58aa5c3b74eb
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class Class_CA extends CA {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.java
new file mode 100644
index 000000000000..c1c3d624b126
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class Class_CB extends CB {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.java
new file mode 100644
index 000000000000..398b56975f1c
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class Class_CB_IA extends CB implements IA {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.java
new file mode 100644
index 000000000000..44cbd8f9bffa
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class Class_I1 implements I1 {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.java
new file mode 100644
index 000000000000..42355a34b65c
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class Class_I1_IA implements I1, IA {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.java
new file mode 100644
index 000000000000..09c80992e450
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class Class_I2 implements I2 {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.java
new file mode 100644
index 000000000000..0806a478d756
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class Class_I3 implements I3 {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.java
new file mode 100644
index 000000000000..eaa8528a3f31
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class Class_I3_IA implements I3, IA {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.java
new file mode 100644
index 000000000000..778c5aaf27f0
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class Class_IA implements IA {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.java
new file mode 100644
index 000000000000..493f7c83c0f0
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class Class_IA_I1 implements IA, I1 {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.java
new file mode 100644
index 000000000000..2aa1de18b7f4
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class Class_IA_I3 implements IA, I3 {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.java
new file mode 100644
index 000000000000..d9eae0934c42
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class Class_IB implements IB {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.java
new file mode 100644
index 000000000000..9ee42836ac9a
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class Class_IB_IA implements IB, IA {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.java
new file mode 100644
index 000000000000..50ec2cbc9c6e
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class Class_None {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I1.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I1.java
new file mode 100644
index 000000000000..3f3659644a80
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I1.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.test.tinyframework.subclasstest;
+
+public interface I1 {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I2.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I2.java
new file mode 100644
index 000000000000..960060c8a036
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I2.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.test.tinyframework.subclasstest;
+
+public interface I2 extends I1 {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I3.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I3.java
new file mode 100644
index 000000000000..c678eaa789b0
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I3.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.test.tinyframework.subclasstest;
+
+public interface I3 extends I2 {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IA.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IA.java
new file mode 100644
index 000000000000..1cff484c3cd8
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IA.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.test.tinyframework.subclasstest;
+
+public interface IA {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IB.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IB.java
new file mode 100644
index 000000000000..84e7173c71b8
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IB.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 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.hoststubgen.test.tinyframework.subclasstest;
+
+public interface IB {
+}
diff --git a/tools/hoststubgen/scripts/run-all-tests.sh b/tools/hoststubgen/scripts/run-all-tests.sh
deleted file mode 100755
index a6847ae97bae..000000000000
--- a/tools/hoststubgen/scripts/run-all-tests.sh
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/bin/bash
-# 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.
-
-source "${0%/*}"/../common.sh
-
-# Move to the top directory of hoststubgen
-cd ..
-
-ATEST_ARGS="--host"
-
-# These tests are known to pass.
-READY_TEST_MODULES=(
- hoststubgen-test-tiny-test
- CtsUtilTestCasesRavenwood
- CtsOsTestCasesRavenwood # This one uses native sustitution, so let's run it too.
-)
-
-MUST_BUILD_MODULES=(
- "${NOT_READY_TEST_MODULES[*]}"
-)
-
-# First, build all the test / etc modules. This shouldn't fail.
-run m "${MUST_BUILD_MODULES[@]}"
-
-# Run the hoststubgen unittests / etc
-run atest $ATEST_ARGS hoststubgentest hoststubgen-invoke-test
-
-# Next, run the golden check. This should always pass too.
-# The following scripts _should_ pass too, but they depend on the internal paths to soong generated
-# files, and they may fail when something changes in the build system.
-run ./hoststubgen/test-tiny-framework/diff-and-update-golden.sh
-
-run ./hoststubgen/test-tiny-framework/run-test-manually.sh
-run atest $ATEST_ARGS tiny-framework-dump-test
-
-# This script is already broken on goog/master
-# run ./scripts/build-framework-hostside-jars-without-genrules.sh
-
-# These tests should all pass.
-run atest $ATEST_ARGS ${READY_TEST_MODULES[*]}
-
-echo ""${0##*/}" finished, with no failures. Ready to submit!"
diff --git a/tools/protologtool/Android.bp b/tools/protologtool/Android.bp
index 46745e995f64..8fbc3e8a78db 100644
--- a/tools/protologtool/Android.bp
+++ b/tools/protologtool/Android.bp
@@ -11,12 +11,13 @@ java_library_host {
name: "protologtool-lib",
srcs: [
"src/com/android/protolog/tool/**/*.kt",
- ":protolog-common-no-android-src",
+ ":protolog-common-src",
],
static_libs: [
"javaparser",
"platformprotos",
"jsonlib",
+ "perfetto_trace-full",
],
}
@@ -42,5 +43,6 @@ java_test_host {
"junit",
"mockito",
"objenesis",
+ "truth",
],
}
diff --git a/tools/protologtool/README.md b/tools/protologtool/README.md
index ba639570f88c..24a4861c63a2 100644
--- a/tools/protologtool/README.md
+++ b/tools/protologtool/README.md
@@ -8,11 +8,13 @@ ProtoLogTool incorporates three different modes of operation:
### Code transformation
-Command: `protologtool transform-protolog-calls
- --protolog-class <protolog class name>
- --protolog-impl-class <protolog implementation class name>
+Command: `protologtool transform-protolog-calls
+ --protolog-class <protolog class name>
--loggroups-class <protolog groups class name>
--loggroups-jar <config jar path>
+ --viewer-config-file-path <protobuf viewer config file path>
+ --legacy-viewer-config-file-path <legacy json.gz viewer config file path>
+ --legacy-output-file-path <.winscope file path to write the legacy trace to>
--output-srcjar <output.srcjar>
[<input.java>]`
@@ -44,10 +46,11 @@ jar file (config.jar).
### Viewer config generation
Command: `generate-viewer-config
- --protolog-class <protolog class name>
+ --protolog-class <protolog class name>
--loggroups-class <protolog groups class name>
--loggroups-jar <config jar path>
- --viewer-conf <viewer.json>
+ --viewer-config-type <proto|json>
+ --viewer-config <viewer.json>
[<input.java>]`
This command is similar in it's syntax to the previous one, only instead of creating a processed source jar
@@ -74,7 +77,7 @@ it writes a viewer configuration file with following schema:
### Binary log viewing
-Command: `read-log --viewer-conf <viewer.json> <wm_log.pb>`
+Command: `read-log --viewer-config <viewer.json> <wm_log.pb>`
Reads the binary ProtoLog log file and outputs a human-readable LogCat-like text log.
diff --git a/tools/protologtool/src/com/android/protolog/tool/CodeUtils.kt b/tools/protologtool/src/com/android/protolog/tool/CodeUtils.kt
index 451e514b8b33..3d1dec2e0724 100644
--- a/tools/protologtool/src/com/android/protolog/tool/CodeUtils.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/CodeUtils.kt
@@ -16,20 +16,27 @@
package com.android.protolog.tool
+import com.android.internal.protolog.common.LogLevel
import com.github.javaparser.ast.CompilationUnit
import com.github.javaparser.ast.ImportDeclaration
import com.github.javaparser.ast.expr.BinaryExpr
import com.github.javaparser.ast.expr.Expression
import com.github.javaparser.ast.expr.StringLiteralExpr
+import java.util.UUID
object CodeUtils {
/**
* Returns a stable hash of a string.
* We reimplement String::hashCode() for readability reasons.
*/
- fun hash(position: String, messageString: String, logLevel: LogLevel, logGroup: LogGroup): Int {
- return (position + messageString + logLevel.name + logGroup.name)
- .map { c -> c.code }.reduce { h, c -> h * 31 + c }
+ fun hash(
+ position: String,
+ messageString: String,
+ logLevel: LogLevel,
+ logGroup: LogGroup
+ ): Long {
+ val fullStringIdentifier = position + messageString + logLevel.name + logGroup.name
+ return UUID.nameUUIDFromBytes(fullStringIdentifier.toByteArray()).mostSignificantBits
}
fun checkWildcardStaticImported(code: CompilationUnit, className: String, fileName: String) {
diff --git a/tools/protologtool/src/com/android/protolog/tool/CommandOptions.kt b/tools/protologtool/src/com/android/protolog/tool/CommandOptions.kt
index bfbbf7a32c22..a3591558bd67 100644
--- a/tools/protologtool/src/com/android/protolog/tool/CommandOptions.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/CommandOptions.kt
@@ -26,32 +26,35 @@ class CommandOptions(args: Array<String>) {
private val commands = setOf(TRANSFORM_CALLS_CMD, GENERATE_CONFIG_CMD, READ_LOG_CMD)
private const val PROTOLOG_CLASS_PARAM = "--protolog-class"
- private const val PROTOLOGIMPL_CLASS_PARAM = "--protolog-impl-class"
- private const val PROTOLOGCACHE_CLASS_PARAM = "--protolog-cache-class"
private const val PROTOLOGGROUP_CLASS_PARAM = "--loggroups-class"
private const val PROTOLOGGROUP_JAR_PARAM = "--loggroups-jar"
- private const val VIEWER_CONFIG_JSON_PARAM = "--viewer-conf"
+ private const val VIEWER_CONFIG_PARAM = "--viewer-config"
+ private const val VIEWER_CONFIG_TYPE_PARAM = "--viewer-config-type"
private const val OUTPUT_SOURCE_JAR_PARAM = "--output-srcjar"
- private val parameters = setOf(PROTOLOG_CLASS_PARAM, PROTOLOGIMPL_CLASS_PARAM,
- PROTOLOGCACHE_CLASS_PARAM, PROTOLOGGROUP_CLASS_PARAM, PROTOLOGGROUP_JAR_PARAM,
- VIEWER_CONFIG_JSON_PARAM, OUTPUT_SOURCE_JAR_PARAM)
+ private const val VIEWER_CONFIG_FILE_PATH_PARAM = "--viewer-config-file-path"
+ // TODO(b/324128613): Remove these legacy options once we fully flip the Perfetto protolog flag
+ private const val LEGACY_VIEWER_CONFIG_FILE_PATH_PARAM = "--legacy-viewer-config-file-path"
+ private const val LEGACY_OUTPUT_FILE_PATH = "--legacy-output-file-path"
+ private val parameters = setOf(PROTOLOG_CLASS_PARAM, PROTOLOGGROUP_CLASS_PARAM,
+ PROTOLOGGROUP_JAR_PARAM, VIEWER_CONFIG_PARAM, VIEWER_CONFIG_TYPE_PARAM,
+ OUTPUT_SOURCE_JAR_PARAM, VIEWER_CONFIG_FILE_PATH_PARAM,
+ LEGACY_VIEWER_CONFIG_FILE_PATH_PARAM, LEGACY_OUTPUT_FILE_PATH)
val USAGE = """
Usage: ${Constants.NAME} <command> [<args>]
Available commands:
- $TRANSFORM_CALLS_CMD $PROTOLOG_CLASS_PARAM <class name> $PROTOLOGIMPL_CLASS_PARAM
- <class name> $PROTOLOGCACHE_CLASS_PARAM
- <class name> $PROTOLOGGROUP_CLASS_PARAM <class name> $PROTOLOGGROUP_JAR_PARAM
- <config.jar> $OUTPUT_SOURCE_JAR_PARAM <output.srcjar> [<input.java>]
+ $TRANSFORM_CALLS_CMD $PROTOLOG_CLASS_PARAM <class name>
+ $PROTOLOGGROUP_CLASS_PARAM <class name> $PROTOLOGGROUP_JAR_PARAM <config.jar>
+ $OUTPUT_SOURCE_JAR_PARAM <output.srcjar> [<input.java>]
- processes java files replacing stub calls with logging code.
- $GENERATE_CONFIG_CMD $PROTOLOG_CLASS_PARAM <class name> $PROTOLOGGROUP_CLASS_PARAM
- <class name> $PROTOLOGGROUP_JAR_PARAM <config.jar> $VIEWER_CONFIG_JSON_PARAM
- <viewer.json> [<input.java>]
+ $GENERATE_CONFIG_CMD $PROTOLOG_CLASS_PARAM <class name>
+ $PROTOLOGGROUP_CLASS_PARAM <class name> $PROTOLOGGROUP_JAR_PARAM <config.jar>
+ $VIEWER_CONFIG_PARAM <viewer.json|viewer.pb> [<input.java>]
- creates viewer config file from given java files.
- $READ_LOG_CMD $VIEWER_CONFIG_JSON_PARAM <viewer.json> <wm_log.pb>
+ $READ_LOG_CMD $VIEWER_CONFIG_PARAM <viewer.json|viewer.pb> <wm_log.pb>
- translates a binary log to a readable format.
""".trimIndent()
@@ -69,6 +72,13 @@ class CommandOptions(args: Array<String>) {
return params.getValue(paramName)
}
+ private fun getOptionalParam(paramName: String, params: Map<String, String>): String? {
+ if (!params.containsKey(paramName)) {
+ return null
+ }
+ return params.getValue(paramName)
+ }
+
private fun validateNotSpecified(paramName: String, params: Map<String, String>): String {
if (params.containsKey(paramName)) {
throw InvalidCommandException("Unsupported param $paramName")
@@ -90,9 +100,43 @@ class CommandOptions(args: Array<String>) {
return name
}
- private fun validateJSONName(name: String): String {
- if (!name.endsWith(".json")) {
- throw InvalidCommandException("Json file required, got $name instead")
+ private fun validateViewerConfigFilePath(name: String): String {
+ if (!name.endsWith(".pb")) {
+ throw InvalidCommandException("Proto file (ending with .pb) required, " +
+ "got $name instead")
+ }
+ return name
+ }
+
+ private fun validateLegacyViewerConfigFilePath(name: String): String {
+ if (!name.endsWith(".json.gz")) {
+ throw InvalidCommandException("GZiped Json file (ending with .json.gz) required, " +
+ "got $name instead")
+ }
+ return name
+ }
+
+ private fun validateOutputFilePath(name: String): String {
+ if (!name.endsWith(".winscope")) {
+ throw InvalidCommandException("Winscope file (ending with .winscope) required, " +
+ "got $name instead")
+ }
+ return name
+ }
+
+ private fun validateConfigFileName(name: String): String {
+ if (!name.endsWith(".json") && !name.endsWith(".pb")) {
+ throw InvalidCommandException("Json file (ending with .json) or proto file " +
+ "(ending with .pb) required, got $name instead")
+ }
+ return name
+ }
+
+ private fun validateConfigType(name: String): String {
+ val validType = listOf("json", "proto")
+ if (!validType.contains(name)) {
+ throw InvalidCommandException("Unexpected config file type. " +
+ "Expected on of [${validType.joinToString()}], but got $name")
}
return name
}
@@ -102,8 +146,8 @@ class CommandOptions(args: Array<String>) {
throw InvalidCommandException("No java source input files")
}
list.forEach { name ->
- if (!name.endsWith(".java")) {
- throw InvalidCommandException("Not a java source file $name")
+ if (!name.endsWith(".java") && !name.endsWith(".kt")) {
+ throw InvalidCommandException("Not a java or kotlin source file $name")
}
}
return list
@@ -122,12 +166,14 @@ class CommandOptions(args: Array<String>) {
val protoLogClassNameArg: String
val protoLogGroupsClassNameArg: String
- val protoLogImplClassNameArg: String
- val protoLogCacheClassNameArg: String
val protoLogGroupsJarArg: String
- val viewerConfigJsonArg: String
+ val viewerConfigFileNameArg: String
+ val viewerConfigTypeArg: String
val outputSourceJarArg: String
val logProtofileArg: String
+ val viewerConfigFilePathArg: String
+ val legacyViewerConfigFilePathArg: String?
+ val legacyOutputFilePath: String?
val javaSourceArgs: List<String>
val command: String
@@ -169,38 +215,55 @@ class CommandOptions(args: Array<String>) {
when (command) {
TRANSFORM_CALLS_CMD -> {
protoLogClassNameArg = validateClassName(getParam(PROTOLOG_CLASS_PARAM, params))
- protoLogGroupsClassNameArg = validateClassName(getParam(PROTOLOGGROUP_CLASS_PARAM,
- params))
- protoLogImplClassNameArg = validateClassName(getParam(PROTOLOGIMPL_CLASS_PARAM,
- params))
- protoLogCacheClassNameArg = validateClassName(getParam(PROTOLOGCACHE_CLASS_PARAM,
- params))
+ protoLogGroupsClassNameArg =
+ validateClassName(getParam(PROTOLOGGROUP_CLASS_PARAM, params))
protoLogGroupsJarArg = validateJarName(getParam(PROTOLOGGROUP_JAR_PARAM, params))
- viewerConfigJsonArg = validateNotSpecified(VIEWER_CONFIG_JSON_PARAM, params)
+ viewerConfigFileNameArg = validateNotSpecified(VIEWER_CONFIG_PARAM, params)
+ viewerConfigTypeArg = validateNotSpecified(VIEWER_CONFIG_TYPE_PARAM, params)
outputSourceJarArg = validateSrcJarName(getParam(OUTPUT_SOURCE_JAR_PARAM, params))
+ viewerConfigFilePathArg = validateViewerConfigFilePath(
+ getParam(VIEWER_CONFIG_FILE_PATH_PARAM, params))
+ legacyViewerConfigFilePathArg =
+ getOptionalParam(LEGACY_VIEWER_CONFIG_FILE_PATH_PARAM, params)?.let {
+ validateLegacyViewerConfigFilePath(it)
+ }
+ legacyOutputFilePath =
+ getOptionalParam(LEGACY_OUTPUT_FILE_PATH, params)?.let {
+ validateOutputFilePath(it)
+ }
javaSourceArgs = validateJavaInputList(inputFiles)
logProtofileArg = ""
}
GENERATE_CONFIG_CMD -> {
protoLogClassNameArg = validateClassName(getParam(PROTOLOG_CLASS_PARAM, params))
- protoLogGroupsClassNameArg = validateClassName(getParam(PROTOLOGGROUP_CLASS_PARAM,
- params))
- protoLogImplClassNameArg = validateNotSpecified(PROTOLOGIMPL_CLASS_PARAM, params)
- protoLogCacheClassNameArg = validateNotSpecified(PROTOLOGCACHE_CLASS_PARAM, params)
+ protoLogGroupsClassNameArg =
+ validateClassName(getParam(PROTOLOGGROUP_CLASS_PARAM, params))
protoLogGroupsJarArg = validateJarName(getParam(PROTOLOGGROUP_JAR_PARAM, params))
- viewerConfigJsonArg = validateJSONName(getParam(VIEWER_CONFIG_JSON_PARAM, params))
+ viewerConfigFileNameArg =
+ validateConfigFileName(getParam(VIEWER_CONFIG_PARAM, params))
+ viewerConfigTypeArg = validateConfigType(getParam(VIEWER_CONFIG_TYPE_PARAM, params))
outputSourceJarArg = validateNotSpecified(OUTPUT_SOURCE_JAR_PARAM, params)
+ viewerConfigFilePathArg =
+ validateNotSpecified(VIEWER_CONFIG_FILE_PATH_PARAM, params)
+ legacyViewerConfigFilePathArg =
+ validateNotSpecified(LEGACY_VIEWER_CONFIG_FILE_PATH_PARAM, params)
+ legacyOutputFilePath = validateNotSpecified(LEGACY_OUTPUT_FILE_PATH, params)
javaSourceArgs = validateJavaInputList(inputFiles)
logProtofileArg = ""
}
READ_LOG_CMD -> {
protoLogClassNameArg = validateNotSpecified(PROTOLOG_CLASS_PARAM, params)
protoLogGroupsClassNameArg = validateNotSpecified(PROTOLOGGROUP_CLASS_PARAM, params)
- protoLogImplClassNameArg = validateNotSpecified(PROTOLOGIMPL_CLASS_PARAM, params)
- protoLogCacheClassNameArg = validateNotSpecified(PROTOLOGCACHE_CLASS_PARAM, params)
protoLogGroupsJarArg = validateNotSpecified(PROTOLOGGROUP_JAR_PARAM, params)
- viewerConfigJsonArg = validateJSONName(getParam(VIEWER_CONFIG_JSON_PARAM, params))
+ viewerConfigFileNameArg =
+ validateConfigFileName(getParam(VIEWER_CONFIG_PARAM, params))
+ viewerConfigTypeArg = validateNotSpecified(VIEWER_CONFIG_TYPE_PARAM, params)
outputSourceJarArg = validateNotSpecified(OUTPUT_SOURCE_JAR_PARAM, params)
+ viewerConfigFilePathArg =
+ validateNotSpecified(VIEWER_CONFIG_FILE_PATH_PARAM, params)
+ legacyViewerConfigFilePathArg =
+ validateNotSpecified(LEGACY_VIEWER_CONFIG_FILE_PATH_PARAM, params)
+ legacyOutputFilePath = validateNotSpecified(LEGACY_OUTPUT_FILE_PATH, params)
javaSourceArgs = listOf()
logProtofileArg = validateLogInputList(inputFiles)
}
diff --git a/tools/protologtool/src/com/android/protolog/tool/LogLevel.kt b/tools/protologtool/src/com/android/protolog/tool/LogLevel.kt
deleted file mode 100644
index e88f0f8231bd..000000000000
--- a/tools/protologtool/src/com/android/protolog/tool/LogLevel.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.protolog.tool
-
-import com.github.javaparser.ast.Node
-
-enum class LogLevel {
- DEBUG, VERBOSE, INFO, WARN, ERROR, WTF;
-
- companion object {
- fun getLevelForMethodName(name: String, node: Node, context: ParsingContext): LogLevel {
- return when (name) {
- "d" -> DEBUG
- "v" -> VERBOSE
- "i" -> INFO
- "w" -> WARN
- "e" -> ERROR
- "wtf" -> WTF
- else ->
- throw InvalidProtoLogCallException("Unknown log level $name in $node", context)
- }
- }
- }
-}
diff --git a/tools/protologtool/src/com/android/protolog/tool/MethodCallVisitor.kt b/tools/protologtool/src/com/android/protolog/tool/MethodCallVisitor.kt
new file mode 100644
index 000000000000..fda6351361bf
--- /dev/null
+++ b/tools/protologtool/src/com/android/protolog/tool/MethodCallVisitor.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 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.protolog.tool
+
+import com.github.javaparser.ast.expr.MethodCallExpr
+
+interface MethodCallVisitor {
+ fun processCall(call: MethodCallExpr)
+}
diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessor.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessor.kt
index 2181cf680f6c..47724b7a9e1d 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessor.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessor.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -17,97 +17,12 @@
package com.android.protolog.tool
import com.github.javaparser.ast.CompilationUnit
-import com.github.javaparser.ast.expr.Expression
-import com.github.javaparser.ast.expr.FieldAccessExpr
-import com.github.javaparser.ast.expr.MethodCallExpr
-import com.github.javaparser.ast.expr.NameExpr
-/**
- * Helper class for visiting all ProtoLog calls.
- * For every valid call in the given {@code CompilationUnit} a {@code ProtoLogCallVisitor} callback
- * is executed.
- */
-open class ProtoLogCallProcessor(
- private val protoLogClassName: String,
- private val protoLogGroupClassName: String,
- private val groupMap: Map<String, LogGroup>
-) {
- private val protoLogSimpleClassName = protoLogClassName.substringAfterLast('.')
- private val protoLogGroupSimpleClassName = protoLogGroupClassName.substringAfterLast('.')
-
- private fun getLogGroupName(
- expr: Expression,
- isClassImported: Boolean,
- staticImports: Set<String>,
+interface ProtoLogCallProcessor {
+ fun process(
+ code: CompilationUnit,
+ logCallVisitor: ProtoLogCallVisitor?,
+ otherCallVisitor: MethodCallVisitor?,
fileName: String
- ): String {
- val context = ParsingContext(fileName, expr)
- return when (expr) {
- is NameExpr -> when {
- expr.nameAsString in staticImports -> expr.nameAsString
- else ->
- throw InvalidProtoLogCallException("Unknown/not imported ProtoLogGroup: $expr",
- context)
- }
- is FieldAccessExpr -> when {
- expr.scope.toString() == protoLogGroupClassName
- || isClassImported &&
- expr.scope.toString() == protoLogGroupSimpleClassName -> expr.nameAsString
- else ->
- throw InvalidProtoLogCallException("Unknown/not imported ProtoLogGroup: $expr",
- context)
- }
- else -> throw InvalidProtoLogCallException("Invalid group argument " +
- "- must be ProtoLogGroup enum member reference: $expr", context)
- }
- }
-
- private fun isProtoCall(
- call: MethodCallExpr,
- isLogClassImported: Boolean,
- staticLogImports: Collection<String>
- ): Boolean {
- return call.scope.isPresent && call.scope.get().toString() == protoLogClassName ||
- isLogClassImported && call.scope.isPresent &&
- call.scope.get().toString() == protoLogSimpleClassName ||
- !call.scope.isPresent && staticLogImports.contains(call.name.toString())
- }
-
- open fun process(code: CompilationUnit, callVisitor: ProtoLogCallVisitor?, fileName: String):
- CompilationUnit {
- CodeUtils.checkWildcardStaticImported(code, protoLogClassName, fileName)
- CodeUtils.checkWildcardStaticImported(code, protoLogGroupClassName, fileName)
-
- val isLogClassImported = CodeUtils.isClassImportedOrSamePackage(code, protoLogClassName)
- val staticLogImports = CodeUtils.staticallyImportedMethods(code, protoLogClassName)
- val isGroupClassImported = CodeUtils.isClassImportedOrSamePackage(code,
- protoLogGroupClassName)
- val staticGroupImports = CodeUtils.staticallyImportedMethods(code, protoLogGroupClassName)
-
- code.findAll(MethodCallExpr::class.java)
- .filter { call ->
- isProtoCall(call, isLogClassImported, staticLogImports)
- }.forEach { call ->
- val context = ParsingContext(fileName, call)
- if (call.arguments.size < 2) {
- throw InvalidProtoLogCallException("Method signature does not match " +
- "any ProtoLog method: $call", context)
- }
-
- val messageString = CodeUtils.concatMultilineString(call.getArgument(1),
- context)
- val groupNameArg = call.getArgument(0)
- val groupName =
- getLogGroupName(groupNameArg, isGroupClassImported,
- staticGroupImports, fileName)
- if (groupName !in groupMap) {
- throw InvalidProtoLogCallException("Unknown group argument " +
- "- not a ProtoLogGroup enum member: $call", context)
- }
-
- callVisitor?.processCall(call, messageString, LogLevel.getLevelForMethodName(
- call.name.toString(), call, context), groupMap.getValue(groupName))
- }
- return code
- }
+ ): CompilationUnit
}
diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt
new file mode 100644
index 000000000000..1087ae6ee41d
--- /dev/null
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.protolog.tool
+
+import com.android.internal.protolog.common.LogLevel
+import com.github.javaparser.ast.CompilationUnit
+import com.github.javaparser.ast.expr.Expression
+import com.github.javaparser.ast.expr.FieldAccessExpr
+import com.github.javaparser.ast.expr.MethodCallExpr
+import com.github.javaparser.ast.expr.NameExpr
+
+/**
+ * Helper class for visiting all ProtoLog calls.
+ * For every valid call in the given {@code CompilationUnit} a {@code ProtoLogCallVisitor} callback
+ * is executed.
+ */
+class ProtoLogCallProcessorImpl(
+ private val protoLogClassName: String,
+ private val protoLogGroupClassName: String,
+ private val groupMap: Map<String, LogGroup>
+) : ProtoLogCallProcessor {
+ private val protoLogSimpleClassName = protoLogClassName.substringAfterLast('.')
+ private val protoLogGroupSimpleClassName = protoLogGroupClassName.substringAfterLast('.')
+
+ private fun getLogGroupName(
+ expr: Expression,
+ isClassImported: Boolean,
+ staticImports: Set<String>,
+ fileName: String
+ ): String {
+ val context = ParsingContext(fileName, expr)
+ return when (expr) {
+ is NameExpr -> when {
+ expr.nameAsString in staticImports -> expr.nameAsString
+ else ->
+ throw InvalidProtoLogCallException("Unknown/not imported ProtoLogGroup: $expr",
+ context)
+ }
+ is FieldAccessExpr -> when {
+ expr.scope.toString() == protoLogGroupClassName || isClassImported &&
+ expr.scope.toString() == protoLogGroupSimpleClassName -> expr.nameAsString
+ else ->
+ throw InvalidProtoLogCallException("Unknown/not imported ProtoLogGroup: $expr",
+ context)
+ }
+ else -> throw InvalidProtoLogCallException("Invalid group argument " +
+ "- must be ProtoLogGroup enum member reference: $expr", context)
+ }
+ }
+
+ private fun isProtoCall(
+ call: MethodCallExpr,
+ isLogClassImported: Boolean,
+ staticLogImports: Collection<String>
+ ): Boolean {
+ return call.scope.isPresent && call.scope.get().toString() == protoLogClassName ||
+ isLogClassImported && call.scope.isPresent &&
+ call.scope.get().toString() == protoLogSimpleClassName ||
+ !call.scope.isPresent && staticLogImports.contains(call.name.toString())
+ }
+
+ fun process(code: CompilationUnit, logCallVisitor: ProtoLogCallVisitor?, fileName: String):
+ CompilationUnit {
+ return process(code, logCallVisitor, null, fileName)
+ }
+
+ override fun process(
+ code: CompilationUnit,
+ logCallVisitor: ProtoLogCallVisitor?,
+ otherCallVisitor: MethodCallVisitor?,
+ fileName: String
+ ): CompilationUnit {
+ CodeUtils.checkWildcardStaticImported(code, protoLogClassName, fileName)
+ CodeUtils.checkWildcardStaticImported(code, protoLogGroupClassName, fileName)
+
+ val isLogClassImported = CodeUtils.isClassImportedOrSamePackage(code, protoLogClassName)
+ val staticLogImports = CodeUtils.staticallyImportedMethods(code, protoLogClassName)
+ val isGroupClassImported = CodeUtils.isClassImportedOrSamePackage(code,
+ protoLogGroupClassName)
+ val staticGroupImports = CodeUtils.staticallyImportedMethods(code, protoLogGroupClassName)
+
+ code.findAll(MethodCallExpr::class.java)
+ .filter { call ->
+ isProtoCall(call, isLogClassImported, staticLogImports)
+ }.forEach { call ->
+ val context = ParsingContext(fileName, call)
+
+ val logMethods = LogLevel.entries.map { it.shortCode }
+ if (logMethods.contains(call.name.id)) {
+ // Process a log call
+ if (call.arguments.size < 2) {
+ throw InvalidProtoLogCallException("Method signature does not match " +
+ "any ProtoLog method: $call", context)
+ }
+
+ val messageString = CodeUtils.concatMultilineString(call.getArgument(1),
+ context)
+ val groupNameArg = call.getArgument(0)
+ val groupName =
+ getLogGroupName(groupNameArg, isGroupClassImported,
+ staticGroupImports, fileName)
+ if (groupName !in groupMap) {
+ throw InvalidProtoLogCallException("Unknown group argument " +
+ "- not a ProtoLogGroup enum member: $call", context)
+ }
+
+ logCallVisitor?.processCall(call, messageString, getLevelForMethodName(
+ call.name.toString(), call, context), groupMap.getValue(groupName))
+ } else {
+ // Process non-log message calls
+ otherCallVisitor?.processCall(call)
+ }
+ }
+ return code
+ }
+
+ private fun getLevelForMethodName(
+ name: String,
+ node: MethodCallExpr,
+ context: ParsingContext
+ ): LogLevel = when (name) {
+ "d" -> LogLevel.DEBUG
+ "v" -> LogLevel.VERBOSE
+ "i" -> LogLevel.INFO
+ "w" -> LogLevel.WARN
+ "e" -> LogLevel.ERROR
+ "wtf" -> LogLevel.WTF
+ else ->
+ throw InvalidProtoLogCallException("Unknown log level $name in $node", context)
+ }
+}
diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallVisitor.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallVisitor.kt
index aa58b69d61cb..8cd927a7cd0e 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallVisitor.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallVisitor.kt
@@ -16,6 +16,7 @@
package com.android.protolog.tool
+import com.android.internal.protolog.common.LogLevel
import com.github.javaparser.ast.expr.MethodCallExpr
interface ProtoLogCallVisitor {
diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
index 3c55237ce443..1381847c258f 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
@@ -16,22 +16,44 @@
package com.android.protolog.tool
+import com.android.internal.protolog.common.LogLevel
+import com.android.internal.protolog.common.ProtoLog
+import com.android.internal.protolog.common.ProtoLogToolInjected
import com.android.protolog.tool.CommandOptions.Companion.USAGE
import com.github.javaparser.ParseProblemException
import com.github.javaparser.ParserConfiguration
import com.github.javaparser.StaticJavaParser
import com.github.javaparser.ast.CompilationUnit
+import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration
+import com.github.javaparser.ast.expr.MethodCallExpr
+import com.github.javaparser.ast.expr.NullLiteralExpr
+import com.github.javaparser.ast.expr.SimpleName
+import com.github.javaparser.ast.expr.StringLiteralExpr
import java.io.File
import java.io.FileInputStream
+import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.io.OutputStream
+import java.time.LocalDateTime
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.jar.JarOutputStream
import java.util.zip.ZipEntry
+import kotlin.math.abs
+import kotlin.random.Random
import kotlin.system.exitProcess
object ProtoLogTool {
+ const val PROTOLOG_IMPL_SRC_PATH =
+ "frameworks/base/core/java/com/android/internal/protolog/ProtoLogImpl.java"
+
+ data class LogCall(
+ val messageString: String,
+ val logLevel: LogLevel,
+ val logGroup: LogGroup,
+ val position: String
+ )
+
private fun showHelpAndExit() {
println(USAGE)
exitProcess(-1)
@@ -42,27 +64,48 @@ object ProtoLogTool {
return source.contains(protoLogSimpleClassName)
}
+ private fun zipEntry(path: String): ZipEntry {
+ val entry = ZipEntry(path)
+ // Use a constant time to improve the cachability of build actions.
+ entry.timeLocal = LocalDateTime.of(2008, 1, 1, 0, 0, 0)
+ return entry
+ }
+
private fun processClasses(command: CommandOptions) {
+ val generationHash = abs(Random.nextInt())
+ // Need to generate a new impl class to inject static constants into the class.
+ val generatedProtoLogImplClass =
+ "com.android.internal.protolog.ProtoLogImpl_$generationHash"
+
val groups = injector.readLogGroups(
command.protoLogGroupsJarArg,
command.protoLogGroupsClassNameArg)
val out = injector.fileOutputStream(command.outputSourceJarArg)
val outJar = JarOutputStream(out)
- val processor = ProtoLogCallProcessor(command.protoLogClassNameArg,
- command.protoLogGroupsClassNameArg, groups)
+ val processor = ProtoLogCallProcessorImpl(
+ command.protoLogClassNameArg,
+ command.protoLogGroupsClassNameArg,
+ groups)
+
+ val protologImplName = generatedProtoLogImplClass.split(".").last()
+ val protologImplPath = "gen/${generatedProtoLogImplClass.split(".")
+ .joinToString("/")}.java"
+ outJar.putNextEntry(zipEntry(protologImplPath))
+
+ outJar.write(generateProtoLogImpl(protologImplName, command.viewerConfigFilePathArg,
+ command.legacyViewerConfigFilePathArg, command.legacyOutputFilePath).toByteArray())
val executor = newThreadPool()
try {
command.javaSourceArgs.map { path ->
executor.submitCallable {
- val transformer = SourceTransformer(command.protoLogImplClassNameArg,
- command.protoLogCacheClassNameArg, processor)
+ val transformer = SourceTransformer(generatedProtoLogImplClass, processor)
val file = File(path)
val text = injector.readText(file)
val outSrc = try {
val code = tryParse(text, path)
- if (containsProtoLogText(text, command.protoLogClassNameArg)) {
+ if (containsProtoLogText(text, ProtoLog::class.java.simpleName)) {
transformer.processClass(text, path, packagePath(file, code), code)
} else {
text
@@ -77,7 +120,7 @@ object ProtoLogTool {
}
}.map { future ->
val (path, outSrc) = future.get()
- outJar.putNextEntry(ZipEntry(path))
+ outJar.putNextEntry(zipEntry(path))
outJar.write(outSrc.toByteArray())
outJar.closeEntry()
}
@@ -85,51 +128,77 @@ object ProtoLogTool {
executor.shutdown()
}
- val cacheSplit = command.protoLogCacheClassNameArg.split(".")
- val cacheName = cacheSplit.last()
- val cachePackage = cacheSplit.dropLast(1).joinToString(".")
- val cachePath = "gen/${cacheSplit.joinToString("/")}.java"
-
- outJar.putNextEntry(ZipEntry(cachePath))
- outJar.write(generateLogGroupCache(cachePackage, cacheName, groups,
- command.protoLogImplClassNameArg, command.protoLogGroupsClassNameArg).toByteArray())
-
outJar.close()
out.close()
}
- fun generateLogGroupCache(
- cachePackage: String,
- cacheName: String,
- groups: Map<String, LogGroup>,
- protoLogImplClassName: String,
- protoLogGroupsClassName: String
+ private fun generateProtoLogImpl(
+ protoLogImplGenName: String,
+ viewerConfigFilePath: String,
+ legacyViewerConfigFilePath: String?,
+ legacyOutputFilePath: String?,
): String {
- val fields = groups.values.map {
- "public static boolean ${it.name}_enabled = false;"
- }.joinToString("\n")
+ val file = File(PROTOLOG_IMPL_SRC_PATH)
- val updates = groups.values.map {
- "${it.name}_enabled = " +
- "$protoLogImplClassName.isEnabled($protoLogGroupsClassName.${it.name});"
- }.joinToString("\n")
+ val text = try {
+ injector.readText(file)
+ } catch (e: FileNotFoundException) {
+ throw RuntimeException("Expected to find '$PROTOLOG_IMPL_SRC_PATH' but file was not " +
+ "included in source for the ProtoLog Tool to process.")
+ }
- return """
- package $cachePackage;
+ val code = tryParse(text, PROTOLOG_IMPL_SRC_PATH)
- public class $cacheName {
-${fields.replaceIndent(" ")}
+ val classDeclarations = code.findAll(ClassOrInterfaceDeclaration::class.java)
+ require(classDeclarations.size == 1) { "Expected exactly one class declaration" }
+ val classDeclaration = classDeclarations[0]
- static {
- $protoLogImplClassName.sCacheUpdater = $cacheName::update;
- update();
- }
+ val classNameNode = classDeclaration.findFirst(SimpleName::class.java).get()
+ classNameNode.setId(protoLogImplGenName)
- static void update() {
-${updates.replaceIndent(" ")}
- }
- }
- """.trimIndent()
+ injectConstants(classDeclaration,
+ viewerConfigFilePath, legacyViewerConfigFilePath, legacyOutputFilePath)
+
+ return code.toString()
+ }
+
+ private fun injectConstants(
+ classDeclaration: ClassOrInterfaceDeclaration,
+ viewerConfigFilePath: String,
+ legacyViewerConfigFilePath: String?,
+ legacyOutputFilePath: String?
+ ) {
+ classDeclaration.fields.forEach { field ->
+ field.getAnnotationByClass(ProtoLogToolInjected::class.java)
+ .ifPresent { annotationExpr ->
+ if (annotationExpr.isSingleMemberAnnotationExpr) {
+ val valueName = annotationExpr.asSingleMemberAnnotationExpr()
+ .memberValue.asNameExpr().name.asString()
+ when (valueName) {
+ ProtoLogToolInjected.Value.VIEWER_CONFIG_PATH.name -> {
+ field.setFinal(true)
+ field.variables.first()
+ .setInitializer(StringLiteralExpr(viewerConfigFilePath))
+ }
+ ProtoLogToolInjected.Value.LEGACY_OUTPUT_FILE_PATH.name -> {
+ field.setFinal(true)
+ field.variables.first()
+ .setInitializer(legacyOutputFilePath?.let {
+ StringLiteralExpr(it)
+ } ?: NullLiteralExpr())
+ }
+ ProtoLogToolInjected.Value.LEGACY_VIEWER_CONFIG_PATH.name -> {
+ field.setFinal(true)
+ field.variables.first()
+ .setInitializer(legacyViewerConfigFilePath?.let {
+ StringLiteralExpr(it)
+ } ?: NullLiteralExpr())
+ }
+ else -> error("Unhandled ProtoLogToolInjected value: $valueName.")
+ }
+ }
+ }
+ }
}
private fun tryParse(code: String, fileName: String): CompilationUnit {
@@ -137,24 +206,53 @@ ${updates.replaceIndent(" ")}
return StaticJavaParser.parse(code)
} catch (ex: ParseProblemException) {
val problem = ex.problems.first()
- throw ParsingException("Java parsing erro" +
- "r: ${problem.verboseMessage}",
+ throw ParsingException("Java parsing error: ${problem.verboseMessage}",
ParsingContext(fileName, problem.location.orElse(null)
?.begin?.range?.orElse(null)?.begin?.line
?: 0))
}
}
+ class LogCallRegistry {
+ private val statements = mutableMapOf<LogCall, Long>()
+
+ fun addLogCalls(calls: List<LogCall>) {
+ calls.forEach { logCall ->
+ if (logCall.logGroup.enabled) {
+ statements.putIfAbsent(logCall,
+ CodeUtils.hash(logCall.position, logCall.messageString,
+ logCall.logLevel, logCall.logGroup))
+ }
+ }
+ }
+
+ fun getStatements(): Map<LogCall, Long> {
+ return statements
+ }
+ }
+
+ interface ProtologViewerConfigBuilder {
+ fun build(statements: Map<LogCall, Long>): ByteArray
+ }
+
private fun viewerConf(command: CommandOptions) {
val groups = injector.readLogGroups(
command.protoLogGroupsJarArg,
command.protoLogGroupsClassNameArg)
- val processor = ProtoLogCallProcessor(command.protoLogClassNameArg,
+ val processor = ProtoLogCallProcessorImpl(command.protoLogClassNameArg,
command.protoLogGroupsClassNameArg, groups)
- val builder = ViewerConfigBuilder(processor)
+ val outputType = command.viewerConfigTypeArg
+
+ val configBuilder: ProtologViewerConfigBuilder = when (outputType.lowercase()) {
+ "json" -> ViewerConfigJsonBuilder()
+ "proto" -> ViewerConfigProtoBuilder()
+ else -> error("Invalid output type provide. Provided '$outputType'.")
+ }
val executor = newThreadPool()
+ val logCallRegistry = LogCallRegistry()
+
try {
command.javaSourceArgs.map { path ->
executor.submitCallable {
@@ -163,7 +261,7 @@ ${updates.replaceIndent(" ")}
if (containsProtoLogText(text, command.protoLogClassNameArg)) {
try {
val code = tryParse(text, path)
- builder.findLogCalls(code, path, packagePath(file, code))
+ findLogCalls(code, path, packagePath(file, code), processor)
} catch (ex: ParsingException) {
// If we cannot parse this file, skip it (and log why). Compilation will
// fail in a subsequent build step.
@@ -175,15 +273,38 @@ ${updates.replaceIndent(" ")}
}
}
}.forEach { future ->
- builder.addLogCalls(future.get() ?: return@forEach)
+ logCallRegistry.addLogCalls(future.get() ?: return@forEach)
}
} finally {
executor.shutdown()
}
- val out = injector.fileOutputStream(command.viewerConfigJsonArg)
- out.write(builder.build().toByteArray())
- out.close()
+ val outFile = injector.fileOutputStream(command.viewerConfigFileNameArg)
+ outFile.write(configBuilder.build(logCallRegistry.getStatements()))
+ outFile.close()
+ }
+
+ private fun findLogCalls(
+ unit: CompilationUnit,
+ path: String,
+ packagePath: String,
+ processor: ProtoLogCallProcessorImpl
+ ): List<LogCall> {
+ val calls = mutableListOf<LogCall>()
+ val logCallVisitor = object : ProtoLogCallVisitor {
+ override fun processCall(
+ call: MethodCallExpr,
+ messageString: String,
+ level: LogLevel,
+ group: LogGroup
+ ) {
+ val logCall = LogCall(messageString, level, group, packagePath)
+ calls.add(logCall)
+ }
+ }
+ processor.process(unit, logCallVisitor, path)
+
+ return calls
}
private fun packagePath(file: File, code: CompilationUnit): String {
@@ -196,7 +317,7 @@ ${updates.replaceIndent(" ")}
private fun read(command: CommandOptions) {
LogParser(ViewerConfigParser())
.parse(FileInputStream(command.logProtofileArg),
- FileInputStream(command.viewerConfigJsonArg), System.out)
+ FileInputStream(command.viewerConfigFileNameArg), System.out)
}
@JvmStatic
diff --git a/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt b/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt
index 27e61a139451..2b7164191dd0 100644
--- a/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt
@@ -17,15 +17,16 @@
package com.android.protolog.tool
import com.android.internal.protolog.common.LogDataType
+import com.android.internal.protolog.common.LogLevel
import com.github.javaparser.StaticJavaParser
import com.github.javaparser.ast.CompilationUnit
import com.github.javaparser.ast.NodeList
import com.github.javaparser.ast.body.VariableDeclarator
-import com.github.javaparser.ast.expr.BooleanLiteralExpr
import com.github.javaparser.ast.expr.CastExpr
import com.github.javaparser.ast.expr.Expression
import com.github.javaparser.ast.expr.FieldAccessExpr
import com.github.javaparser.ast.expr.IntegerLiteralExpr
+import com.github.javaparser.ast.expr.LongLiteralExpr
import com.github.javaparser.ast.expr.MethodCallExpr
import com.github.javaparser.ast.expr.NameExpr
import com.github.javaparser.ast.expr.NullLiteralExpr
@@ -34,7 +35,6 @@ import com.github.javaparser.ast.expr.TypeExpr
import com.github.javaparser.ast.expr.VariableDeclarationExpr
import com.github.javaparser.ast.stmt.BlockStmt
import com.github.javaparser.ast.stmt.ExpressionStmt
-import com.github.javaparser.ast.stmt.IfStmt
import com.github.javaparser.ast.type.ArrayType
import com.github.javaparser.ast.type.ClassOrInterfaceType
import com.github.javaparser.ast.type.PrimitiveType
@@ -44,15 +44,59 @@ import com.github.javaparser.printer.PrettyPrinterConfiguration
class SourceTransformer(
protoLogImplClassName: String,
- protoLogCacheClassName: String,
private val protoLogCallProcessor: ProtoLogCallProcessor
-) : ProtoLogCallVisitor {
- override fun processCall(
- call: MethodCallExpr,
- messageString: String,
- level: LogLevel,
- group: LogGroup
- ) {
+) {
+ private val inlinePrinter: PrettyPrinter
+ private val objectType = StaticJavaParser.parseClassOrInterfaceType("Object")
+
+ init {
+ val config = PrettyPrinterConfiguration()
+ config.endOfLineCharacter = " "
+ config.indentSize = 0
+ config.tabWidth = 1
+ inlinePrinter = PrettyPrinter(config)
+ }
+
+ fun processClass(
+ code: String,
+ path: String,
+ packagePath: String,
+ compilationUnit: CompilationUnit =
+ StaticJavaParser.parse(code)
+ ): String {
+ this.path = path
+ this.packagePath = packagePath
+ processedCode = code.split('\n').toMutableList()
+ offsets = IntArray(processedCode.size)
+ protoLogCallProcessor.process(compilationUnit, protoLogCallVisitor, otherCallVisitor, path)
+ return processedCode.joinToString("\n")
+ }
+
+ private val protoLogImplClassNode =
+ StaticJavaParser.parseExpression<FieldAccessExpr>(protoLogImplClassName)
+ private var processedCode: MutableList<String> = mutableListOf()
+ private var offsets: IntArray = IntArray(0)
+ /** The path of the file being processed, relative to $ANDROID_BUILD_TOP */
+ private var path: String = ""
+ /** The path of the file being processed, relative to the root package */
+ private var packagePath: String = ""
+
+ private val protoLogCallVisitor = object : ProtoLogCallVisitor {
+ override fun processCall(
+ call: MethodCallExpr,
+ messageString: String,
+ level: LogLevel,
+ group: LogGroup
+ ) {
+ validateCall(call)
+ val processedCallStatement =
+ createProcessedCallStatement(call, group, level, messageString)
+ val parentStmt = call.parentNode.get() as ExpressionStmt
+ injectProcessedCallStatementInCode(processedCallStatement, parentStmt)
+ }
+ }
+
+ private fun validateCall(call: MethodCallExpr) {
// Input format: ProtoLog.e(GROUP, "msg %d", arg)
if (!call.parentNode.isPresent) {
// Should never happen
@@ -70,89 +114,79 @@ class SourceTransformer(
throw RuntimeException("Unable to process log call $call " +
"- no grandparent node in AST")
}
- val ifStmt: IfStmt
- if (group.enabled) {
- val hash = CodeUtils.hash(packagePath, messageString, level, group)
- val newCall = call.clone()
- if (!group.textEnabled) {
- // Remove message string if text logging is not enabled by default.
- // Out: ProtoLog.e(GROUP, null, arg)
- newCall.arguments[1].replace(NameExpr("null"))
- }
- // Insert message string hash as a second argument.
- // Out: ProtoLog.e(GROUP, 1234, null, arg)
- newCall.arguments.add(1, IntegerLiteralExpr(hash))
- val argTypes = LogDataType.parseFormatString(messageString)
- val typeMask = LogDataType.logDataTypesToBitMask(argTypes)
- // Insert bitmap representing which Number parameters are to be considered as
- // floating point numbers.
- // Out: ProtoLog.e(GROUP, 1234, 0, null, arg)
- newCall.arguments.add(2, IntegerLiteralExpr(typeMask))
- // Replace call to a stub method with an actual implementation.
- // Out: ProtoLogImpl.e(GROUP, 1234, null, arg)
- newCall.setScope(protoLogImplClassNode)
- // Create a call to ProtoLog$Cache.GROUP_enabled
- // Out: com.android.server.protolog.ProtoLog$Cache.GROUP_enabled
- val isLogEnabled = FieldAccessExpr(protoLogCacheClassNode, "${group.name}_enabled")
- if (argTypes.size != call.arguments.size - 2) {
- throw InvalidProtoLogCallException(
- "Number of arguments (${argTypes.size} does not mach format" +
- " string in: $call", ParsingContext(path, call))
- }
- val blockStmt = BlockStmt()
- if (argTypes.isNotEmpty()) {
- // Assign every argument to a variable to check its type in compile time
- // (this is assignment is optimized-out by dex tool, there is no runtime impact)/
- // Out: long protoLogParam0 = arg
- argTypes.forEachIndexed { idx, type ->
- val varName = "protoLogParam$idx"
- val declaration = VariableDeclarator(getASTTypeForDataType(type), varName,
- getConversionForType(type)(newCall.arguments[idx + 4].clone()))
- blockStmt.addStatement(ExpressionStmt(VariableDeclarationExpr(declaration)))
- newCall.setArgument(idx + 4, NameExpr(SimpleName(varName)))
- }
- } else {
- // Assign (Object[])null as the vararg parameter to prevent allocating an empty
- // object array.
- val nullArray = CastExpr(ArrayType(objectType), NullLiteralExpr())
- newCall.addArgument(nullArray)
+ }
+
+ private fun createProcessedCallStatement(
+ call: MethodCallExpr,
+ group: LogGroup,
+ level: LogLevel,
+ messageString: String
+ ): BlockStmt {
+ val hash = CodeUtils.hash(packagePath, messageString, level, group)
+ val newCall = call.clone()
+ if (!group.textEnabled) {
+ // Remove message string if text logging is not enabled by default.
+ // Out: ProtoLog.e(GROUP, null, arg)
+ newCall.arguments[1].replace(NameExpr("null"))
+ }
+ // Insert message string hash as a second argument.
+ // Out: ProtoLog.e(GROUP, 1234, null, arg)
+ newCall.arguments.add(1, LongLiteralExpr("" + hash + "L"))
+ val argTypes = LogDataType.parseFormatString(messageString)
+ val typeMask = LogDataType.logDataTypesToBitMask(argTypes)
+ // Insert bitmap representing which Number parameters are to be considered as
+ // floating point numbers.
+ // Out: ProtoLog.e(GROUP, 1234, 0, null, arg)
+ newCall.arguments.add(2, IntegerLiteralExpr(typeMask))
+ // Replace call to a stub method with an actual implementation.
+ // Out: ProtoLogImpl.e(GROUP, 1234, null, arg)
+ newCall.setScope(protoLogImplClassNode)
+ if (argTypes.size != call.arguments.size - 2) {
+ throw InvalidProtoLogCallException(
+ "Number of arguments (${argTypes.size} does not match format" +
+ " string in: $call", ParsingContext(path, call))
+ }
+ val blockStmt = BlockStmt()
+ if (argTypes.isNotEmpty()) {
+ // Assign every argument to a variable to check its type in compile time
+ // (this is assignment is optimized-out by dex tool, there is no runtime impact)/
+ // Out: long protoLogParam0 = arg
+ argTypes.forEachIndexed { idx, type ->
+ val varName = "protoLogParam$idx"
+ val declaration = VariableDeclarator(getASTTypeForDataType(type), varName,
+ getConversionForType(type)(newCall.arguments[idx + 4].clone()))
+ blockStmt.addStatement(ExpressionStmt(VariableDeclarationExpr(declaration)))
+ newCall.setArgument(idx + 4, NameExpr(SimpleName(varName)))
}
- blockStmt.addStatement(ExpressionStmt(newCall))
- // Create an IF-statement with the previously created condition.
- // Out: if (ProtoLogImpl.isEnabled(GROUP)) {
- // long protoLogParam0 = arg;
- // ProtoLogImpl.e(GROUP, 1234, 0, null, protoLogParam0);
- // }
- ifStmt = IfStmt(isLogEnabled, blockStmt, null)
} else {
- // Surround with if (false).
- val newCall = parentStmt.clone()
- ifStmt = IfStmt(BooleanLiteralExpr(false), BlockStmt(NodeList(newCall)), null)
- newCall.setBlockComment(" ${group.name} is disabled ")
+ // Assign (Object[])null as the vararg parameter to prevent allocating an empty
+ // object array.
+ val nullArray = CastExpr(ArrayType(objectType), NullLiteralExpr())
+ newCall.addArgument(nullArray)
}
+ blockStmt.addStatement(ExpressionStmt(newCall))
+
+ return blockStmt
+ }
+
+ private fun injectProcessedCallStatementInCode(
+ processedCallStatement: BlockStmt,
+ parentStmt: ExpressionStmt
+ ) {
// Inline the new statement.
- val printedIfStmt = inlinePrinter.print(ifStmt)
+ val printedBlockStmt = inlinePrinter.print(processedCallStatement)
// Append blank lines to preserve line numbering in file (to allow debugging)
val parentRange = parentStmt.range.get()
val newLines = parentRange.end.line - parentRange.begin.line
- val newStmt = printedIfStmt.substringBeforeLast('}') + ("\n".repeat(newLines)) + '}'
+ val newStmt = printedBlockStmt.substringBeforeLast('}') + ("\n".repeat(newLines)) + '}'
// pre-workaround code, see explanation below
- /*
- val inlinedIfStmt = StaticJavaParser.parseStatement(newStmt)
- LexicalPreservingPrinter.setup(inlinedIfStmt)
- // Replace the original call.
- if (!parentStmt.replace(inlinedIfStmt)) {
- // Should never happen
- throw RuntimeException("Unable to process log call $call " +
- "- unable to replace the call.")
- }
- */
+
/** Workaround for a bug in JavaParser (AST tree invalid after replacing a node when using
* LexicalPreservingPrinter (https://github.com/javaparser/javaparser/issues/2290).
* Replace the code below with the one commended-out above one the issue is resolved. */
if (!parentStmt.range.isPresent) {
// Should never happen
- throw RuntimeException("Unable to process log call $call " +
+ throw RuntimeException("Unable to process log call in $parentStmt " +
"- unable to replace the call.")
}
val range = parentStmt.range.get()
@@ -160,29 +194,38 @@ class SourceTransformer(
val oldLines = processedCode.subList(begin, range.end.line)
val oldCode = oldLines.joinToString("\n")
val newCode = oldCode.replaceRange(
- offsets[begin] + range.begin.column - 1,
- oldCode.length - oldLines.lastOrNull()!!.length +
- range.end.column + offsets[range.end.line - 1], newStmt)
+ offsets[begin] + range.begin.column - 1,
+ oldCode.length - oldLines.lastOrNull()!!.length +
+ range.end.column + offsets[range.end.line - 1], newStmt)
newCode.split("\n").forEachIndexed { idx, line ->
offsets[begin + idx] += line.length - processedCode[begin + idx].length
processedCode[begin + idx] = line
}
}
- private val inlinePrinter: PrettyPrinter
- private val objectType = StaticJavaParser.parseClassOrInterfaceType("Object")
+ private val otherCallVisitor = object : MethodCallVisitor {
+ override fun processCall(call: MethodCallExpr) {
+ val newCall = call.clone()
+ newCall.setScope(protoLogImplClassNode)
- init {
- val config = PrettyPrinterConfiguration()
- config.endOfLineCharacter = " "
- config.indentSize = 0
- config.tabWidth = 1
- inlinePrinter = PrettyPrinter(config)
+ val range = call.range.get()
+ val begin = range.begin.line - 1
+ val oldLines = processedCode.subList(begin, range.end.line)
+ val oldCode = oldLines.joinToString("\n")
+ val newCode = oldCode.replaceRange(
+ offsets[begin] + range.begin.column - 1,
+ oldCode.length - oldLines.lastOrNull()!!.length +
+ range.end.column + offsets[range.end.line - 1], newCall.toString())
+ newCode.split("\n").forEachIndexed { idx, line ->
+ offsets[begin + idx] += line.length - processedCode[begin + idx].length
+ processedCode[begin + idx] = line
+ }
+ }
}
companion object {
private val stringType: ClassOrInterfaceType =
- StaticJavaParser.parseClassOrInterfaceType("String")
+ StaticJavaParser.parseClassOrInterfaceType("String")
fun getASTTypeForDataType(type: Int): Type {
return when (type) {
@@ -201,36 +244,10 @@ class SourceTransformer(
return when (type) {
LogDataType.STRING -> { expr ->
MethodCallExpr(TypeExpr(StaticJavaParser.parseClassOrInterfaceType("String")),
- SimpleName("valueOf"), NodeList(expr))
+ SimpleName("valueOf"), NodeList(expr))
}
else -> { expr -> expr }
}
}
}
-
- private val protoLogImplClassNode =
- StaticJavaParser.parseExpression<FieldAccessExpr>(protoLogImplClassName)
- private val protoLogCacheClassNode =
- StaticJavaParser.parseExpression<FieldAccessExpr>(protoLogCacheClassName)
- private var processedCode: MutableList<String> = mutableListOf()
- private var offsets: IntArray = IntArray(0)
- /** The path of the file being processed, relative to $ANDROID_BUILD_TOP */
- private var path: String = ""
- /** The path of the file being processed, relative to the root package */
- private var packagePath: String = ""
-
- fun processClass(
- code: String,
- path: String,
- packagePath: String,
- compilationUnit: CompilationUnit =
- StaticJavaParser.parse(code)
- ): String {
- this.path = path
- this.packagePath = packagePath
- processedCode = code.split('\n').toMutableList()
- offsets = IntArray(processedCode.size)
- protoLogCallProcessor.process(compilationUnit, this, path)
- return processedCode.joinToString("\n")
- }
}
diff --git a/tools/protologtool/src/com/android/protolog/tool/ViewerConfigBuilder.kt b/tools/protologtool/src/com/android/protolog/tool/ViewerConfigBuilder.kt
deleted file mode 100644
index 175c71ff810b..000000000000
--- a/tools/protologtool/src/com/android/protolog/tool/ViewerConfigBuilder.kt
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.protolog.tool
-
-import com.android.json.stream.JsonWriter
-import com.github.javaparser.ast.CompilationUnit
-import com.android.protolog.tool.Constants.VERSION
-import com.github.javaparser.ast.expr.MethodCallExpr
-import java.io.StringWriter
-
-class ViewerConfigBuilder(
- private val processor: ProtoLogCallProcessor
-) {
- private fun addLogCall(logCall: LogCall, context: ParsingContext) {
- val group = logCall.logGroup
- val messageString = logCall.messageString
- if (group.enabled) {
- val key = logCall.key()
- if (statements.containsKey(key)) {
- if (statements[key] != logCall) {
- throw HashCollisionException(
- "Please modify the log message \"$messageString\" " +
- "or \"${statements[key]}\" - their hashes are equal.", context)
- }
- } else {
- groups.add(group)
- statements[key] = logCall
- }
- }
- }
-
- private val statements: MutableMap<Int, LogCall> = mutableMapOf()
- private val groups: MutableSet<LogGroup> = mutableSetOf()
-
- fun findLogCalls(
- unit: CompilationUnit,
- path: String,
- packagePath: String
- ): List<Pair<LogCall, ParsingContext>> {
- val calls = mutableListOf<Pair<LogCall, ParsingContext>>()
- val visitor = object : ProtoLogCallVisitor {
- override fun processCall(
- call: MethodCallExpr,
- messageString: String,
- level: LogLevel,
- group: LogGroup
- ) {
- val logCall = LogCall(messageString, level, group, packagePath)
- val context = ParsingContext(path, call)
- calls.add(logCall to context)
- }
- }
- processor.process(unit, visitor, path)
-
- return calls
- }
-
- fun addLogCalls(calls: List<Pair<LogCall, ParsingContext>>) {
- calls.forEach { (logCall, context) ->
- addLogCall(logCall, context)
- }
- }
-
- fun build(): String {
- val stringWriter = StringWriter()
- val writer = JsonWriter(stringWriter)
- writer.setIndent(" ")
- writer.beginObject()
- writer.name("version")
- writer.value(VERSION)
- writer.name("messages")
- writer.beginObject()
- statements.toSortedMap().forEach { (key, value) ->
- writer.name(key.toString())
- writer.beginObject()
- writer.name("message")
- writer.value(value.messageString)
- writer.name("level")
- writer.value(value.logLevel.name)
- writer.name("group")
- writer.value(value.logGroup.name)
- writer.name("at")
- writer.value(value.position)
- writer.endObject()
- }
- writer.endObject()
- writer.name("groups")
- writer.beginObject()
- groups.toSortedSet(Comparator { o1, o2 -> o1.name.compareTo(o2.name) }).forEach { group ->
- writer.name(group.name)
- writer.beginObject()
- writer.name("tag")
- writer.value(group.tag)
- writer.endObject()
- }
- writer.endObject()
- writer.endObject()
- stringWriter.buffer.append('\n')
- return stringWriter.toString()
- }
-
- data class LogCall(
- val messageString: String,
- val logLevel: LogLevel,
- val logGroup: LogGroup,
- val position: String
- ) {
- fun key() = CodeUtils.hash(position, messageString, logLevel, logGroup)
- }
-}
diff --git a/tools/protologtool/src/com/android/protolog/tool/ViewerConfigJsonBuilder.kt b/tools/protologtool/src/com/android/protolog/tool/ViewerConfigJsonBuilder.kt
new file mode 100644
index 000000000000..7714db212c9f
--- /dev/null
+++ b/tools/protologtool/src/com/android/protolog/tool/ViewerConfigJsonBuilder.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2024 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.protolog.tool
+
+import com.android.json.stream.JsonWriter
+import com.android.protolog.tool.Constants.VERSION
+import java.io.StringWriter
+
+class ViewerConfigJsonBuilder : ProtoLogTool.ProtologViewerConfigBuilder {
+ override fun build(statements: Map<ProtoLogTool.LogCall, Long>): ByteArray {
+ val groups = statements.map { it.key.logGroup }.toSet()
+ val stringWriter = StringWriter()
+ val writer = JsonWriter(stringWriter)
+ writer.setIndent(" ")
+ writer.beginObject()
+ writer.name("version")
+ writer.value(VERSION)
+ writer.name("messages")
+ writer.beginObject()
+ statements.forEach { (log, key) ->
+ writer.name(key.toString())
+ writer.beginObject()
+ writer.name("message")
+ writer.value(log.messageString)
+ writer.name("level")
+ writer.value(log.logLevel.name)
+ writer.name("group")
+ writer.value(log.logGroup.name)
+ writer.name("at")
+ writer.value(log.position)
+ writer.endObject()
+ }
+ writer.endObject()
+ writer.name("groups")
+ writer.beginObject()
+ groups.toSortedSet { o1, o2 -> o1.name.compareTo(o2.name) }.forEach { group ->
+ writer.name(group.name)
+ writer.beginObject()
+ writer.name("tag")
+ writer.value(group.tag)
+ writer.endObject()
+ }
+ writer.endObject()
+ writer.endObject()
+ stringWriter.buffer.append('\n')
+ return stringWriter.toString().toByteArray()
+ }
+}
diff --git a/tools/protologtool/src/com/android/protolog/tool/ViewerConfigParser.kt b/tools/protologtool/src/com/android/protolog/tool/ViewerConfigParser.kt
index 7278db0094e6..58be3a3e04af 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ViewerConfigParser.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ViewerConfigParser.kt
@@ -63,12 +63,12 @@ open class ViewerConfigParser {
return GroupEntry(tag)
}
- fun parseMessages(jsonReader: JsonReader): Map<Int, MessageEntry> {
- val config: MutableMap<Int, MessageEntry> = mutableMapOf()
+ fun parseMessages(jsonReader: JsonReader): Map<Long, MessageEntry> {
+ val config: MutableMap<Long, MessageEntry> = mutableMapOf()
jsonReader.beginObject()
while (jsonReader.hasNext()) {
val key = jsonReader.nextName()
- val hash = key.toIntOrNull()
+ val hash = key.toLongOrNull()
?: throw InvalidViewerConfigException("Invalid key in messages viewer config")
config[hash] = parseMessage(jsonReader)
}
@@ -89,8 +89,8 @@ open class ViewerConfigParser {
data class ConfigEntry(val messageString: String, val level: String, val tag: String)
- open fun parseConfig(jsonReader: JsonReader): Map<Int, ConfigEntry> {
- var messages: Map<Int, MessageEntry>? = null
+ open fun parseConfig(jsonReader: JsonReader): Map<Long, ConfigEntry> {
+ var messages: Map<Long, MessageEntry>? = null
var groups: Map<String, GroupEntry>? = null
var version: String? = null
diff --git a/tools/protologtool/src/com/android/protolog/tool/ViewerConfigProtoBuilder.kt b/tools/protologtool/src/com/android/protolog/tool/ViewerConfigProtoBuilder.kt
new file mode 100644
index 000000000000..cf0876a1f072
--- /dev/null
+++ b/tools/protologtool/src/com/android/protolog/tool/ViewerConfigProtoBuilder.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.protolog.tool
+
+import perfetto.protos.PerfettoTrace.ProtoLogLevel
+import perfetto.protos.PerfettoTrace.ProtoLogViewerConfig
+
+/**
+ * A builder class to construct the viewer configuration (i.e. mappings of protolog hashes to log
+ * message information used to decode the protolog messages) encoded as a proto message.
+ */
+class ViewerConfigProtoBuilder : ProtoLogTool.ProtologViewerConfigBuilder {
+ /**
+ * @return a byte array of a ProtoLogViewerConfig proto message encoding all the viewer
+ * configurations mapping protolog hashes to message information and log group information.
+ */
+ override fun build(statements: Map<ProtoLogTool.LogCall, Long>): ByteArray {
+ val configBuilder = ProtoLogViewerConfig.newBuilder()
+
+ val groups = statements.map { it.key.logGroup }.toSet()
+ val groupIds = mutableMapOf<LogGroup, Int>()
+ groups.forEach {
+ groupIds.putIfAbsent(it, groupIds.size + 1)
+ }
+
+ groupIds.forEach { (group, id) ->
+ configBuilder.addGroups(ProtoLogViewerConfig.Group.newBuilder()
+ .setId(id)
+ .setName(group.name)
+ .setTag(group.tag)
+ .build())
+ }
+
+ statements.forEach { (log, key) ->
+ val groupId = groupIds[log.logGroup] ?: error("missing group id")
+
+ configBuilder.addMessages(
+ ProtoLogViewerConfig.MessageData.newBuilder()
+ .setMessageId(key)
+ .setMessage(log.messageString)
+ .setLevel(
+ ProtoLogLevel.forNumber(log.logLevel.ordinal + 1))
+ .setGroupId(groupId)
+ )
+ }
+
+ return configBuilder.build().toByteArray()
+ }
+}
diff --git a/tools/protologtool/tests/com/android/protolog/tool/CodeUtilsTest.kt b/tools/protologtool/tests/com/android/protolog/tool/CodeUtilsTest.kt
index b916f8f00a68..0cd02a5c5ce8 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/CodeUtilsTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/CodeUtilsTest.kt
@@ -16,6 +16,7 @@
package com.android.protolog.tool
+import com.android.internal.protolog.common.LogLevel
import com.github.javaparser.StaticJavaParser
import com.github.javaparser.ast.expr.BinaryExpr
import com.github.javaparser.ast.expr.StringLiteralExpr
@@ -27,31 +28,31 @@ import org.junit.Test
class CodeUtilsTest {
@Test
fun hash() {
- assertEquals(-1259556708, CodeUtils.hash("Test.java:50", "test",
+ assertEquals(3883826472308915399, CodeUtils.hash("Test.java:50", "test",
LogLevel.DEBUG, LogGroup("test", true, true, "TAG")))
}
@Test
fun hash_changeLocation() {
- assertEquals(15793504, CodeUtils.hash("Test.java:10", "test2",
+ assertEquals(4125273133972468649, CodeUtils.hash("Test.java:10", "test2",
LogLevel.DEBUG, LogGroup("test", true, true, "TAG")))
}
@Test
fun hash_changeLevel() {
- assertEquals(-731772463, CodeUtils.hash("Test.java:50", "test",
+ assertEquals(2618535069521361990, CodeUtils.hash("Test.java:50", "test",
LogLevel.ERROR, LogGroup("test", true, true, "TAG")))
}
@Test
fun hash_changeMessage() {
- assertEquals(-2026343204, CodeUtils.hash("Test.java:50", "test2",
+ assertEquals(8907822592109789043, CodeUtils.hash("Test.java:50", "test2",
LogLevel.DEBUG, LogGroup("test", true, true, "TAG")))
}
@Test
fun hash_changeGroup() {
- assertEquals(1607870166, CodeUtils.hash("Test.java:50", "test2",
+ assertEquals(-1299517016176640015, CodeUtils.hash("Test.java:50", "test2",
LogLevel.DEBUG, LogGroup("test2", true, true, "TAG")))
}
diff --git a/tools/protologtool/tests/com/android/protolog/tool/CommandOptionsTest.kt b/tools/protologtool/tests/com/android/protolog/tool/CommandOptionsTest.kt
index 3cfbb435a764..5ef2833080a2 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/CommandOptionsTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/CommandOptionsTest.kt
@@ -16,7 +16,9 @@
package com.android.protolog.tool
+import com.google.common.truth.Truth.assertThat
import org.junit.Assert.assertEquals
+import org.junit.Assert.assertThrows
import org.junit.Test
class CommandOptionsTest {
@@ -35,6 +37,10 @@ class CommandOptionsTest {
private const val TEST_PROTOLOGGROUP_JAR = "out/soong/.intermediates/frameworks/base/" +
"services/core/services.core.wm.protologgroups/android_common/javac/" +
"services.core.wm.protologgroups.jar"
+ private const val TEST_VIEWER_CONFIG_FILE_PATH = "/some/viewer/config/file/path.pb"
+ private const val TEST_LEGACY_VIEWER_CONFIG_FILE_PATH =
+ "/some/viewer/config/file/path.json.gz"
+ private const val TEST_LEGACY_OUTPUT_FILE_PATH = "/some/output/file/path.winscope"
private const val TEST_SRC_JAR = "out/soong/.temp/sbox175955373/" +
"services.core.wm.protolog.srcjar"
private const val TEST_VIEWER_JSON = "out/soong/.temp/sbox175955373/" +
@@ -42,186 +48,263 @@ class CommandOptionsTest {
private const val TEST_LOG = "./test_log.pb"
}
- @Test(expected = InvalidCommandException::class)
+ @Test
fun noCommand() {
- CommandOptions(arrayOf())
+ val exception = assertThrows<InvalidCommandException>(InvalidCommandException::class.java) {
+ CommandOptions(arrayOf())
+ }
+ assertThat(exception).hasMessageThat().contains("No command specified")
}
- @Test(expected = InvalidCommandException::class)
+ @Test
fun invalidCommand() {
val testLine = "invalid"
- CommandOptions(testLine.split(' ').toTypedArray())
+ val exception = assertThrows<InvalidCommandException>(InvalidCommandException::class.java) {
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+ assertThat(exception).hasMessageThat().contains("Unknown command")
}
@Test
fun transformClasses() {
- val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
- "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
- "--protolog-cache-class $TEST_PROTOLOGCACHE_CLASS " +
+ val testLine = "transform-protolog-calls " +
+ "--protolog-class $TEST_PROTOLOG_CLASS " +
"--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
"--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
+ "--viewer-config-file-path $TEST_VIEWER_CONFIG_FILE_PATH " +
+ "--legacy-viewer-config-file-path $TEST_LEGACY_VIEWER_CONFIG_FILE_PATH " +
+ "--legacy-output-file-path $TEST_LEGACY_OUTPUT_FILE_PATH " +
"--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
val cmd = CommandOptions(testLine.split(' ').toTypedArray())
assertEquals(CommandOptions.TRANSFORM_CALLS_CMD, cmd.command)
assertEquals(TEST_PROTOLOG_CLASS, cmd.protoLogClassNameArg)
- assertEquals(TEST_PROTOLOGIMPL_CLASS, cmd.protoLogImplClassNameArg)
assertEquals(TEST_PROTOLOGGROUP_CLASS, cmd.protoLogGroupsClassNameArg)
assertEquals(TEST_PROTOLOGGROUP_JAR, cmd.protoLogGroupsJarArg)
+ assertEquals(TEST_VIEWER_CONFIG_FILE_PATH, cmd.viewerConfigFilePathArg)
+ assertEquals(TEST_LEGACY_VIEWER_CONFIG_FILE_PATH, cmd.legacyViewerConfigFilePathArg)
+ assertEquals(TEST_LEGACY_OUTPUT_FILE_PATH, cmd.legacyOutputFilePath)
assertEquals(TEST_SRC_JAR, cmd.outputSourceJarArg)
assertEquals(TEST_JAVA_SRC, cmd.javaSourceArgs)
}
- @Test(expected = InvalidCommandException::class)
- fun transformClasses_noProtoLogClass() {
+ @Test
+ fun transformClasses_noViewerConfigFile() {
val testLine = "transform-protolog-calls " +
- "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
- "--protolog-cache-class $TEST_PROTOLOGCACHE_CLASS " +
+ "--protolog-class $TEST_PROTOLOG_CLASS " +
"--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
"--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
+ "--legacy-viewer-config-file-path $TEST_LEGACY_VIEWER_CONFIG_FILE_PATH " +
+ "--legacy-output-file-path $TEST_LEGACY_OUTPUT_FILE_PATH " +
"--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
- CommandOptions(testLine.split(' ').toTypedArray())
+ val exception = assertThrows<InvalidCommandException>(InvalidCommandException::class.java) {
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+ assertThat(exception).hasMessageThat().contains("--viewer-config-file-path")
}
- @Test(expected = InvalidCommandException::class)
- fun transformClasses_noProtoLogImplClass() {
- val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
- "--protolog-cache-class $TEST_PROTOLOGCACHE_CLASS " +
+ @Test
+ fun transformClasses_noLegacyViewerConfigFile() {
+ val testLine = "transform-protolog-calls " +
+ "--protolog-class $TEST_PROTOLOG_CLASS " +
"--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
"--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
+ "--viewer-config-file-path $TEST_VIEWER_CONFIG_FILE_PATH " +
+ "--legacy-output-file-path $TEST_LEGACY_OUTPUT_FILE_PATH " +
"--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
- CommandOptions(testLine.split(' ').toTypedArray())
+ val cmd = CommandOptions(testLine.split(' ').toTypedArray())
+ assertEquals(CommandOptions.TRANSFORM_CALLS_CMD, cmd.command)
+ assertEquals(TEST_PROTOLOG_CLASS, cmd.protoLogClassNameArg)
+ assertEquals(TEST_PROTOLOGGROUP_CLASS, cmd.protoLogGroupsClassNameArg)
+ assertEquals(TEST_PROTOLOGGROUP_JAR, cmd.protoLogGroupsJarArg)
+ assertEquals(TEST_VIEWER_CONFIG_FILE_PATH, cmd.viewerConfigFilePathArg)
+ assertEquals(null, cmd.legacyViewerConfigFilePathArg)
+ assertEquals(TEST_LEGACY_OUTPUT_FILE_PATH, cmd.legacyOutputFilePath)
+ assertEquals(TEST_SRC_JAR, cmd.outputSourceJarArg)
+ assertEquals(TEST_JAVA_SRC, cmd.javaSourceArgs)
}
- @Test(expected = InvalidCommandException::class)
- fun transformClasses_noProtoLogCacheClass() {
- val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
- "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
+ @Test
+ fun transformClasses_noLegacyOutputFile() {
+ val testLine = "transform-protolog-calls " +
+ "--protolog-class $TEST_PROTOLOG_CLASS " +
"--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
"--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
+ "--viewer-config-file-path $TEST_VIEWER_CONFIG_FILE_PATH " +
+ "--legacy-viewer-config-file-path $TEST_LEGACY_VIEWER_CONFIG_FILE_PATH " +
"--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
- CommandOptions(testLine.split(' ').toTypedArray())
+ val cmd = CommandOptions(testLine.split(' ').toTypedArray())
+ assertEquals(CommandOptions.TRANSFORM_CALLS_CMD, cmd.command)
+ assertEquals(TEST_PROTOLOG_CLASS, cmd.protoLogClassNameArg)
+ assertEquals(TEST_PROTOLOGGROUP_CLASS, cmd.protoLogGroupsClassNameArg)
+ assertEquals(TEST_PROTOLOGGROUP_JAR, cmd.protoLogGroupsJarArg)
+ assertEquals(TEST_VIEWER_CONFIG_FILE_PATH, cmd.viewerConfigFilePathArg)
+ assertEquals(TEST_LEGACY_VIEWER_CONFIG_FILE_PATH, cmd.legacyViewerConfigFilePathArg)
+ assertEquals(null, cmd.legacyOutputFilePath)
+ assertEquals(TEST_SRC_JAR, cmd.outputSourceJarArg)
+ assertEquals(TEST_JAVA_SRC, cmd.javaSourceArgs)
}
- @Test(expected = InvalidCommandException::class)
+ @Test
+ fun transformClasses_noProtoLogClass() {
+ val testLine = "transform-protolog-calls " +
+ "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
+ "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
+ "--viewer-config-file-path $TEST_VIEWER_CONFIG_FILE_PATH " +
+ "--legacy-viewer-config-file-path $TEST_LEGACY_VIEWER_CONFIG_FILE_PATH " +
+ "--legacy-output-file-path $TEST_LEGACY_OUTPUT_FILE_PATH " +
+ "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
+ val exception = assertThrows<InvalidCommandException>(InvalidCommandException::class.java) {
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+ assertThat(exception).hasMessageThat().contains("--protolog-class")
+ }
+
+ @Test
fun transformClasses_noProtoLogGroupClass() {
- val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
- "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
- "--protolog-cache-class $TEST_PROTOLOGCACHE_CLASS " +
+ val testLine = "transform-protolog-calls " +
+ "--protolog-class $TEST_PROTOLOG_CLASS " +
"--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
+ "--viewer-config-file-path $TEST_VIEWER_CONFIG_FILE_PATH " +
+ "--legacy-viewer-config-file-path $TEST_LEGACY_VIEWER_CONFIG_FILE_PATH " +
+ "--legacy-output-file-path $TEST_LEGACY_OUTPUT_FILE_PATH " +
"--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
- CommandOptions(testLine.split(' ').toTypedArray())
+ val exception = assertThrows<InvalidCommandException>(InvalidCommandException::class.java) {
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+ assertThat(exception).hasMessageThat().contains("--loggroups-class")
}
- @Test(expected = InvalidCommandException::class)
+ @Test
fun transformClasses_noProtoLogGroupJar() {
- val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
- "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
- "--protolog-cache-class $TEST_PROTOLOGCACHE_CLASS " +
+ val testLine = "transform-protolog-calls " +
+ "--protolog-class $TEST_PROTOLOG_CLASS " +
"--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
+ "--viewer-config-file-path $TEST_VIEWER_CONFIG_FILE_PATH " +
+ "--legacy-viewer-config-file-path $TEST_LEGACY_VIEWER_CONFIG_FILE_PATH " +
+ "--legacy-output-file-path $TEST_LEGACY_OUTPUT_FILE_PATH " +
"--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
- CommandOptions(testLine.split(' ').toTypedArray())
+ val exception = assertThrows<InvalidCommandException>(InvalidCommandException::class.java) {
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+ assertThat(exception).hasMessageThat().contains("--loggroups-jar")
}
- @Test(expected = InvalidCommandException::class)
+ @Test
fun transformClasses_noOutJar() {
- val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
- "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
- "--protolog-cache-class $TEST_PROTOLOGCACHE_CLASS " +
+ val testLine = "transform-protolog-calls " +
+ "--protolog-class $TEST_PROTOLOG_CLASS " +
"--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
"--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
- TEST_JAVA_SRC.joinToString(" ")
- CommandOptions(testLine.split(' ').toTypedArray())
+ "--viewer-config-file-path $TEST_VIEWER_CONFIG_FILE_PATH " +
+ "--legacy-viewer-config-file-path $TEST_LEGACY_VIEWER_CONFIG_FILE_PATH " +
+ "--legacy-output-file-path $TEST_LEGACY_OUTPUT_FILE_PATH " +
+ "${TEST_JAVA_SRC.joinToString(" ")}"
+ val exception = assertThrows<InvalidCommandException>(InvalidCommandException::class.java) {
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+ assertThat(exception).hasMessageThat().contains("--output-srcjar")
}
- @Test(expected = InvalidCommandException::class)
+ @Test
fun transformClasses_noJavaInput() {
- val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
- "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
- "--protolog-cache-class $TEST_PROTOLOGCACHE_CLASS " +
+ val testLine = "transform-protolog-calls " +
+ "--protolog-class $TEST_PROTOLOG_CLASS " +
"--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
"--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
+ "--viewer-config-file-path $TEST_VIEWER_CONFIG_FILE_PATH " +
+ "--legacy-viewer-config-file-path $TEST_LEGACY_VIEWER_CONFIG_FILE_PATH " +
+ "--legacy-output-file-path $TEST_LEGACY_OUTPUT_FILE_PATH " +
"--output-srcjar $TEST_SRC_JAR"
- CommandOptions(testLine.split(' ').toTypedArray())
+ val exception = assertThrows<InvalidCommandException>(InvalidCommandException::class.java) {
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+ assertThat(exception).hasMessageThat().contains("No java source input files")
}
- @Test(expected = InvalidCommandException::class)
+ @Test
fun transformClasses_invalidProtoLogClass() {
- val testLine = "transform-protolog-calls --protolog-class invalid " +
- "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
- "--protolog-cache-class $TEST_PROTOLOGCACHE_CLASS " +
- "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
- "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
- "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
- CommandOptions(testLine.split(' ').toTypedArray())
- }
-
- @Test(expected = InvalidCommandException::class)
- fun transformClasses_invalidProtoLogImplClass() {
- val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
- "--protolog-impl-class invalid " +
- "--protolog-cache-class $TEST_PROTOLOGCACHE_CLASS " +
- "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
- "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
- "--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
- CommandOptions(testLine.split(' ').toTypedArray())
- }
-
- @Test(expected = InvalidCommandException::class)
- fun transformClasses_invalidProtoLogCacheClass() {
- val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
- "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
- "--protolog-cache-class invalid " +
+ val testLine = "transform-protolog-calls " +
+ "--protolog-class invalid " +
"--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
"--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
+ "--viewer-config-file-path $TEST_VIEWER_CONFIG_FILE_PATH " +
+ "--legacy-viewer-config-file-path $TEST_LEGACY_VIEWER_CONFIG_FILE_PATH " +
+ "--legacy-output-file-path $TEST_LEGACY_OUTPUT_FILE_PATH " +
"--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
- CommandOptions(testLine.split(' ').toTypedArray())
+ val exception = assertThrows<InvalidCommandException>(InvalidCommandException::class.java) {
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+ assertThat(exception).hasMessageThat().contains("class name invalid")
}
- @Test(expected = InvalidCommandException::class)
+ @Test
fun transformClasses_invalidProtoLogGroupClass() {
- val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
- "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
- "--protolog-cache-class $TEST_PROTOLOGCACHE_CLASS " +
+ val testLine = "transform-protolog-calls " +
+ "--protolog-class $TEST_PROTOLOG_CLASS " +
"--loggroups-class invalid " +
"--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
+ "--viewer-config-file-path $TEST_VIEWER_CONFIG_FILE_PATH " +
+ "--legacy-viewer-config-file-path $TEST_LEGACY_VIEWER_CONFIG_FILE_PATH " +
+ "--legacy-output-file-path $TEST_LEGACY_OUTPUT_FILE_PATH " +
"--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
- CommandOptions(testLine.split(' ').toTypedArray())
+ val exception = assertThrows<InvalidCommandException>(InvalidCommandException::class.java) {
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+ assertThat(exception).hasMessageThat().contains("class name invalid")
}
- @Test(expected = InvalidCommandException::class)
+ @Test
fun transformClasses_invalidProtoLogGroupJar() {
- val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
- "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
- "--protolog-cache-class $TEST_PROTOLOGCACHE_CLASS " +
+ val testLine = "transform-protolog-calls " +
+ "--protolog-class $TEST_PROTOLOG_CLASS " +
"--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
"--loggroups-jar invalid.txt " +
+ "--viewer-config-file-path $TEST_VIEWER_CONFIG_FILE_PATH " +
+ "--legacy-viewer-config-file-path $TEST_LEGACY_VIEWER_CONFIG_FILE_PATH " +
+ "--legacy-output-file-path $TEST_LEGACY_OUTPUT_FILE_PATH " +
"--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
- CommandOptions(testLine.split(' ').toTypedArray())
+ val exception = assertThrows<InvalidCommandException>(InvalidCommandException::class.java) {
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+ assertThat(exception).hasMessageThat()
+ .contains("Jar file required, got invalid.txt instead")
}
- @Test(expected = InvalidCommandException::class)
+ @Test
fun transformClasses_invalidOutJar() {
- val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
- "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
- "--protolog-cache-class $TEST_PROTOLOGCACHE_CLASS " +
+ val testLine = "transform-protolog-calls " +
+ "--protolog-class $TEST_PROTOLOG_CLASS " +
"--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
"--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
- "--output-srcjar invalid.db ${TEST_JAVA_SRC.joinToString(" ")}"
- CommandOptions(testLine.split(' ').toTypedArray())
+ "--viewer-config-file-path $TEST_VIEWER_CONFIG_FILE_PATH " +
+ "--legacy-viewer-config-file-path $TEST_LEGACY_VIEWER_CONFIG_FILE_PATH " +
+ "--legacy-output-file-path $TEST_LEGACY_OUTPUT_FILE_PATH " +
+ "--output-srcjar invalid.pb ${TEST_JAVA_SRC.joinToString(" ")}"
+ val exception = assertThrows<InvalidCommandException>(InvalidCommandException::class.java) {
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+ assertThat(exception).hasMessageThat()
+ .contains("Source jar file required, got invalid.pb instead")
}
- @Test(expected = InvalidCommandException::class)
+ @Test
fun transformClasses_invalidJavaInput() {
- val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
- "--protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
- "--protolog-cache-class $TEST_PROTOLOGCACHE_CLASS " +
- "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
- "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
- "--output-srcjar $TEST_SRC_JAR invalid.py"
- CommandOptions(testLine.split(' ').toTypedArray())
+ val testLine = "transform-protolog-calls " +
+ "--protolog-class $TEST_PROTOLOG_CLASS " +
+ "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
+ "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
+ "--viewer-config-file-path $TEST_VIEWER_CONFIG_FILE_PATH " +
+ "--legacy-viewer-config-file-path $TEST_LEGACY_VIEWER_CONFIG_FILE_PATH " +
+ "--legacy-output-file-path $TEST_LEGACY_OUTPUT_FILE_PATH " +
+ "--output-srcjar $TEST_SRC_JAR invalid.py"
+ val exception = assertThrows<InvalidCommandException>(InvalidCommandException::class.java) {
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+ assertThat(exception).hasMessageThat()
+ .contains("Not a java or kotlin source file invalid.py")
}
- @Test(expected = InvalidCommandException::class)
+ @Test
fun transformClasses_unknownParam() {
val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
"--unknown test --protolog-impl-class $TEST_PROTOLOGIMPL_CLASS " +
@@ -229,59 +312,88 @@ class CommandOptionsTest {
"--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
"--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
"--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
- CommandOptions(testLine.split(' ').toTypedArray())
+ val exception = assertThrows<InvalidCommandException>(InvalidCommandException::class.java) {
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+ assertThat(exception).hasMessageThat().contains("--unknown")
}
- @Test(expected = InvalidCommandException::class)
+ @Test
fun transformClasses_noValue() {
val testLine = "transform-protolog-calls --protolog-class $TEST_PROTOLOG_CLASS " +
- "--protolog-impl-class " +
- "--protolog-cache-class $TEST_PROTOLOGCACHE_CLASS " +
- "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
+ "--loggroups-class " +
"--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
"--output-srcjar $TEST_SRC_JAR ${TEST_JAVA_SRC.joinToString(" ")}"
- CommandOptions(testLine.split(' ').toTypedArray())
+ val exception = assertThrows<InvalidCommandException>(InvalidCommandException::class.java) {
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+ assertThat(exception).hasMessageThat().contains("No value for --loggroups-class")
}
@Test
- fun generateConfig() {
- val testLine = "generate-viewer-config --protolog-class $TEST_PROTOLOG_CLASS " +
+ fun generateConfig_json() {
+ val testLine = "generate-viewer-config " +
+ "--protolog-class $TEST_PROTOLOG_CLASS " +
"--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
"--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
- "--viewer-conf $TEST_VIEWER_JSON ${TEST_JAVA_SRC.joinToString(" ")}"
+ "--viewer-config-type json " +
+ "--viewer-config $TEST_VIEWER_JSON ${TEST_JAVA_SRC.joinToString(" ")}"
val cmd = CommandOptions(testLine.split(' ').toTypedArray())
assertEquals(CommandOptions.GENERATE_CONFIG_CMD, cmd.command)
assertEquals(TEST_PROTOLOG_CLASS, cmd.protoLogClassNameArg)
assertEquals(TEST_PROTOLOGGROUP_CLASS, cmd.protoLogGroupsClassNameArg)
assertEquals(TEST_PROTOLOGGROUP_JAR, cmd.protoLogGroupsJarArg)
- assertEquals(TEST_VIEWER_JSON, cmd.viewerConfigJsonArg)
+ assertEquals(TEST_VIEWER_JSON, cmd.viewerConfigFileNameArg)
assertEquals(TEST_JAVA_SRC, cmd.javaSourceArgs)
}
- @Test(expected = InvalidCommandException::class)
+ @Test
+ fun generateConfig_proto() {
+ val testLine = "generate-viewer-config " +
+ "--protolog-class $TEST_PROTOLOG_CLASS " +
+ "--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
+ "--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
+ "--viewer-config-type proto " +
+ "--viewer-config $TEST_VIEWER_JSON ${TEST_JAVA_SRC.joinToString(" ")}"
+ val cmd = CommandOptions(testLine.split(' ').toTypedArray())
+ assertEquals(CommandOptions.GENERATE_CONFIG_CMD, cmd.command)
+ assertEquals(TEST_PROTOLOG_CLASS, cmd.protoLogClassNameArg)
+ assertEquals(TEST_PROTOLOGGROUP_CLASS, cmd.protoLogGroupsClassNameArg)
+ assertEquals(TEST_PROTOLOGGROUP_JAR, cmd.protoLogGroupsJarArg)
+ assertEquals(TEST_VIEWER_JSON, cmd.viewerConfigFileNameArg)
+ assertEquals(TEST_JAVA_SRC, cmd.javaSourceArgs)
+ }
+
+ @Test
fun generateConfig_noViewerConfig() {
val testLine = "generate-viewer-config --protolog-class $TEST_PROTOLOG_CLASS " +
"--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
"--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
TEST_JAVA_SRC.joinToString(" ")
- CommandOptions(testLine.split(' ').toTypedArray())
+ val exception = assertThrows<InvalidCommandException>(InvalidCommandException::class.java) {
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+ assertThat(exception).hasMessageThat().contains("--viewer-config required")
}
- @Test(expected = InvalidCommandException::class)
+ @Test
fun generateConfig_invalidViewerConfig() {
val testLine = "generate-viewer-config --protolog-class $TEST_PROTOLOG_CLASS " +
"--loggroups-class $TEST_PROTOLOGGROUP_CLASS " +
"--loggroups-jar $TEST_PROTOLOGGROUP_JAR " +
- "--viewer-conf invalid.yaml ${TEST_JAVA_SRC.joinToString(" ")}"
- CommandOptions(testLine.split(' ').toTypedArray())
+ "--viewer-config invalid.yaml ${TEST_JAVA_SRC.joinToString(" ")}"
+ val exception = assertThrows<InvalidCommandException>(InvalidCommandException::class.java) {
+ CommandOptions(testLine.split(' ').toTypedArray())
+ }
+ assertThat(exception).hasMessageThat().contains("required, got invalid.yaml instead")
}
@Test
fun readLog() {
- val testLine = "read-log --viewer-conf $TEST_VIEWER_JSON $TEST_LOG"
+ val testLine = "read-log --viewer-config $TEST_VIEWER_JSON $TEST_LOG"
val cmd = CommandOptions(testLine.split(' ').toTypedArray())
assertEquals(CommandOptions.READ_LOG_CMD, cmd.command)
- assertEquals(TEST_VIEWER_JSON, cmd.viewerConfigJsonArg)
+ assertEquals(TEST_VIEWER_JSON, cmd.viewerConfigFileNameArg)
assertEquals(TEST_LOG, cmd.logProtofileArg)
}
}
diff --git a/tools/protologtool/tests/com/android/protolog/tool/EndToEndTest.kt b/tools/protologtool/tests/com/android/protolog/tool/EndToEndTest.kt
index 0d2b91d6cfb8..822118cc5343 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/EndToEndTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/EndToEndTest.kt
@@ -16,22 +16,24 @@
package com.android.protolog.tool
-import org.junit.Assert
-import org.junit.Assert.assertTrue
-import org.junit.Test
+import com.android.protolog.tool.ProtoLogTool.PROTOLOG_IMPL_SRC_PATH
+import com.google.common.truth.Truth
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.FileNotFoundException
import java.io.OutputStream
import java.util.jar.JarInputStream
+import java.util.regex.Pattern
+import org.junit.Assert
+import org.junit.Test
class EndToEndTest {
@Test
fun e2e_transform() {
val output = run(
- src = "frameworks/base/org/example/Example.java" to """
+ srcs = mapOf("frameworks/base/org/example/Example.java" to """
package org.example;
import com.android.internal.protolog.common.ProtoLog;
import static com.android.internal.protolog.ProtoLogGroup.GROUP;
@@ -43,26 +45,29 @@ class EndToEndTest {
ProtoLog.d(GROUP, "Example: %s %d", argString, argInt);
}
}
- """.trimIndent(),
+ """.trimIndent()),
logGroup = LogGroup("GROUP", true, false, "TAG_GROUP"),
commandOptions = CommandOptions(arrayOf("transform-protolog-calls",
"--protolog-class", "com.android.internal.protolog.common.ProtoLog",
- "--protolog-impl-class", "com.android.internal.protolog.ProtoLogImpl",
- "--protolog-cache-class",
- "com.android.server.wm.ProtoLogCache",
"--loggroups-class", "com.android.internal.protolog.ProtoLogGroup",
"--loggroups-jar", "not_required.jar",
+ "--viewer-config-file-path", "not_required.pb",
"--output-srcjar", "out.srcjar",
"frameworks/base/org/example/Example.java"))
)
val outSrcJar = assertLoadSrcJar(output, "out.srcjar")
- assertTrue(" 2066303299," in outSrcJar["frameworks/base/org/example/Example.java"]!!)
+ Truth.assertThat(outSrcJar["frameworks/base/org/example/Example.java"])
+ .containsMatch(Pattern.compile("\\{ String protoLogParam0 = " +
+ "String\\.valueOf\\(argString\\); long protoLogParam1 = argInt; " +
+ "com\\.android\\.internal\\.protolog.ProtoLogImpl_.*\\.d\\(" +
+ "GROUP, -6872339441335321086L, 4, null, protoLogParam0, protoLogParam1" +
+ "\\); \\}"))
}
@Test
fun e2e_viewerConfig() {
val output = run(
- src = "frameworks/base/org/example/Example.java" to """
+ srcs = mapOf("frameworks/base/org/example/Example.java" to """
package org.example;
import com.android.internal.protolog.common.ProtoLog;
import static com.android.internal.protolog.ProtoLogGroup.GROUP;
@@ -74,17 +79,27 @@ class EndToEndTest {
ProtoLog.d(GROUP, "Example: %s %d", argString, argInt);
}
}
- """.trimIndent(),
+ """.trimIndent()),
logGroup = LogGroup("GROUP", true, false, "TAG_GROUP"),
commandOptions = CommandOptions(arrayOf("generate-viewer-config",
"--protolog-class", "com.android.internal.protolog.common.ProtoLog",
"--loggroups-class", "com.android.internal.protolog.ProtoLogGroup",
"--loggroups-jar", "not_required.jar",
- "--viewer-conf", "out.json",
+ "--viewer-config-type", "json",
+ "--viewer-config", "out.json",
"frameworks/base/org/example/Example.java"))
)
val viewerConfigJson = assertLoadText(output, "out.json")
- assertTrue("\"2066303299\"" in viewerConfigJson)
+ Truth.assertThat(viewerConfigJson).contains("""
+ "messages": {
+ "-6872339441335321086": {
+ "message": "Example: %s %d",
+ "level": "DEBUG",
+ "group": "GROUP",
+ "at": "org\/example\/Example.java"
+ }
+ }
+ """.trimIndent())
}
private fun assertLoadSrcJar(
@@ -112,21 +127,46 @@ class EndToEndTest {
}
fun run(
- src: Pair<String, String>,
+ srcs: Map<String, String>,
logGroup: LogGroup,
commandOptions: CommandOptions
): Map<String, ByteArray> {
val outputs = mutableMapOf<String, ByteArrayOutputStream>()
+ val srcs = srcs.toMutableMap()
+ srcs[PROTOLOG_IMPL_SRC_PATH] = """
+ package com.android.internal.protolog;
+
+ import static com.android.internal.protolog.common.ProtoLogToolInjected.Value.LEGACY_OUTPUT_FILE_PATH;
+ import static com.android.internal.protolog.common.ProtoLogToolInjected.Value.LEGACY_VIEWER_CONFIG_PATH;
+ import static com.android.internal.protolog.common.ProtoLogToolInjected.Value.VIEWER_CONFIG_PATH;
+
+ import com.android.internal.protolog.common.ProtoLogToolInjected;
+
+ public class ProtoLogImpl {
+ @ProtoLogToolInjected(VIEWER_CONFIG_PATH)
+ private static String sViewerConfigPath;
+
+ @ProtoLogToolInjected(LEGACY_VIEWER_CONFIG_PATH)
+ private static String sLegacyViewerConfigPath;
+
+ @ProtoLogToolInjected(LEGACY_OUTPUT_FILE_PATH)
+ private static String sLegacyOutputFilePath;
+ }
+ """.trimIndent()
+
ProtoLogTool.injector = object : ProtoLogTool.Injector {
override fun fileOutputStream(file: String): OutputStream =
ByteArrayOutputStream().also { outputs[file] = it }
override fun readText(file: File): String {
- if (file.path == src.first) {
- return src.second
+ for (src in srcs.entries) {
+ val filePath = src.key
+ if (file.path == filePath) {
+ return src.value
+ }
}
- throw FileNotFoundException("expected: ${src.first}, but was $file")
+ throw FileNotFoundException("$file not found in [${srcs.keys.joinToString()}].")
}
override fun readLogGroups(jarPath: String, className: String) = mapOf(
diff --git a/tools/protologtool/tests/com/android/protolog/tool/LogParserTest.kt b/tools/protologtool/tests/com/android/protolog/tool/LogParserTest.kt
index 512d90c725fe..1d3270268843 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/LogParserTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/LogParserTest.kt
@@ -35,7 +35,7 @@ import java.util.Locale
class LogParserTest {
private val configParser: ViewerConfigParser = mock(ViewerConfigParser::class.java)
private val parser = LogParser(configParser)
- private var config: MutableMap<Int, ViewerConfigParser.ConfigEntry> = mutableMapOf()
+ private var config: MutableMap<Long, ViewerConfigParser.ConfigEntry> = mutableMapOf()
private var outStream: OutputStream = ByteArrayOutputStream()
private var printStream: PrintStream = PrintStream(outStream)
private val dateFormat = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
diff --git a/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorTest.kt b/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorImplTest.kt
index 97f67a0a3fdb..5e50f71d75cc 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorImplTest.kt
@@ -16,12 +16,13 @@
package com.android.protolog.tool
+import com.android.internal.protolog.common.LogLevel
import com.github.javaparser.StaticJavaParser
import com.github.javaparser.ast.expr.MethodCallExpr
import org.junit.Assert.assertEquals
import org.junit.Test
-class ProtoLogCallProcessorTest {
+class ProtoLogCallProcessorImplTest {
private data class LogCall(
val call: MethodCallExpr,
val messageString: String,
@@ -31,8 +32,11 @@ class ProtoLogCallProcessorTest {
private val groupMap: MutableMap<String, LogGroup> = mutableMapOf()
private val calls: MutableList<LogCall> = mutableListOf()
- private val visitor = ProtoLogCallProcessor("org.example.ProtoLog", "org.example.ProtoLogGroup",
- groupMap)
+ private val visitor = ProtoLogCallProcessorImpl(
+ "org.example.ProtoLog",
+ "org.example.ProtoLogGroup",
+ groupMap
+ )
private val processor = object : ProtoLogCallVisitor {
override fun processCall(
call: MethodCallExpr,
diff --git a/tools/protologtool/tests/com/android/protolog/tool/ProtoLogToolTest.kt b/tools/protologtool/tests/com/android/protolog/tool/ProtoLogToolTest.kt
deleted file mode 100644
index ea9a58d859af..000000000000
--- a/tools/protologtool/tests/com/android/protolog/tool/ProtoLogToolTest.kt
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.protolog.tool
-
-import org.junit.Assert.assertEquals
-import org.junit.Test
-
-class ProtoLogToolTest {
-
- @Test
- fun generateLogGroupCache() {
- val groups = mapOf(
- "GROUP1" to LogGroup("GROUP1", true, true, "TAG1"),
- "GROUP2" to LogGroup("GROUP2", true, true, "TAG2")
- )
- val code = ProtoLogTool.generateLogGroupCache("org.example", "ProtoLog\$Cache",
- groups, "org.example.ProtoLogImpl", "org.example.ProtoLogGroups")
-
- assertEquals("""
- package org.example;
-
- public class ProtoLog${'$'}Cache {
- public static boolean GROUP1_enabled = false;
- public static boolean GROUP2_enabled = false;
-
- static {
- org.example.ProtoLogImpl.sCacheUpdater = ProtoLog${'$'}Cache::update;
- update();
- }
-
- static void update() {
- GROUP1_enabled = org.example.ProtoLogImpl.isEnabled(org.example.ProtoLogGroups.GROUP1);
- GROUP2_enabled = org.example.ProtoLogImpl.isEnabled(org.example.ProtoLogGroups.GROUP2);
- }
- }
- """.trimIndent(), code)
- }
-} \ No newline at end of file
diff --git a/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt b/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt
index 4f2be328fc8a..de0b5bae118e 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt
@@ -16,20 +16,18 @@
package com.android.protolog.tool
+import com.android.internal.protolog.common.LogLevel
import com.github.javaparser.StaticJavaParser
import com.github.javaparser.ast.CompilationUnit
import com.github.javaparser.ast.expr.MethodCallExpr
-import com.github.javaparser.ast.stmt.IfStmt
+import com.google.common.truth.Truth
import org.junit.Assert.assertEquals
-import org.junit.Assert.assertFalse
import org.junit.Test
import org.mockito.Mockito
class SourceTransformerTest {
companion object {
- private const val PROTO_LOG_IMPL_PATH = "org.example.ProtoLogImpl"
- /* ktlint-disable max-line-length */
private val TEST_CODE = """
package org.example;
@@ -78,7 +76,7 @@ class SourceTransformerTest {
class Test {
void test() {
- if (org.example.ProtoLogCache.TEST_GROUP_enabled) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, 1698911065, 9, "test %d %f", protoLogParam0, protoLogParam1); }
+ { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, -1473209266730422156L, 9, "test %d %f", protoLogParam0, protoLogParam1); }
}
}
""".trimIndent()
@@ -88,20 +86,20 @@ class SourceTransformerTest {
class Test {
void test() {
- if (org.example.ProtoLogCache.TEST_GROUP_enabled) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; String protoLogParam2 = String.valueOf("test"); org.example.ProtoLogImpl.w(TEST_GROUP, 1780316587, 9, "test %d %f " + "abc %s\n test", protoLogParam0, protoLogParam1, protoLogParam2);
+ { long protoLogParam0 = 100; double protoLogParam1 = 0.1; String protoLogParam2 = String.valueOf("test"); org.example.ProtoLogImpl.w(TEST_GROUP, -4447034859795564700L, 9, "test %d %f " + "abc %s\n test", protoLogParam0, protoLogParam1, protoLogParam2);
}
}
}
""".trimIndent()
- private val TRANSFORMED_CODE_MULTICALL_TEXT_ENABLED = """
+ private val TRANSFORMED_CODE_MULTICALL_TEXT = """
package org.example;
class Test {
void test() {
- if (org.example.ProtoLogCache.TEST_GROUP_enabled) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, 1698911065, 9, "test %d %f", protoLogParam0, protoLogParam1); } /* ProtoLog.w(TEST_GROUP, "test %d %f", 100, 0.1); */ if (org.example.ProtoLogCache.TEST_GROUP_enabled) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, 1698911065, 9, "test %d %f", protoLogParam0, protoLogParam1); }
- if (org.example.ProtoLogCache.TEST_GROUP_enabled) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, 1698911065, 9, "test %d %f", protoLogParam0, protoLogParam1); }
+ { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, -1473209266730422156L, 9, "test %d %f", protoLogParam0, protoLogParam1); } /* ProtoLog.w(TEST_GROUP, "test %d %f", 100, 0.1); */ { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, -1473209266730422156L, 9, "test %d %f", protoLogParam0, protoLogParam1); }
+ { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, -1473209266730422156L, 9, "test %d %f", protoLogParam0, protoLogParam1); }
}
}
""".trimIndent()
@@ -111,7 +109,7 @@ class SourceTransformerTest {
class Test {
void test() {
- if (org.example.ProtoLogCache.TEST_GROUP_enabled) { org.example.ProtoLogImpl.w(TEST_GROUP, -1741986185, 0, "test", (Object[]) null); }
+ { org.example.ProtoLogImpl.w(TEST_GROUP, 3218600869538902408L, 0, "test", (Object[]) null); }
}
}
""".trimIndent()
@@ -121,7 +119,7 @@ class SourceTransformerTest {
class Test {
void test() {
- if (org.example.ProtoLogCache.TEST_GROUP_enabled) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, 1698911065, 9, null, protoLogParam0, protoLogParam1); }
+ { long protoLogParam0 = 100; double protoLogParam1 = 0.1; org.example.ProtoLogImpl.w(TEST_GROUP, -1473209266730422156L, 9, null, protoLogParam0, protoLogParam1); }
}
}
""".trimIndent()
@@ -131,43 +129,19 @@ class SourceTransformerTest {
class Test {
void test() {
- if (org.example.ProtoLogCache.TEST_GROUP_enabled) { long protoLogParam0 = 100; double protoLogParam1 = 0.1; String protoLogParam2 = String.valueOf("test"); org.example.ProtoLogImpl.w(TEST_GROUP, 1780316587, 9, null, protoLogParam0, protoLogParam1, protoLogParam2);
+ { long protoLogParam0 = 100; double protoLogParam1 = 0.1; String protoLogParam2 = String.valueOf("test"); org.example.ProtoLogImpl.w(TEST_GROUP, -4447034859795564700L, 9, null, protoLogParam0, protoLogParam1, protoLogParam2);
}
}
}
""".trimIndent()
- private val TRANSFORMED_CODE_DISABLED = """
- package org.example;
-
- class Test {
- void test() {
- if (false) { /* TEST_GROUP is disabled */ ProtoLog.w(TEST_GROUP, "test %d %f", 100, 0.1); }
- }
- }
- """.trimIndent()
-
- private val TRANSFORMED_CODE_MULTILINE_DISABLED = """
- package org.example;
-
- class Test {
- void test() {
- if (false) { /* TEST_GROUP is disabled */ ProtoLog.w(TEST_GROUP, "test %d %f " + "abc %s\n test", 100, 0.1, "test");
-
- }
- }
- }
- """.trimIndent()
- /* ktlint-enable max-line-length */
-
private const val PATH = "com.example.Test.java"
}
private val processor: ProtoLogCallProcessor = Mockito.mock(ProtoLogCallProcessor::class.java)
private val implName = "org.example.ProtoLogImpl"
- private val cacheName = "org.example.ProtoLogCache"
- private val sourceJarWriter = SourceTransformer(implName, cacheName, processor)
+ private val sourceJarWriter = SourceTransformer(implName, processor)
private fun <T> any(type: Class<T>): T = Mockito.any<T>(type)
@@ -175,9 +149,12 @@ class SourceTransformerTest {
fun processClass_textEnabled() {
var code = StaticJavaParser.parse(TEST_CODE)
- Mockito.`when`(processor.process(any(CompilationUnit::class.java),
- any(ProtoLogCallVisitor::class.java), any(String::class.java)))
- .thenAnswer { invocation ->
+ Mockito.`when`(processor.process(
+ any(CompilationUnit::class.java),
+ any(ProtoLogCallVisitor::class.java),
+ any(MethodCallVisitor::class.java),
+ any(String::class.java))
+ ).thenAnswer { invocation ->
val visitor = invocation.arguments[1] as ProtoLogCallVisitor
visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], "test %d %f",
@@ -189,18 +166,15 @@ class SourceTransformerTest {
val out = sourceJarWriter.processClass(TEST_CODE, PATH, PATH, code)
code = StaticJavaParser.parse(out)
- val ifStmts = code.findAll(IfStmt::class.java)
- assertEquals(1, ifStmts.size)
- val ifStmt = ifStmts[0]
- assertEquals("$cacheName.TEST_GROUP_enabled", ifStmt.condition.toString())
- assertFalse(ifStmt.elseStmt.isPresent)
- assertEquals(3, ifStmt.thenStmt.childNodes.size)
- val methodCall = ifStmt.thenStmt.findAll(MethodCallExpr::class.java)[0] as MethodCallExpr
- assertEquals(PROTO_LOG_IMPL_PATH, methodCall.scope.get().toString())
+ val protoLogCalls = code.findAll(MethodCallExpr::class.java).filter {
+ it.scope.orElse(null)?.toString() == implName
+ }
+ Truth.assertThat(protoLogCalls).hasSize(1)
+ val methodCall = protoLogCalls[0] as MethodCallExpr
assertEquals("w", methodCall.name.asString())
assertEquals(6, methodCall.arguments.size)
assertEquals("TEST_GROUP", methodCall.arguments[0].toString())
- assertEquals("1698911065", methodCall.arguments[1].toString())
+ assertEquals("-1473209266730422156L", methodCall.arguments[1].toString())
assertEquals(0b1001.toString(), methodCall.arguments[2].toString())
assertEquals("\"test %d %f\"", methodCall.arguments[3].toString())
assertEquals("protoLogParam0", methodCall.arguments[4].toString())
@@ -212,9 +186,12 @@ class SourceTransformerTest {
fun processClass_textEnabledMulticalls() {
var code = StaticJavaParser.parse(TEST_CODE_MULTICALLS)
- Mockito.`when`(processor.process(any(CompilationUnit::class.java),
- any(ProtoLogCallVisitor::class.java), any(String::class.java)))
- .thenAnswer { invocation ->
+ Mockito.`when`(processor.process(
+ any(CompilationUnit::class.java),
+ any(ProtoLogCallVisitor::class.java),
+ any(MethodCallVisitor::class.java),
+ any(String::class.java))
+ ).thenAnswer { invocation ->
val visitor = invocation.arguments[1] as ProtoLogCallVisitor
val calls = code.findAll(MethodCallExpr::class.java)
@@ -231,32 +208,32 @@ class SourceTransformerTest {
val out = sourceJarWriter.processClass(TEST_CODE_MULTICALLS, PATH, PATH, code)
code = StaticJavaParser.parse(out)
- val ifStmts = code.findAll(IfStmt::class.java)
- assertEquals(3, ifStmts.size)
- val ifStmt = ifStmts[1]
- assertEquals("$cacheName.TEST_GROUP_enabled", ifStmt.condition.toString())
- assertFalse(ifStmt.elseStmt.isPresent)
- assertEquals(3, ifStmt.thenStmt.childNodes.size)
- val methodCall = ifStmt.thenStmt.findAll(MethodCallExpr::class.java)[0] as MethodCallExpr
- assertEquals(PROTO_LOG_IMPL_PATH, methodCall.scope.get().toString())
+ val protoLogCalls = code.findAll(MethodCallExpr::class.java).filter {
+ it.scope.orElse(null)?.toString() == implName
+ }
+ Truth.assertThat(protoLogCalls).hasSize(3)
+ val methodCall = protoLogCalls[0] as MethodCallExpr
assertEquals("w", methodCall.name.asString())
assertEquals(6, methodCall.arguments.size)
assertEquals("TEST_GROUP", methodCall.arguments[0].toString())
- assertEquals("1698911065", methodCall.arguments[1].toString())
+ assertEquals("-1473209266730422156L", methodCall.arguments[1].toString())
assertEquals(0b1001.toString(), methodCall.arguments[2].toString())
assertEquals("\"test %d %f\"", methodCall.arguments[3].toString())
assertEquals("protoLogParam0", methodCall.arguments[4].toString())
assertEquals("protoLogParam1", methodCall.arguments[5].toString())
- assertEquals(TRANSFORMED_CODE_MULTICALL_TEXT_ENABLED, out)
+ assertEquals(TRANSFORMED_CODE_MULTICALL_TEXT, out)
}
@Test
fun processClass_textEnabledMultiline() {
var code = StaticJavaParser.parse(TEST_CODE_MULTILINE)
- Mockito.`when`(processor.process(any(CompilationUnit::class.java),
- any(ProtoLogCallVisitor::class.java), any(String::class.java)))
- .thenAnswer { invocation ->
+ Mockito.`when`(processor.process(
+ any(CompilationUnit::class.java),
+ any(ProtoLogCallVisitor::class.java),
+ any(MethodCallVisitor::class.java),
+ any(String::class.java))
+ ).thenAnswer { invocation ->
val visitor = invocation.arguments[1] as ProtoLogCallVisitor
visitor.processCall(code.findAll(MethodCallExpr::class.java)[0],
@@ -269,18 +246,15 @@ class SourceTransformerTest {
val out = sourceJarWriter.processClass(TEST_CODE_MULTILINE, PATH, PATH, code)
code = StaticJavaParser.parse(out)
- val ifStmts = code.findAll(IfStmt::class.java)
- assertEquals(1, ifStmts.size)
- val ifStmt = ifStmts[0]
- assertEquals("$cacheName.TEST_GROUP_enabled", ifStmt.condition.toString())
- assertFalse(ifStmt.elseStmt.isPresent)
- assertEquals(4, ifStmt.thenStmt.childNodes.size)
- val methodCall = ifStmt.thenStmt.findAll(MethodCallExpr::class.java)[1] as MethodCallExpr
- assertEquals(PROTO_LOG_IMPL_PATH, methodCall.scope.get().toString())
+ val protoLogCalls = code.findAll(MethodCallExpr::class.java).filter {
+ it.scope.orElse(null)?.toString() == implName
+ }
+ Truth.assertThat(protoLogCalls).hasSize(1)
+ val methodCall = protoLogCalls[0] as MethodCallExpr
assertEquals("w", methodCall.name.asString())
assertEquals(7, methodCall.arguments.size)
assertEquals("TEST_GROUP", methodCall.arguments[0].toString())
- assertEquals("1780316587", methodCall.arguments[1].toString())
+ assertEquals("-4447034859795564700L", methodCall.arguments[1].toString())
assertEquals(0b001001.toString(), methodCall.arguments[2].toString())
assertEquals("protoLogParam0", methodCall.arguments[4].toString())
assertEquals("protoLogParam1", methodCall.arguments[5].toString())
@@ -292,9 +266,12 @@ class SourceTransformerTest {
fun processClass_noParams() {
var code = StaticJavaParser.parse(TEST_CODE_NO_PARAMS)
- Mockito.`when`(processor.process(any(CompilationUnit::class.java),
- any(ProtoLogCallVisitor::class.java), any(String::class.java)))
- .thenAnswer { invocation ->
+ Mockito.`when`(processor.process(
+ any(CompilationUnit::class.java),
+ any(ProtoLogCallVisitor::class.java),
+ any(MethodCallVisitor::class.java),
+ any(String::class.java))
+ ).thenAnswer { invocation ->
val visitor = invocation.arguments[1] as ProtoLogCallVisitor
visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], "test",
@@ -306,18 +283,15 @@ class SourceTransformerTest {
val out = sourceJarWriter.processClass(TEST_CODE_NO_PARAMS, PATH, PATH, code)
code = StaticJavaParser.parse(out)
- val ifStmts = code.findAll(IfStmt::class.java)
- assertEquals(1, ifStmts.size)
- val ifStmt = ifStmts[0]
- assertEquals("$cacheName.TEST_GROUP_enabled", ifStmt.condition.toString())
- assertFalse(ifStmt.elseStmt.isPresent)
- assertEquals(1, ifStmt.thenStmt.childNodes.size)
- val methodCall = ifStmt.thenStmt.findAll(MethodCallExpr::class.java)[0] as MethodCallExpr
- assertEquals(PROTO_LOG_IMPL_PATH, methodCall.scope.get().toString())
+ val protoLogCalls = code.findAll(MethodCallExpr::class.java).filter {
+ it.scope.orElse(null)?.toString() == implName
+ }
+ Truth.assertThat(protoLogCalls).hasSize(1)
+ val methodCall = protoLogCalls[0] as MethodCallExpr
assertEquals("w", methodCall.name.asString())
assertEquals(5, methodCall.arguments.size)
assertEquals("TEST_GROUP", methodCall.arguments[0].toString())
- assertEquals("-1741986185", methodCall.arguments[1].toString())
+ assertEquals("3218600869538902408L", methodCall.arguments[1].toString())
assertEquals(0.toString(), methodCall.arguments[2].toString())
assertEquals(TRANSFORMED_CODE_NO_PARAMS, out)
}
@@ -326,9 +300,12 @@ class SourceTransformerTest {
fun processClass_textDisabled() {
var code = StaticJavaParser.parse(TEST_CODE)
- Mockito.`when`(processor.process(any(CompilationUnit::class.java),
- any(ProtoLogCallVisitor::class.java), any(String::class.java)))
- .thenAnswer { invocation ->
+ Mockito.`when`(processor.process(
+ any(CompilationUnit::class.java),
+ any(ProtoLogCallVisitor::class.java),
+ any(MethodCallVisitor::class.java),
+ any(String::class.java))
+ ).thenAnswer { invocation ->
val visitor = invocation.arguments[1] as ProtoLogCallVisitor
visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], "test %d %f",
@@ -340,18 +317,15 @@ class SourceTransformerTest {
val out = sourceJarWriter.processClass(TEST_CODE, PATH, PATH, code)
code = StaticJavaParser.parse(out)
- val ifStmts = code.findAll(IfStmt::class.java)
- assertEquals(1, ifStmts.size)
- val ifStmt = ifStmts[0]
- assertEquals("$cacheName.TEST_GROUP_enabled", ifStmt.condition.toString())
- assertFalse(ifStmt.elseStmt.isPresent)
- assertEquals(3, ifStmt.thenStmt.childNodes.size)
- val methodCall = ifStmt.thenStmt.findAll(MethodCallExpr::class.java)[0] as MethodCallExpr
- assertEquals(PROTO_LOG_IMPL_PATH, methodCall.scope.get().toString())
+ val protoLogCalls = code.findAll(MethodCallExpr::class.java).filter {
+ it.scope.orElse(null)?.toString() == implName
+ }
+ Truth.assertThat(protoLogCalls).hasSize(1)
+ val methodCall = protoLogCalls[0] as MethodCallExpr
assertEquals("w", methodCall.name.asString())
assertEquals(6, methodCall.arguments.size)
assertEquals("TEST_GROUP", methodCall.arguments[0].toString())
- assertEquals("1698911065", methodCall.arguments[1].toString())
+ assertEquals("-1473209266730422156L", methodCall.arguments[1].toString())
assertEquals(0b1001.toString(), methodCall.arguments[2].toString())
assertEquals("null", methodCall.arguments[3].toString())
assertEquals("protoLogParam0", methodCall.arguments[4].toString())
@@ -363,9 +337,12 @@ class SourceTransformerTest {
fun processClass_textDisabledMultiline() {
var code = StaticJavaParser.parse(TEST_CODE_MULTILINE)
- Mockito.`when`(processor.process(any(CompilationUnit::class.java),
- any(ProtoLogCallVisitor::class.java), any(String::class.java)))
- .thenAnswer { invocation ->
+ Mockito.`when`(processor.process(
+ any(CompilationUnit::class.java),
+ any(ProtoLogCallVisitor::class.java),
+ any(MethodCallVisitor::class.java),
+ any(String::class.java))
+ ).thenAnswer { invocation ->
val visitor = invocation.arguments[1] as ProtoLogCallVisitor
visitor.processCall(code.findAll(MethodCallExpr::class.java)[0],
@@ -378,18 +355,15 @@ class SourceTransformerTest {
val out = sourceJarWriter.processClass(TEST_CODE_MULTILINE, PATH, PATH, code)
code = StaticJavaParser.parse(out)
- val ifStmts = code.findAll(IfStmt::class.java)
- assertEquals(1, ifStmts.size)
- val ifStmt = ifStmts[0]
- assertEquals("$cacheName.TEST_GROUP_enabled", ifStmt.condition.toString())
- assertFalse(ifStmt.elseStmt.isPresent)
- assertEquals(4, ifStmt.thenStmt.childNodes.size)
- val methodCall = ifStmt.thenStmt.findAll(MethodCallExpr::class.java)[1] as MethodCallExpr
- assertEquals(PROTO_LOG_IMPL_PATH, methodCall.scope.get().toString())
+ val protoLogCalls = code.findAll(MethodCallExpr::class.java).filter {
+ it.scope.orElse(null)?.toString() == implName
+ }
+ Truth.assertThat(protoLogCalls).hasSize(1)
+ val methodCall = protoLogCalls[0] as MethodCallExpr
assertEquals("w", methodCall.name.asString())
assertEquals(7, methodCall.arguments.size)
assertEquals("TEST_GROUP", methodCall.arguments[0].toString())
- assertEquals("1780316587", methodCall.arguments[1].toString())
+ assertEquals("-4447034859795564700L", methodCall.arguments[1].toString())
assertEquals(0b001001.toString(), methodCall.arguments[2].toString())
assertEquals("null", methodCall.arguments[3].toString())
assertEquals("protoLogParam0", methodCall.arguments[4].toString())
@@ -397,55 +371,4 @@ class SourceTransformerTest {
assertEquals("protoLogParam2", methodCall.arguments[6].toString())
assertEquals(TRANSFORMED_CODE_MULTILINE_TEXT_DISABLED, out)
}
-
- @Test
- fun processClass_disabled() {
- var code = StaticJavaParser.parse(TEST_CODE)
-
- Mockito.`when`(processor.process(any(CompilationUnit::class.java),
- any(ProtoLogCallVisitor::class.java), any(String::class.java)))
- .thenAnswer { invocation ->
- val visitor = invocation.arguments[1] as ProtoLogCallVisitor
-
- visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], "test %d %f",
- LogLevel.WARN, LogGroup("TEST_GROUP", false, true, "WM_TEST"))
-
- invocation.arguments[0] as CompilationUnit
- }
-
- val out = sourceJarWriter.processClass(TEST_CODE, PATH, PATH, code)
- code = StaticJavaParser.parse(out)
-
- val ifStmts = code.findAll(IfStmt::class.java)
- assertEquals(1, ifStmts.size)
- val ifStmt = ifStmts[0]
- assertEquals("false", ifStmt.condition.toString())
- assertEquals(TRANSFORMED_CODE_DISABLED, out)
- }
-
- @Test
- fun processClass_disabledMultiline() {
- var code = StaticJavaParser.parse(TEST_CODE_MULTILINE)
-
- Mockito.`when`(processor.process(any(CompilationUnit::class.java),
- any(ProtoLogCallVisitor::class.java), any(String::class.java)))
- .thenAnswer { invocation ->
- val visitor = invocation.arguments[1] as ProtoLogCallVisitor
-
- visitor.processCall(code.findAll(MethodCallExpr::class.java)[0],
- "test %d %f abc %s\n test", LogLevel.WARN, LogGroup("TEST_GROUP",
- false, true, "WM_TEST"))
-
- invocation.arguments[0] as CompilationUnit
- }
-
- val out = sourceJarWriter.processClass(TEST_CODE_MULTILINE, PATH, PATH, code)
- code = StaticJavaParser.parse(out)
-
- val ifStmts = code.findAll(IfStmt::class.java)
- assertEquals(1, ifStmts.size)
- val ifStmt = ifStmts[0]
- assertEquals("false", ifStmt.condition.toString())
- assertEquals(TRANSFORMED_CODE_MULTILINE_DISABLED, out)
- }
}
diff --git a/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigBuilderTest.kt b/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigJsonBuilderTest.kt
index a24761aed9db..d27ae88fc488 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigBuilderTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigJsonBuilderTest.kt
@@ -16,14 +16,14 @@
package com.android.protolog.tool
+import com.android.internal.protolog.common.LogLevel
import com.android.json.stream.JsonReader
-import com.android.protolog.tool.ViewerConfigBuilder.LogCall
+import com.android.protolog.tool.ProtoLogTool.LogCall
+import java.io.StringReader
import org.junit.Assert.assertEquals
import org.junit.Test
-import org.mockito.Mockito
-import java.io.StringReader
-class ViewerConfigBuilderTest {
+class ViewerConfigJsonBuilderTest {
companion object {
private val TAG1 = "WM_TEST"
private val TAG2 = "WM_DEBUG"
@@ -38,20 +38,22 @@ class ViewerConfigBuilderTest {
private const val PATH = "/tmp/test.java"
}
- private val configBuilder = ViewerConfigBuilder(Mockito.mock(ProtoLogCallProcessor::class.java))
+ private val configBuilder = ViewerConfigJsonBuilder()
- private fun parseConfig(json: String): Map<Int, ViewerConfigParser.ConfigEntry> {
+ private fun parseConfig(json: String): Map<Long, ViewerConfigParser.ConfigEntry> {
return ViewerConfigParser().parseConfig(JsonReader(StringReader(json)))
}
@Test
fun processClass() {
- configBuilder.addLogCalls(listOf(
+ val logCallRegistry = ProtoLogTool.LogCallRegistry()
+ logCallRegistry.addLogCalls(listOf(
LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH),
LogCall(TEST2.messageString, LogLevel.DEBUG, GROUP2, PATH),
- LogCall(TEST3.messageString, LogLevel.ERROR, GROUP3, PATH)).withContext())
+ LogCall(TEST3.messageString, LogLevel.ERROR, GROUP3, PATH)))
- val parsedConfig = parseConfig(configBuilder.build())
+ val parsedConfig = parseConfig(
+ configBuilder.build(logCallRegistry.getStatements()).toString(Charsets.UTF_8))
assertEquals(3, parsedConfig.size)
assertEquals(TEST1, parsedConfig[CodeUtils.hash(PATH,
TEST1.messageString, LogLevel.INFO, GROUP1)])
@@ -63,32 +65,16 @@ class ViewerConfigBuilderTest {
@Test
fun processClass_nonUnique() {
- configBuilder.addLogCalls(listOf(
+ val logCallRegistry = ProtoLogTool.LogCallRegistry()
+ logCallRegistry.addLogCalls(listOf(
LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH),
LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH),
- LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH)).withContext())
+ LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH)))
- val parsedConfig = parseConfig(configBuilder.build())
+ val parsedConfig = parseConfig(
+ configBuilder.build(logCallRegistry.getStatements()).toString(Charsets.UTF_8))
assertEquals(1, parsedConfig.size)
assertEquals(TEST1, parsedConfig[CodeUtils.hash(PATH, TEST1.messageString,
- LogLevel.INFO, GROUP1)])
- }
-
- @Test
- fun processClass_disabled() {
- configBuilder.addLogCalls(listOf(
- LogCall(TEST1.messageString, LogLevel.INFO, GROUP1, PATH),
- LogCall(TEST2.messageString, LogLevel.DEBUG, GROUP_DISABLED, PATH),
- LogCall(TEST3.messageString, LogLevel.ERROR, GROUP_TEXT_DISABLED, PATH))
- .withContext())
-
- val parsedConfig = parseConfig(configBuilder.build())
- assertEquals(2, parsedConfig.size)
- assertEquals(TEST1, parsedConfig[CodeUtils.hash(
- PATH, TEST1.messageString, LogLevel.INFO, GROUP1)])
- assertEquals(TEST3, parsedConfig[CodeUtils.hash(
- PATH, TEST3.messageString, LogLevel.ERROR, GROUP_TEXT_DISABLED)])
+ LogLevel.INFO, GROUP1)])
}
-
- private fun List<LogCall>.withContext() = map { it to ParsingContext() }
}
diff --git a/wifi/TEST_MAPPING b/wifi/TEST_MAPPING
index 757ecaa8aca9..3ae91bee6f45 100644
--- a/wifi/TEST_MAPPING
+++ b/wifi/TEST_MAPPING
@@ -2,9 +2,7 @@
"presubmit": [
{
"name": "FrameworksWifiNonUpdatableApiTests"
- }
- ],
- "postsubmit": [
+ },
{
"name": "CtsWifiNonUpdatableTestCases"
}